Movement in tasks, no more processMove

Forum closed. All further discussion to be discussed at https://github.com/OpenKore/

Moderator: Moderators

Message
Author
EternalHarvest
Developers
Developers
Posts: 1798
Joined: 05 Dec 2008, 05:42
Noob?: Yes

Movement in tasks, no more processMove

#1 Post by EternalHarvest »

Ok, I've got it working and about to commit.

Code: Select all

Index: Commands.pm
===================================================================
--- Commands.pm	(revision 7684)
+++ Commands.pm	(working copy)
@@ -3953,6 +3953,7 @@
 	require Task::SitStand;
 	my $task = new Task::ErrorReport(
 		task => new Task::SitStand(
+			actor => $char,
 			mode => 'sit',
 			priority => Task::USER_PRIORITY
 		)
@@ -4056,6 +4057,7 @@
 	require Task::SitStand;
 	my $task = new Task::ErrorReport(
 		task => new Task::SitStand(
+			actor => $char,
 			mode => 'stand',
 			priority => Task::USER_PRIORITY
 		)
Index: Actor.pm
===================================================================
--- Actor.pm	(revision 7684)
+++ Actor.pm	(working copy)
@@ -40,6 +40,7 @@
 use Utils::CallbackList;
 use Log qw(message error debug);
 use Misc;
+use Task;
 use Translation qw(T TF);
 use Actor::Unknown;
 
@@ -514,56 +515,98 @@
 sub move {
 	my ($self, $x, $y, $attackID) = @_;
 	
-	unless ($x || $y) {
-		error "BUG: Actor::move(0, 0) called!\n";
-		return;
-	}
+	require Task::Move;
 	
-	my %args = (
-		move_to => { x => $x, y => $y },
-		attackID => $attackID,
-		time_move => $self->{time_move},
-		ai_move_giveup => { timeout => $timeout{ai_move_giveup}{timeout} },
+	$self->queue('move', my $task = new Task::Move(
+		actor => $self,
+		x => $x,
+		y => $y,
+	));
+	$task->{attackID} = $attackID;
+}
+
+sub route {
+	my ($self, $map, $x, $y, %args) = @_;
+	debug "$self on route to: $maps_lut{$map.'.rsw'}($map): $x, $y\n", "route";
+	
+	# I can't use 'use' because of circular dependencies.
+	require Task::Route;
+	require Task::MapRoute;
+	
+	# from Homunculus AI
+	($x, $y) = map { $_ ne '' ? int $_ : $_ } ($x, $y);
+	
+	my $task;
+	my @params = (
+		actor => $self,
+		x => $x,
+		y => $y,
+		maxDistance => $args{maxRouteDistance},
+		maxTime => $args{maxRouteTime},
+		avoidWalls => !$args{noAvoidWalls},
+		map { $_ => $args{$_} } qw(distFromGoal pyDistFromGoal notifyUponArrival)
 	);
 	
-	debug sprintf("%s sending move from (%d,%d) to (%d,%d) - distance %.2f\n",
-		$self, @{$self->{pos}}{qw(x y)}, $x, $y, Utils::distance($self->{pos}, $args{move_to})), "ai_move";
-	$self->queue("move", \%args);
+	if ($map && !$args{noMapRoute}) {
+		$task = new Task::MapRoute(map => $map, @params);
+	} else {
+		$task = new Task::Route(@params);
+	}
+	$task->{$_} = $args{$_} for qw(attackID attackOnRoute noSitAuto LOSSubRoute);
+	
+	$self->queue('route', $task);
 }
 
-sub processMove {
-	my ($self) = @_;
-	
-	if ($self->action eq "move") {
-		my $args = $self->args;
-		$args->{ai_move_giveup}{time} = time unless $args->{ai_move_giveup}{time};
-
-		# Wait until we've stand up, if we're sitting
-		if ($self->{sitting}) {
-			$args->{ai_move_giveup}{time} = 0;
-			AI::stand;
-
-		# Stop if the map changed
-		} elsif ($args->{mapChanged}) {
-			debug "$self move - map change detected\n", "ai_move";
-			$self->dequeue;
-
-		# Stop if we've moved
-		} elsif ($args->{time_move} != $self->{time_move}) {
-			debug "$self move - moving\n", "ai_move";
-			$self->dequeue;
-
-		# Stop if we've timed out
-		} elsif (timeOut($args->{ai_move_giveup})) {
-			debug "$self move - timeout\n", "ai_move";
-			$self->dequeue;
-
-		} elsif (timeOut($self->{move_retry}, 0.5)) {
-			# No update yet, send move request again.
-			# We do this every 0.5 secs
-			$self->{move_retry} = time;
-			$self->sendMove(@{$args->{move_to}}{qw(x y)});
+sub processTask {
+	my $self = shift;
+	my $ai_name = shift;
+	if ($self->action eq $ai_name) {
+		my $task = $self->args;
+		if ($task->getStatus() == Task::INACTIVE) {
+			$task->activate();
+			should($task->getStatus(), Task::RUNNING) if DEBUG;
 		}
+		if (DEBUG && $task->getStatus() != Task::RUNNING) {
+			require Scalar::Util;
+			require Data::Dumper;
+			# Make sure redundant information is not included in the error report.
+			if ($task->isa('Task::MapRoute')) {
+				delete $task->{ST_subtask}{solution};
+			} elsif ($task->isa('Task::Route') && $task->{ST_subtask}) {
+				delete $task->{solution};
+			}
+			die "Task '" . $task->getName() . "' (class " . Scalar::Util::blessed($task) . ") has status " .
+				Task::_getStatusName($task->getStatus()) .
+				", but should be RUNNING. Object details:\n" .
+				Data::Dumper::Dumper($task);
+		}
+		$task->iterate();
+		if ($task->getStatus() == Task::DONE) {
+			# We can't just dequeue the last AI sequence. Perhaps the task
+			# pushed a new AI sequence on the AI stack just before finishing.
+			# For example, the Route task does that when it's stuck.
+			# So, we must dequeue the correct sequence without affecting the
+			# others.
+			my ($ai_seq, $ai_seq_args) = $self->isa('Actor::You') ? (\@AI::ai_seq, \@AI::ai_seq_args) : (@{$self}{qw(slave_ai_seq slave_ai_seq_args)});
+			for (my $i = 0; $i < @$ai_seq; $i++) {
+				if ($ai_seq->[$i] eq $ai_name) {
+					splice(@$ai_seq, $i, 1);
+					splice(@$ai_seq_args, $i, 1);
+					last;
+				}
+			}
+			my %args = @_;
+			my $error = $task->getError();
+			if ($error) {
+				if ($args{onError}) {
+					$args{onError}->($task, $error);
+				} else {
+					error("$error->{message}\n");
+				}
+			} elsif ($args{onSuccess}) {
+				$args{onSuccess}->($task);
+			}
+		}
 	}
 }
 
Index: AI/CoreLogic.pm
===================================================================
--- AI/CoreLogic.pm	(revision 7684)
+++ AI/CoreLogic.pm	(working copy)
@@ -65,21 +65,21 @@
 	return if processClientSuspend();
 	Benchmark::begin("AI (part 1.1)") if DEBUG;
 	processLook();
-	processTask('NPC');
+	$char->processTask('NPC');
 	processEquip();
 	processDrop();
 	processEscapeUnknownMaps();
 	Benchmark::end("AI (part 1.1)") if DEBUG;
 	Benchmark::begin("AI (part 1.2)") if DEBUG;
 	processDelayedTeleport();
-	processTask("sitting");
-	processTask("standing");
+	$char->processTask("sitting");
+	$char->processTask("standing");
 	AI::Attack::process();
 	Benchmark::end("AI (part 1.2)") if DEBUG;
 	Benchmark::begin("AI (part 1.3)") if DEBUG;
 	processSkillUse();
 	processAutoCommandUse();
-	processTask("route", onError => sub {
+	$char->processTask("route", onError => sub {
 		my ($task, $error) = @_;
 		if (!($task->isa('Task::MapRoute') && $error->{code} == Task::MapRoute::TOO_MUCH_TIME())
 		 && !($task->isa('Task::Route') && $error->{code} == Task::Route::TOO_MUCH_TIME())) {
@@ -87,7 +87,7 @@
 		}
 	});
 	processTake();
-	$char->processMove;
+	$char->processTask('move');
 	Benchmark::end("AI (part 1.3)") if DEBUG;
 
 	Benchmark::begin("AI (part 1.4)") if DEBUG;
@@ -722,57 +722,6 @@
 	}
 }
 
-sub processTask {
-	my $ai_name = shift;
-	if (AI::action eq $ai_name) {
-		my $task = AI::args;
-		if ($task->getStatus() == Task::INACTIVE) {
-			$task->activate();
-			should($task->getStatus(), Task::RUNNING) if DEBUG;
-		}
-		if (DEBUG && $task->getStatus() != Task::RUNNING) {
-			require Scalar::Util;
-			require Data::Dumper;
-			# Make sure redundant information is not included in the error report.
-			if ($task->isa('Task::MapRoute')) {
-				delete $task->{ST_subtask}{solution};
-			} elsif ($task->isa('Task::Route') && $task->{ST_subtask}) {
-				delete $task->{solution};
-			}
-			die "Task '" . $task->getName() . "' (class " . Scalar::Util::blessed($task) . ") has status " .
-				Task::_getStatusName($task->getStatus()) .
-				", but should be RUNNING. Object details:\n" .
-				Data::Dumper::Dumper($task);
-		}
-		$task->iterate();
-		if ($task->getStatus() == Task::DONE) {
-			# We can't just dequeue the last AI sequence. Perhaps the task
-			# pushed a new AI sequence on the AI stack just before finishing.
-			# For example, the Route task does that when it's stuck.
-			# So, we must dequeue the correct sequence without affecting the
-			# others.
-			for (my $i = 0; $i < @AI::ai_seq; $i++) {
-				if ($AI::ai_seq[$i] eq $ai_name) {
-					splice(@AI::ai_seq, $i, 1);
-					splice(@AI::ai_seq_args, $i, 1);
-					last;
-				}
-			}
-			my %args = @_;
-			my $error = $task->getError();
-			if ($error) {
-				if ($args{onError}) {
-					$args{onError}->($task, $error);
-				} else {
-					error("$error->{message}\n");
-				}
-			} elsif ($args{onSuccess}) {
-				$args{onSuccess}->($task);
-			}
-		}
-	}
-}
-
 sub processTake {
 	##### TAKE #####
 
@@ -802,7 +751,7 @@
 				my (%vec, %pos);
 				getVector(\%vec, $item->{pos}, $myPos);
 				moveAlongVector(\%pos, $myPos, \%vec, $dist - 1);
-				move($pos{x}, $pos{y});
+				$char->move(@pos{qw(x y)});
 			} else {
 				my $pos = $item->{pos};
 				message TF("Routing to (%s, %s) to take %s (%s), distance %s\n", $pos->{x}, $pos->{y}, $item->{name}, $item->{binID}, $dist);
@@ -2022,7 +1971,7 @@
 						getVector(\%vec, $player->{pos_to}, $char->{pos_to});
 						moveAlongVector(\%pos, $char->{pos_to}, \%vec, $dist - $config{followDistanceMin});
 						$timeout{ai_sit_idle}{time} = time;
-						$messageSender->sendMove($pos{x}, $pos{y});
+						$char->sendMove(@pos{qw(x y)});
 					}
 				}
 			}
@@ -2157,7 +2106,7 @@
 				getVector(\%vec, $pos, $char->{pos_to});
 				moveAlongVector(\%pos_to, $char->{pos_to}, \%vec, $dist);
 				$timeout{ai_sit_idle}{time} = time;
-				move($pos_to{x}, $pos_to{y});
+				$char->move(@pos_to{qw(x y)});
 				$pos->{x} = int $pos_to{x};
 				$pos->{y} = int $pos_to{y};
 
@@ -2174,7 +2123,7 @@
 		} elsif ($args->{'lost_stuck'}) {
 			if ($args->{'follow_lost_portalID'} eq "") {
 				moveAlongVector($ai_v{'temp'}{'pos'}, $chars[$config{'char'}]{'pos_to'}, $args->{'ai_follow_lost_vec'}, $config{'followLostStep'} / ($args->{'lost_stuck'} + 1));
-				move($ai_v{'temp'}{'pos'}{'x'}, $ai_v{'temp'}{'pos'}{'y'});
+				$char->move(@{$ai_v{temp}{pos}}{qw(x y)});
 			}
 		} else {
 			my $portalID = $args->{follow_lost_portalID};
@@ -2187,7 +2136,7 @@
 				}
 			} else {
 				moveAlongVector($ai_v{'temp'}{'pos'}, $chars[$config{'char'}]{'pos_to'}, $args->{'ai_follow_lost_vec'}, $config{'followLostStep'});
-				move($ai_v{'temp'}{'pos'}{'x'}, $ai_v{'temp'}{'pos'}{'y'});
+				$char->move(@{$ai_v{temp}{pos}}{qw(x y)});
 			}
 		}
 	}
@@ -2814,7 +2763,7 @@
 				my (%vec, %pos);
 				getVector(\%vec, $items{$ID}{pos}, $myPos);
 				moveAlongVector(\%pos, $myPos, \%vec, $dist - 1);
-				move($pos{x}, $pos{y});
+				$char->move(@pos{qw(x y)});
 			} else {
 				my $item = $items{$ID};
 				my $pos = $item->{pos};
Index: AI/Attack.pm
===================================================================
--- AI/Attack.pm	(revision 7684)
+++ AI/Attack.pm	(working copy)
@@ -396,7 +396,7 @@
 	} elsif (!$cleanMonster) {
 		# Drop target if it's already attacked by someone else
 		message T("Dropping target - you will not kill steal others\n"), "ai_attack";
-		$messageSender->sendMove($realMyPos->{x}, $realMyPos->{y});
+		$char->sendMove(@{$realMyPos}{qw(x y)});
 		AI::dequeue;
 		if ($config{teleportAuto_dropTargetKS}) {
 			message T("Teleporting due to dropping attack target\n"), "teleport";
@@ -456,9 +456,9 @@
 			if ($config{attackChangeTarget} == 1) {
 				# Restart attack from processAutoAttack
 				AI::dequeue;
-				ai_route($field->baseName, $best_spot->{x}, $best_spot->{y}, LOSSubRoute => 1);
+				$char->route(undef, @{$best_spot}{qw(x y)}, LOSSubRoute => 1);
 			} else {
-				ai_route($field->baseName, $best_spot->{x}, $best_spot->{y});
+				$char->route(undef, @{$best_spot}{qw(x y)});
 			}
 		} else {
 			warning TF("%s; no acceptable place to stand\n", $msg);
@@ -523,7 +523,7 @@
 		#message "Time spent: " . (time - $begin) . "\n";
 		#debug_showSpots('runFromTarget', \@blocks, $bestBlock);
 		$args->{avoiding} = 1;
-		move($bestBlock->{x}, $bestBlock->{y}, $ID);
+		$char->move(@{$bestBlock}{qw(x y)}, $ID);
 
 	} elsif ($realMonsterDist > $args->{attackMethod}{maxDistance}
 	  && timeOut($args->{ai_attack_giveup}, 0.5)) {
@@ -537,7 +537,7 @@
 		debug "Target distance $dist is >$args->{attackMethod}{maxDistance}; moving to target: " .
 			"from ($myPos->{x},$myPos->{y}) to ($pos->{x},$pos->{y})\n", "ai_attack";
 
-		my $result = ai_route($field->baseName, $pos->{x}, $pos->{y},
+		my $result = $char->route(undef, @{$pos}{qw(x y)},
 			maxRouteTime => $config{'attackMaxRouteTime'},
 			attackID => $ID,
 			noMapRoute => 1,
@@ -569,7 +569,7 @@
 			# Our recorded position might be out of sync, so try to unstuck
 			$args->{unstuck}{time} = time;
 			debug("Attack - trying to unstuck\n", "ai_attack");
-			move($myPos->{x}, $myPos->{y});
+			$char->move(@{$myPos}{qw(x y)});
 			$args->{unstuck}{count}++;
 		}
 
Index: AI/Slave.pm
===================================================================
--- AI/Slave.pm	(revision 7684)
+++ AI/Slave.pm	(working copy)
@@ -136,7 +136,7 @@
 		# auto-follow
 		if (
 			$slave->{slave_AI} == AI::AUTO
-			&& AI::action eq "move"
+			&& (AI::action eq "move" || AI::action eq "route")
 			&& !$char->{sitting}
 			&& !AI::args->{mapChanged}
 			&& !AI::args->{time_move} != $char->{time_move}
@@ -148,7 +148,7 @@
 		) {
 			$slave->clear('move', 'route');
 			if (!checkLineWalkable($slave->{pos_to}, $char->{pos_to})) {
-				$slave->slave_route($char->{pos_to}{x}, $char->{pos_to}{y});
+				$slave->route(undef, @{$char->{pos_to}}{qw(x y)});
 				$slave->args->{follow_route} = 1 if $slave->action eq 'route';
 				debug sprintf("Slave follow route (distance: %.2f)\n", $slave->distance()), 'homunculus';
 	
@@ -231,8 +231,14 @@
 			return unless $slave->{slave_AI};
 			return if $slave->processClientSuspend;
 			$slave->processAttack;
-			$slave->processRouteAI;
-			$slave->processMove;
+			$slave->processTask('route', onError => sub {
+				my ($task, $error) = @_;
+				if (!($task->isa('Task::MapRoute') && $error->{code} == Task::MapRoute::TOO_MUCH_TIME())
+				 && !($task->isa('Task::Route') && $error->{code} == Task::Route::TOO_MUCH_TIME())) {
+					error("$error->{message}\n");
+				}
+			});
+			$slave->processTask('move');
 			return unless $slave->{slave_AI} == AI::AUTO;
 			$slave->processAutoAttack;
 		}
@@ -255,50 +261,6 @@
 	$slave->sendMove ($pos->{x}, $pos->{y});
 }
 
-sub slave_route {
-	my $slave = shift;
-	my $map = $field->baseName;
-	my $x = shift;
-	my $y = shift;
-	my %param = @_;
-	debug "Slave on route to: " .$field->descString(). ": $x, $y\n", "route";
-
-	my %args;
-	$x = int($x) if ($x ne "");
-	$y = int($y) if ($y ne "");
-	$args{'dest'}{'map'} = $map;
-	$args{'dest'}{'pos'}{'x'} = $x;
-	$args{'dest'}{'pos'}{'y'} = $y;
-	$args{'maxRouteDistance'} = $param{maxRouteDistance} if exists $param{maxRouteDistance};
-	$args{'maxRouteTime'} = $param{maxRouteTime} if exists $param{maxRouteTime};
-	$args{'attackOnRoute'} = $param{attackOnRoute} if exists $param{attackOnRoute};
-	$args{'distFromGoal'} = $param{distFromGoal} if exists $param{distFromGoal};
-	$args{'pyDistFromGoal'} = $param{pyDistFromGoal} if exists $param{pyDistFromGoal};
-	$args{'attackID'} = $param{attackID} if exists $param{attackID};
-	$args{'noSitAuto'} = $param{noSitAuto} if exists $param{noSitAuto};
-	$args{'noAvoidWalls'} = $param{noAvoidWalls} if exists $param{noAvoidWalls};
-	$args{notifyUponArrival} = $param{notifyUponArrival} if exists $param{notifyUponArrival};
-	$args{'tags'} = $param{tags} if exists $param{tags};
-	$args{'time_start'} = time;
-
-	if (!$param{'_internal'}) {
-		$args{'solution'} = [];
-		$args{'mapSolution'} = [];
-	} elsif (exists $param{'_solution'}) {
-		$args{'solution'} = $param{'_solution'};
-	}
-
-	# Destination is same map and isn't blocked by walls/water/whatever
-	my $pos = calcPosition($slave);
-	require Task::Route;
-	if ($param{'_internal'} || (Task::Route->getRoute(\@{$args{solution}}, $field, $pos, $args{dest}{pos}, !$args{noAvoidWalls}))) {
-		# Since the solution array is here, we can start in "Route Solution Ready"
-		$args{'stage'} = 'Route Solution Ready';
-		debug "Slave route Solution Ready\n", "route";
-		$slave->queue("route", \%args);
-	}
-}
-
 ##### ATTACK #####
 sub processAttack {
 	my $slave = shift;
@@ -524,7 +486,7 @@
 			my $msg = TF("%s has no LOS from (%d, %d) to target (%d, %d)", $slave, $realMyPos->{x}, $realMyPos->{y}, $realMonsterPos->{x}, $realMonsterPos->{y});
 			if ($best_spot) {
 				message TF("%s; moving to (%s, %s)\n", $msg, $best_spot->{x}, $best_spot->{y}), 'homunculus_attack';
-				$slave->slave_route($best_spot->{x}, $best_spot->{y});
+				$slave->route(undef, @{$best_spot}{qw(x y)});
 			} else {
 				warning TF("%s; no acceptable place to stand\n", $msg);
 				$slave->dequeue;
@@ -609,7 +571,7 @@
 			debug "Slave target distance $dist is >$args->{attackMethod}{maxDistance}; moving to target: " .
 				"from ($myPos->{x},$myPos->{y}) to ($pos->{x},$pos->{y})\n", "ai_attack";
 
-			my $result = $slave->slave_route($pos->{x}, $pos->{y},
+			my $result = $slave->route(undef, @{$pos}{qw(x y)},
 				distFromGoal => $args->{attackMethod}{distance},
 				maxRouteTime => $config{$slave->{configPrefix}.'attackMaxRouteTime'},
 				attackID => $ID,
@@ -689,186 +651,6 @@
 	#Benchmark::end("ai_homunculus_attack") if DEBUG;
 }
 
-####### ROUTE #######
-sub processRouteAI {
-	my $slave = shift;
-	
-	if ($slave->action eq "route" && $slave->args->{suspended}) {
-		$slave->args->{time_start} += time - $slave->args->{suspended};
-		$slave->args->{time_step} += time - $slave->args->{suspended};
-		delete $slave->args->{suspended};
-	}
-
-	if ($slave->action eq "route" && $field->baseName && $slave->{pos_to}{x} ne '' && $slave->{pos_to}{y} ne '') {
-		my $args = $slave->args;
-
-		if ( $args->{maxRouteTime} && timeOut($args->{time_start}, $args->{maxRouteTime})) {
-			# We spent too much time
-			debug "Slave route - we spent too much time; bailing out.\n", "route";
-			$slave->dequeue;
-
-		} elsif ($field->baseName ne $args->{dest}{map} || $args->{mapChanged}) {
-			debug "Slave map changed: $field->baseName $args->{dest}{map}\n", "route";
-			$slave->dequeue;
-
-		} elsif ($args->{stage} eq '') {
-			my $pos = calcPosition($slave);
-			$args->{solution} = [];
-			if (Task::Route->getRoute($args->{solution}, $field, $pos, $args->{dest}{pos})) {
-				$args->{stage} = 'Route Solution Ready';
-				debug "Slave route Solution Ready\n", "route";
-			} else {
-				debug "Something's wrong; there is no path to ".$field->baseName."($args->{dest}{pos}{x},$args->{dest}{pos}{y}).\n", "debug";
-				$slave->dequeue;
-			}
-
-		} elsif ($args->{stage} eq 'Route Solution Ready') {
-			my $solution = $args->{solution};
-			if ($args->{maxRouteDistance} > 0 && $args->{maxRouteDistance} < 1) {
-				# Fractional route motion
-				$args->{maxRouteDistance} = int($args->{maxRouteDistance} * scalar(@{$solution}));
-			}
-			splice(@{$solution}, 1 + $args->{maxRouteDistance}) if $args->{maxRouteDistance} && $args->{maxRouteDistance} < @{$solution};
-
-			# Trim down solution tree for pyDistFromGoal or distFromGoal
-			if ($args->{pyDistFromGoal}) {
-				my $trimsteps = 0;
-				$trimsteps++ while ($trimsteps < @{$solution}
-						 && distance($solution->[@{$solution} - 1 - $trimsteps], $solution->[@{$solution} - 1]) < $args->{pyDistFromGoal}
-					);
-				debug "Slave route - trimming down solution by $trimsteps steps for pyDistFromGoal $args->{'pyDistFromGoal'}\n", "route";
-				splice(@{$args->{'solution'}}, -$trimsteps) if ($trimsteps);
-			} elsif ($args->{distFromGoal}) {
-				my $trimsteps = $args->{distFromGoal};
-				$trimsteps = @{$args->{'solution'}} if $trimsteps > @{$args->{'solution'}};
-				debug "Slave route - trimming down solution by $trimsteps steps for distFromGoal $args->{'distFromGoal'}\n", "route";
-				splice(@{$args->{solution}}, -$trimsteps) if ($trimsteps);
-			}
-
-			undef $args->{mapChanged};
-			undef $args->{index};
-			undef $args->{old_x};
-			undef $args->{old_y};
-			undef $args->{new_x};
-			undef $args->{new_y};
-			$args->{time_step} = time;
-			$args->{stage} = 'Walk the Route Solution';
-
-		} elsif ($args->{stage} eq 'Walk the Route Solution') {
-
-			my $pos = calcPosition($slave);
-			my ($cur_x, $cur_y) = ($pos->{x}, $pos->{y});
-
-			unless (@{$args->{solution}}) {
-				# No more points to cover; we've arrived at the destination
-				if ($args->{notifyUponArrival}) {
- 					message TF("%s destination reached.\n", $slave), "success";
-				} else {
-					debug "Slave destination reached.\n", "route";
-				}
-				$slave->dequeue;
-
-			} elsif ($args->{old_x} == $cur_x && $args->{old_y} == $cur_y && timeOut($args->{time_step}, 3)) {
-				# We tried to move for 3 seconds, but we are still on the same spot,
-				# decrease step size.
-				# However, if $args->{index} was already 0, then that means
-				# we were almost at the destination (only 1 more step is needed).
-				# But we got interrupted (by auto-attack for example). Don't count that
-				# as stuck.
-				my $wasZero = $args->{index} == 0;
-				$args->{index} = int($args->{index} * 0.8);
-				if ($args->{index}) {
-					debug "Slave route - not moving, decreasing step size to $args->{index}\n", "route";
-					if (@{$args->{solution}}) {
-						# If we still have more points to cover, walk to next point
-						$args->{index} = @{$args->{solution}} - 1 if $args->{index} >= @{$args->{solution}};
-						$args->{new_x} = $args->{solution}[$args->{index}]{x};
-						$args->{new_y} = $args->{solution}[$args->{index}]{y};
-						$args->{time_step} = time;
-						$slave->move($args->{new_x}, $args->{new_y}, $args->{attackID});
-					}
-				} elsif (!$wasZero) {
-					# We're stuck
-					my $msg = TF("Slave is stuck at %s (%d,%d), while walking from (%d,%d) to (%d,%d).", 
-						$field->baseName, $slave->{pos_to}{x}, $slave->{pos_to}{y}, $cur_x, $cur_y, $args->{dest}{pos}{x}, $args->{dest}{pos}{y});
-					$msg .= T(" Teleporting to unstuck.") if $config{$slave->{configPrefix}.'teleportAuto_unstuck'};
-					$msg .= "\n";
-					warning $msg, "route";
-					useTeleport(1) if $config{$slave->{configPrefix}.'teleportAuto_unstuck'};
-					$slave->dequeue;
-				} else {
-					$args->{time_step} = time;
-				}
-
-			} else {
-				# We're either starting to move or already moving, so send out more
-				# move commands periodically to keep moving and updating our position
-				my $solution = $args->{solution};
-				$args->{index} = $config{$slave->{configPrefix}.'route_step'} unless $args->{index};
-				$args->{index}++ if ($args->{index} < $config{$slave->{configPrefix}.'route_step'});
-
-				if (defined($args->{old_x}) && defined($args->{old_y})) {
-					# See how far we've walked since the last move command and
-					# trim down the soultion tree by this distance.
-					# Only remove the last step if we reached the destination
-					my $trimsteps = 0;
-					# If position has changed, we must have walked at least one step
-					$trimsteps++ if ($cur_x != $args->{'old_x'} || $cur_y != $args->{'old_y'});
-					# Search the best matching entry for our position in the solution
-					while ($trimsteps < @{$solution}
-							 && distance( { x => $cur_x, y => $cur_y }, $solution->[$trimsteps + 1])
-							    < distance( { x => $cur_x, y => $cur_y }, $solution->[$trimsteps])
-						) {
-						$trimsteps++;
-					}
-					# Remove the last step also if we reached the destination
-					$trimsteps = @{$solution} - 1 if ($trimsteps >= @{$solution});
-					#$trimsteps = @{$solution} if ($trimsteps <= $args->{'index'} && $args->{'new_x'} == $cur_x && $args->{'new_y'} == $cur_y);
-					$trimsteps = @{$solution} if ($cur_x == $solution->[$#{$solution}]{x} && $cur_y == $solution->[$#{$solution}]{y});
-					debug "Slave route - trimming down solution (" . @{$solution} . ") by $trimsteps steps\n", "route";
-					splice(@{$solution}, 0, $trimsteps) if ($trimsteps > 0);
-				}
-
-				my $stepsleft = @{$solution};
-				if ($stepsleft > 0) {
-					# If we still have more points to cover, walk to next point
-					$args->{index} = $stepsleft - 1 if ($args->{index} >= $stepsleft);
-					$args->{new_x} = $args->{solution}[$args->{index}]{x};
-					$args->{new_y} = $args->{solution}[$args->{index}]{y};
-
-					# But first, check whether the distance of the next point isn't abnormally large.
-					# If it is, then we've moved to an unexpected place. This could be caused by auto-attack,
-					# for example.
-					my %nextPos = (x => $args->{new_x}, y => $args->{new_y});
-					if (distance(\%nextPos, $pos) > $config{$slave->{configPrefix}.'route_step'}) {
-						debug "Slave route - movement interrupted: reset route\n", "route";
-						$args->{stage} = '';
-
-					} else {
-						$args->{old_x} = $cur_x;
-						$args->{old_y} = $cur_y;
-						$args->{time_step} = time if ($cur_x != $args->{old_x} || $cur_y != $args->{old_y});
-						debug "Slave route - next step moving to ($args->{new_x}, $args->{new_y}), index $args->{index}, $stepsleft steps left\n", "route";
-						$slave->move($args->{new_x}, $args->{new_y}, $args->{attackID});
-					}
-				} else {
-					# No more points to cover
-					if ($args->{notifyUponArrival}) {
- 						message TF("%s destination reached.\n", $slave), "success";
-					} else {
-						debug "Slave destination reached.\n", "route";
-					}
-					$slave->dequeue;
-				}
-			}
-
-		} else {
-			debug "Unexpected slave route stage [$args->{stage}] occured.\n", "route";
-			$slave->dequeue;
-		}
-	}
-}
-
 sub processClientSuspend {
 	my $slave = shift;
 	##### CLIENT SUSPEND #####
Index: Task/Move.pm
===================================================================
--- Task/Move.pm	(revision 7684)
+++ Task/Move.pm	(working copy)
@@ -33,10 +33,11 @@
 use Task::WithSubtask;
 use base qw(Task::WithSubtask);
 use Task::SitStand;
-use Globals qw(%timeout $char $net $messageSender);
+use Globals qw(%timeout $net);
 use Plugins;
 use Network;
 use Log qw(warning debug);
+use Translation qw(T TF);
 use Utils qw(timeOut);
 use Utils::Exceptions;
 
@@ -72,12 +73,11 @@
 	my %args = @_;
 	my $self = $class->SUPER::new(@_, autostop => 1, autofail => 1, mutexes => MUTEXES);
 
-	if ($args{x} == 0 || $args{y} == 0) {
+	unless ($args{actor}->isa('Actor') and $args{x} != 0 and $args{y} != 0) {
 		ArgumentException->throw(error => "Invalid arguments.");
 	}
 
-	$self->{x} = $args{x};
-	$self->{y} = $args{y};
+	$self->{$_} = $args{$_} for qw(actor x y);
 	$self->{retry}{timeout} = $args{retryTime} || 0.5;
 	$self->{giveup}{timeout} = $args{giveupTime} || $timeout{ai_move_giveup}{timeout} || 3;
 
@@ -125,29 +125,29 @@
 	return if ($net->getState() != Network::IN_GAME);
 
 	# If we're sitting, wait until we've stood up.
-	if ($char->{sitting}) {
-		debug "Move - trying to stand\n", "move";
-		my $task = new Task::SitStand(mode => 'stand');
+	if ($self->{actor}{sitting}) {
+		debug "Move $self->{actor} - trying to stand\n", "move";
+		my $task = new Task::SitStand(actor => $self->{actor}, mode => 'stand');
 		$self->setSubtask($task);
 
 	# Stop if the map changed.
 	} elsif ($self->{mapChanged}) {
-		debug "Move - map change detected\n", "move";
+		debug "Move $self->{actor} - map change detected\n", "move";
 		$self->setDone();
 
 	# Stop if we've moved.
-	} elsif ($char->{time_move} > $self->{start_time}) {
-		debug "Move - done\n", "move";
+	} elsif ($self->{actor}{time_move} > $self->{start_time}) {
+		debug "Move $self->{actor} - done\n", "move";
 		$self->setDone();
 
 	# Stop if we've timed out.
 	} elsif (timeOut($self->{giveup})) {
-		debug "Move - timeout\n", "move";
-		$self->setError(TOO_LONG, "Tried too long to move");
+		debug "Move $self->{actor} - timeout\n", "move";
+		$self->setError(TOO_LONG, TF("%s tried too long to move", $self->{actor}));
 
 	} elsif (timeOut($self->{retry})) {
-		debug "Move - (re)trying\n", "move";
-		$messageSender->sendMove($self->{x}, $self->{y});
+		debug "Move $self->{actor} - (re)trying\n", "move";
+		$self->{actor}->sendMove(@{$self}{qw(x y)});
 		$self->{retry}{time} = time;
 	}
 }
Index: Task/CheckPoints.pm
===================================================================
--- Task/CheckPoints.pm	(revision 7684)
+++ Task/CheckPoints.pm	(working copy)
@@ -72,6 +72,8 @@
 		$self->{index} += $self->{inc};
 
 		my $task = new Task::MapRoute(
+			# FIXME
+			actor => $char,
 			map => $point->{map},
 			x => $point->{x},
 			y => $point->{y},
Index: Task/SitStand.pm
===================================================================
--- Task/SitStand.pm	(revision 7684)
+++ Task/SitStand.pm	(working copy)
@@ -23,7 +23,7 @@
 use Modules 'register';
 use Task;
 use base qw(Task);
-use Globals qw(%timeout $char $messageSender $net);
+use Globals qw(%timeout $net);
 use Network;
 use Skill;
 use Translation qw(T);
@@ -52,11 +52,11 @@
 	my %args = @_;
 	my $self = $class->SUPER::new(@_, mutexes => MUTEXES);
 
-	if ($args{mode} ne 'sit' && $args{mode} ne 'stand') {
-		ArgumentException->throw("No mode specified.");
+	unless ($args{actor}->isa('Actor') and $args{mode} eq 'sit' || $args{mode} eq 'stand') {
+		ArgumentException->throw("Invalid arguments.");
 	}
 
-	$self->{mode} = $args{mode};
+	$self->{$_} = $args{$_} for qw(actor mode);
 	$self->{wait}{timeout} = $args{wait};
 	$self->{retry}{timeout} = $timeout{ai_stand_wait}{timeout} || 1;
 	# $self->{sitSkill} = new Skill(handle => 'NV_BASIC');
@@ -92,18 +92,18 @@
 	$self->SUPER::iterate();
 	return unless ($net->getState() == Network::IN_GAME);
 
-	if (($self->{mode} eq 'stand' && !$char->{sitting}) || ($self->{mode} eq 'sit' && $char->{sitting})) {
+	unless ($self->{mode} eq 'sit' xor $self->{actor}{sitting}) {
 		$self->setDone();
 		$timeout{ai_sit}{time} = $timeout{ai_sit_wait}{time} = 0;
 
-	} elsif ($char->getSkillLevel(new Skill(handle => 'NV_BASIC')) < 3 && ($char->{jobID} == 0 || $char->{jobID} == 161)) {  # Check NV_BASIC skill only for Novice and High Novice
+	} elsif ($self->{actor}->getSkillLevel(new Skill(handle => 'NV_BASIC')) < 3 && ($self->{actor}{jobID} == 0 || $self->{actor}{jobID} == 161)) {  # Check NV_BASIC skill only for Novice and High Novice
 		$self->setError(NO_SIT_STAND_SKILL, T("Basic Skill level 3 is required in order to sit or stand."));
 
 	} elsif (timeOut($self->{wait}) && timeOut($self->{retry})) {
 		if ($self->{mode} eq 'stand') {
-			$messageSender->sendAction(undef, 3);
+			$self->{actor}->sendStand;
 		} else {
-			$messageSender->sendAction(undef, 2);
+			$self->{actor}->sendSit;
 		}
 		$self->{retry}{time} = time;
 	}
Index: Task/UseSkill.pm
===================================================================
--- Task/UseSkill.pm	(revision 7684)
+++ Task/UseSkill.pm	(working copy)
@@ -88,7 +88,7 @@
 	my %args = @_;
 	my $self = $class->SUPER::new(@_, manageMutexes => 1, mutexes => ['movement', 'skill']);
 
-	if (!$args{skill}) {
+	unless ($args{actor}->isa('Actor') and $args{skill}) {
 		ArgumentException->throw("No skill argument given.");
 	}
 
@@ -301,8 +301,7 @@
 		if (!$self->getSubtask()) {
 			my $task = new Task::Chained(tasks => [
 				# TODO: equip here (merge with AI::CoreLogic::processSkillUse)
-				# TODO: pass $self->{actor} to Task::SitStand
-				new Task::SitStand(mode => 'stand')
+				new Task::SitStand(actor => $self->{actor}, mode => 'stand')
 			]);
 			$self->setSubtask($task);
 			$self->{preparationTask} = $task;
Index: Task/FollowActor.pm
===================================================================
--- Task/FollowActor.pm	(revision 7684)
+++ Task/FollowActor.pm	(working copy)
@@ -134,6 +134,8 @@
 
 		if (!$self->{task}) {
 			my $task = $self->{task} = new Task::Route(
+				# FIXME
+				actor => $char,
 				x => $interceptPoint->{x},
 				y => $interceptPoint->{y},
 				maxTime => 5);
Index: Task/MapRoute.pm
===================================================================
--- Task/MapRoute.pm	(revision 7684)
+++ Task/MapRoute.pm	(working copy)
@@ -84,7 +84,7 @@
 	# TODO: do we need a mutex 'npc' too?
 	my $self = $class->SUPER::new(@_, autostop => 1, autofail => 0, mutexes => ['movement']);
 
-	if (!$args{map}) {
+	unless ($args{actor}->isa('Actor') and $args{map}) {
 		ArgumentException->throw(error => "Invalid arguments.");
 	}
 
@@ -96,6 +96,7 @@
 		}
 	}
 
+	$self->{actor} = $args{actor};
 	($self->{dest}{map}, undef) = Field::nameToBaseName(undef, $args{map}); # Hack to clean up InstanceID
 	# $self->{dest}{map} = $args{map};
 	$self->{dest}{pos}{x} = $args{x};
@@ -129,7 +130,8 @@
 sub iterate {
 	my ($self) = @_;
 	return if (!$self->SUPER::iterate() || $net->getState() != Network::IN_GAME);
-	return if (!$field || !defined $char->{pos_to}{x} || !defined $char->{pos_to}{y});
+	# FIXME: don't use global $field in tasks
+	return unless defined $field && defined $self->{actor}{pos_to} && defined $self->{actor}{pos_to}{x} && defined $self->{actor}{pos_to}{y};
 
 	# When the CalcMapRouter subtask finishes, a new Route task may be set as subtask.
 	# In that case we don't want to continue or this MapRoute task may end prematurely.
@@ -173,13 +175,12 @@
 				}
 			}
 
-		} elsif (distance($char->{pos_to}, $self->{mapSolution}[0]{pos}) <= 10) {
+		} elsif (distance($self->{actor}{pos_to}, $self->{mapSolution}[0]{pos}) <= 10) {
 			my ($from,$to) = split /=/, $self->{mapSolution}[0]{portal};
-			if ($char->{zeny} >= $portals_lut{$from}{dest}{$to}{cost}) {
+			if ($self->{actor}{zeny} >= $portals_lut{$from}{dest}{$to}{cost}) {
 				# We have enough money for this service.
 				$self->{substage} = 'Waiting for Warp';
-				$self->{old_x} = $char->{pos_to}{x};
-				$self->{old_y} = $char->{pos_to}{y};
+				@{$self}{qw(old_x old_y)} = @{$self->{actor}{pos_to}}{qw(x y)};
 				$self->{old_map} = $field->baseName;
 				my $task = new Task::TalkNPC(
 					x => $self->{mapSolution}[0]{pos}{x},
@@ -198,11 +199,12 @@
 			debug "MapRoute - We spent too much time; bailing out.\n", "route";
 			$self->setError(TOO_MUCH_TIME, "Too much time spent on route traversal.");
 
-		} elsif ( Task::Route->getRoute(\@solution, $field, $char->{pos_to}, $self->{mapSolution}[0]{pos}) ) {
+		} elsif ( Task::Route->getRoute(\@solution, $field, $self->{actor}{pos_to}, $self->{mapSolution}[0]{pos}) ) {
 			# NPC is reachable from current position
 			# >> Then "route" to it
 			debug "Walking towards the NPC\n", "route";
 			my $task = new Task::Route(
+				actor => $self->{actor},
 				x => $self->{mapSolution}[0]{pos}{x},
 				y => $self->{mapSolution}[0]{pos}{y},
 				maxTime => $self->{maxTime},
@@ -215,7 +217,7 @@
 		} else {
 			# Error, NPC is not reachable from current pos
 			debug "CRITICAL ERROR: NPC is not reachable from current location.\n", "route";
-			error TF("Unable to walk from %s (%s,%s) to NPC at (%s,%s).\n", $field->baseName, $char->{pos_to}{x}, $char->{pos_to}{y}, $self->{mapSolution}[0]{pos}{x}, $self->{mapSolution}[0]{pos}{y}), "route";
+			error TF("Unable to walk from %s (%s,%s) to NPC at (%s,%s).\n", $field->baseName, @{$self->{actor}{pos_to}}{qw(x y)}, $self->{mapSolution}[0]{pos}{x}, $self->{mapSolution}[0]{pos}{y}), "route";
 			shift @{$self->{mapSolution}};
 		}
 
@@ -224,7 +226,7 @@
 		my $distFromGoal = $self->{pyDistFromGoal}
 			? $self->{pyDistFromGoal}
 			: ($self->{distFromGoal} ? $self->{distFromGoal} : 0);
-		if ( $distFromGoal + 2 > distance($char->{pos_to}, $self->{mapSolution}[0]{pos})) {
+		if ( $distFromGoal + 2 > distance($self->{actor}{pos_to}, $self->{mapSolution}[0]{pos})) {
 			# We need to specify +2 because sometimes the exact spot is occupied by someone else
 			shift @{$self->{mapSolution}};
 
@@ -233,10 +235,11 @@
 			debug "We spent too much time; bailing out.\n", "route";
 			$self->setError(TOO_MUCH_TIME, "Too much time spent on route traversal.");
 
-		} elsif ( Task::Route->getRoute(\@solution, $field, $char->{pos_to}, $self->{mapSolution}[0]{pos}) ) {
+		} elsif ( Task::Route->getRoute(\@solution, $field, $self->{actor}{pos_to}, $self->{mapSolution}[0]{pos}) ) {
 			# X,Y is reachable from current position
 			# >> Then "route" to it
 			my $task = new Task::Route(
+				actor => $self->{actor},
 				x => $self->{mapSolution}[0]{pos}{x},
 				y => $self->{mapSolution}[0]{pos}{y},
 				maxTime => $self->{maxTime},
@@ -249,7 +252,7 @@
 
 		} else {
 			warning TF("No LOS from %s (%s,%s) to Final Destination at (%s,%s).\n",
-				$field->baseName, $char->{pos_to}{x}, $char->{pos_to}{y},
+				$field->baseName, @{$self->{actor}{pos_to}}{qw(x y)},
 				$self->{mapSolution}[0]{pos}{x},
 				$self->{mapSolution}[0]{pos}{y}), "route";
 			error TF("Cannot reach (%s,%s) from current position.\n",
@@ -261,11 +264,11 @@
 	} elsif ( $portals_lut{"$self->{mapSolution}[0]{map} $self->{mapSolution}[0]{pos}{x} $self->{mapSolution}[0]{pos}{y}"}{source} ) {
 		# This is a portal solution
 
-		if ( distance($char->{pos_to}, $self->{mapSolution}[0]{pos}) < 2 ) {
+		if ( distance($self->{actor}{pos_to}, $self->{mapSolution}[0]{pos}) < 2 ) {
 			# Portal is within 'Enter Distance'
 			$timeout{ai_portal_wait}{timeout} = $timeout{ai_portal_wait}{timeout} || 0.5;
 			if ( timeOut($timeout{ai_portal_wait}) ) {
-				$messageSender->sendMove(int($self->{mapSolution}[0]{'pos'}{'x'}), int($self->{mapSolution}[0]{'pos'}{'y'}) );
+				$self->{actor}->sendMove(map int, @{$self->{mapSolution}[0]{pos}}{qw(x y)});
 				$timeout{'ai_portal_wait'}{'time'} = time;
 			}
 
@@ -294,7 +297,7 @@
 					}
 
 					my $dist = new PathFinding(
-						start => $char->{pos_to},
+						start => $self->{actor}{pos_to},
 						dest => $portal->{pos},
 						field => $field
 					)->runcount;
@@ -325,12 +328,13 @@
 			}
 
 			if ($walk) {
-				if ( Task::Route->getRoute( \@solution, $field, $char->{pos_to}, $self->{mapSolution}[0]{pos} ) ) {
+				if ( Task::Route->getRoute( \@solution, $field, $self->{actor}{pos_to}, $self->{mapSolution}[0]{pos} ) ) {
 					# Portal is reachable from current position
 					# >> Then "route" to it
 					debug "Portal route within same map.\n", "route";
 					$self->{teleportTries} = 0;
 					my $task = new Task::Route(
+						actor => $self->{actor},
 						x => $self->{mapSolution}[0]{pos}{x},
 						y => $self->{mapSolution}[0]{pos}{y},
 						maxTime => $self->{maxTime},
@@ -341,7 +345,7 @@
 
 				} else {
 					warning TF("No LOS from %s (%s,%s) to Portal at (%s,%s).\n",
-						$field->baseName, $char->{pos_to}{x}, $char->{pos_to}{y},
+						$field->baseName, @{$self->{actor}{pos_to}}{qw(x y)},
 						$self->{mapSolution}[0]{pos}{x}, $self->{mapSolution}[0]{pos}{y}),
 						"route";
 					error T("Cannot reach portal from current position\n"), "route";
@@ -356,8 +360,8 @@
 	my ($self) = @_;
 	my $task = new Task::CalcMapRoute(
 		sourceMap => $field->baseName,
-		sourceX => $char->{pos_to}{x},
-		sourceY => $char->{pos_to}{y},
+		sourceX => $self->{actor}{pos_to}{x},
+		sourceY => $self->{actor}{pos_to}{y},
 		map => $self->{dest}{map},
 		x => $self->{dest}{pos}{x},
 		y => $self->{dest}{pos}{y}
@@ -386,6 +390,7 @@
 			# to walk to.
 			if (@{$self->{mapSolution}} == 0 && defined($self->{dest}{pos}{x}) && defined($self->{dest}{pos}{y})) {
 				my $task = new Task::Route(
+					actor => $self->{actor},
 					x => $self->{dest}{pos}{x},
 					y => $self->{dest}{pos}{y},
 					maxTime => $self->{maxTime},
Index: Task/Route.pm
===================================================================
--- Task/Route.pm	(revision 7684)
+++ Task/Route.pm	(working copy)
@@ -27,7 +27,7 @@
 use base qw(Task::WithSubtask);
 use Task::Move;
 
-use Globals qw($char $field $net %config);
+use Globals qw($field $net %config);
 use Log qw(message debug warning);
 use Network;
 use Field;
@@ -77,7 +77,7 @@
 	my %args = @_;
 	my $self = $class->SUPER::new(@_, autostop => 1, autofail => 0, mutexes => ['movement']);
 
-	if ($args{x} == 0 || $args{y} == 0) {
+	unless ($args{actor}->isa('Actor') and $args{x} != 0 and $args{y} != 0) {
 		ArgumentException->throw(error => "Invalid arguments.");
 	}
 
@@ -89,10 +89,12 @@
 		}
 	}
 
+	$self->{actor} = $args{actor};
+	# FIXME: don't use global $field in tasks
 	$self->{dest}{map} = $field->baseName;
 	$self->{dest}{pos}{x} = $args{x};
 	$self->{dest}{pos}{y} = $args{y};
-	if ($config{'route_avoidWalls'}) {
+	if ($config{$self->{actor}{configPrefix}.'route_avoidWalls'}) {
 		$self->{avoidWalls} = 1 if (!defined $self->{avoidWalls});
 	} else {$self->{avoidWalls} = 0;}
 	$self->{solution} = [];
@@ -147,11 +149,11 @@
 sub iterate {
 	my ($self) = @_;
 	return unless ($self->SUPER::iterate() && $net->getState() == Network::IN_GAME);
-	return unless ($field && defined($char->{pos_to}{x}) && defined($char->{pos_to}{y}));
+	return unless $field && defined $self->{actor}{pos_to} && defined $self->{actor}{pos_to}{x} && defined $self->{actor}{pos_to}{y};
 
 	if ( $self->{maxTime} && timeOut($self->{time_start}, $self->{maxTime})) {
 		# We spent too much time
-		debug "Route - we spent too much time; bailing out.\n", "route";
+		debug "Route $self->{actor} - we spent too much time; bailing out.\n", "route";
 		$self->setError(TOO_MUCH_TIME, "Too much time spent on walking.");
 
 	} elsif ($field->baseName ne $self->{dest}{map} || $self->{mapChanged}) {
@@ -159,11 +161,11 @@
 		$self->setDone();
 
 	} elsif ($self->{stage} eq '') {
-		my $pos = calcPosition($char);
+		my $pos = calcPosition($self->{actor});
 		my $begin = time;
 		if ($self->getRoute($self->{solution}, $field, $pos, $self->{dest}{pos}, $self->{avoidWalls})) {
 			$self->{stage} = 'Route Solution Ready';
-			debug "Route Solution Ready!\n", "route";
+			debug "Route $self->{actor} Solution Ready!\n", "route";
 
 			if (time - $begin < 0.01) {
 				# Optimization: immediately go to the next stage if we
@@ -193,13 +195,13 @@
 			$trimsteps++ while ($trimsteps < @{$solution}
 						&& distance($solution->[@{$solution} - 1 - $trimsteps], $solution->[@{$solution} - 1]) < $self->{pyDistFromGoal}
 				);
-			debug "Route - trimming down solution by $trimsteps steps for pyDistFromGoal $self->{pyDistFromGoal}\n", "route";
+			debug "Route $self->{actor} - trimming down solution by $trimsteps steps for pyDistFromGoal $self->{pyDistFromGoal}\n", "route";
 			splice(@{$self->{'solution'}}, -$trimsteps) if ($trimsteps);
 
 		} elsif ($self->{distFromGoal}) {
 			my $trimsteps = $self->{distFromGoal};
 			$trimsteps = @{$self->{solution}} if ($trimsteps > @{$self->{solution}});
-			debug "Route - trimming down solution by $trimsteps steps for distFromGoal $self->{distFromGoal}\n", "route";
+			debug "Route $self->{actor} - trimming down solution by $trimsteps steps for distFromGoal $self->{distFromGoal}\n", "route";
 			splice(@{$self->{solution}}, -$trimsteps) if ($trimsteps);
 		}
 
@@ -219,15 +221,15 @@
 		}
 
 	} elsif ($self->{stage} eq 'Walk the Route Solution') {
-		my $pos = calcPosition($char);
+		my $pos = calcPosition($self->{actor});
 		my ($cur_x, $cur_y) = ($pos->{x}, $pos->{y});
 
 		if (@{$self->{solution}} == 0) {
 			# No more points to cover; we've arrived at the destination
 			if ($self->{notifyUponArrival}) {
-				message T("Destination reached.\n"), "success";
+				message TF("%s reached the destination.\n", $self->{actor}), "success";
 			} else {
-				debug "Destination reached.\n", "route";
+				debug "$self->{actor} reached the destination.\n", "route";
 			}
 			$self->setDone();
 
@@ -241,7 +243,7 @@
 			my $wasZero = $self->{index} == 0;
 			$self->{index} = int($self->{index} * 0.8);
 			if ($self->{index}) {
-				debug "Route - not moving, decreasing step size to $self->{index}\n", "route";
+				debug "Route $self->{actor} - not moving, decreasing step size to $self->{index}\n", "route";
 				if (@{$self->{solution}}) {
 					# If we still have more points to cover, walk to next point
 					$self->{index} = @{$self->{solution}} - 1 if $self->{index} >= @{$self->{solution}};
@@ -249,6 +251,7 @@
 					$self->{new_y} = $self->{solution}[$self->{index}]{y};
 					$self->{time_step} = time;
 					my $task = new Task::Move(
+						actor => $self->{actor},
 						x => $self->{new_x},
 						y => $self->{new_y});
 					$self->setSubtask($task);
@@ -258,12 +261,12 @@
 				# FIXME: this code looks ugly!
 				# We're stuck
 				my $msg = TF("Stuck at %s (%d,%d), while walking from (%d,%d) to (%d,%d).",
-					$field->baseName, $char->{pos_to}{x}, $char->{pos_to}{y},
+					$field->baseName, @{$self->{actor}{pos_to}}{qw(x y)},
 					$cur_x, $cur_y, $self->{dest}{pos}{x}, $self->{dest}{pos}{y});
-				$msg .= T(" Teleporting to unstuck.") if ($config{teleportAuto_unstuck});
+				$msg .= T(" Teleporting to unstuck.") if ($config{$self->{actor}{configPrefix}.'teleportAuto_unstuck'});
 				$msg .= "\n";
 				warning $msg, "route";
-				Misc::useTeleport(1) if $config{teleportAuto_unstuck};
+				Misc::useTeleport(1) if $config{$self->{actor}{configPrefix}.'teleportAuto_unstuck'};
 				$self->setError(STUCK, T("Stuck during route."));
 			} else {
 				$self->{time_step} = time;
@@ -274,8 +277,8 @@
 			# move commands periodically to keep moving and updating our position
 			my $begin = time;
 			my $solution = $self->{solution};
-			$self->{index} = $config{route_step} unless $self->{index};
-			$self->{index}++ if (($self->{index} < $config{route_step})
+			$self->{index} = $config{$self->{actor}{configPrefix}.'route_step'} unless $self->{index};
+			$self->{index}++ if (($self->{index} < $config{$self->{actor}{configPrefix}.'route_step'})
 			  && ($self->{old_x} != $cur_x || $self->{old_y} != $cur_y));
 
 			if (defined($self->{old_x}) && defined($self->{old_y})) {
@@ -296,7 +299,7 @@
 				$trimsteps = @{$solution} - 1 if ($trimsteps >= @{$solution});
 				#$trimsteps = @{$solution} if ($trimsteps <= $self->{'index'} && $self->{'new_x'} == $cur_x && $self->{'new_y'} == $cur_y);
 				$trimsteps = @{$solution} if ($cur_x == $solution->[$#{$solution}]{x} && $cur_y == $solution->[$#{$solution}]{y});
-				debug "Route - trimming down solution (" . @{$solution} . ") by $trimsteps steps\n", "route";
+				debug "Route $self->{actor} - trimming down solution (" . @{$solution} . ") by $trimsteps steps\n", "route";
 				splice(@{$solution}, 0, $trimsteps) if ($trimsteps > 0);
 			}
 
@@ -311,16 +314,17 @@
 				# If it is, then we've moved to an unexpected place. This could be caused by auto-attack,
 				# for example.
 				my %nextPos = (x => $self->{new_x}, y => $self->{new_y});
-				if (distance(\%nextPos, $pos) > $config{route_step}) {
-					debug "Route - movement interrupted: reset route\n", "route";
+				if (distance(\%nextPos, $pos) > $config{$self->{actor}{configPrefix}.'route_step'}) {
+					debug "Route $self->{actor} - movement interrupted: reset route\n", "route";
 					$self->{stage} = '';
 
 				} else {
 					$self->{old_x} = $cur_x;
 					$self->{old_y} = $cur_y;
 					$self->{time_step} = time if ($cur_x != $self->{old_x} || $cur_y != $self->{old_y});
-					debug "Route - next step moving to ($self->{new_x}, $self->{new_y}), index $self->{index}, $stepsleft steps left\n", "route";
+					debug "Route $self->{actor} - next step moving to ($self->{new_x}, $self->{new_y}), index $self->{index}, $stepsleft steps left\n", "route";
 					my $task = new Task::Move(
+						actor => $self->{actor},
 						x => $self->{new_x},
 						y => $self->{new_y});
 					$self->setSubtask($task);
@@ -334,9 +338,9 @@
 			} else {
 				# No more points to cover
 				if ($self->{notifyUponArrival}) {
-					message T("Destination reached.\n"), "success";
+					message TF("%s reached the destination.\n", $self->{actor}), "success";
 				} else {
-					debug "Destination reached.\n", "route";
+					debug "$self->{actor} reached the destination.\n", "route";
 				}
 				$self->setDone();
 			}
Index: AI.pm
===================================================================
--- AI.pm	(revision 7684)
+++ AI.pm	(working copy)
@@ -475,41 +475,8 @@
 	AI::queue("items_take", \%args);
 }
 
-sub ai_route {
-	my $map = shift;
-	my $x = shift;
-	my $y = shift;
-	my %args = @_;
-	debug "On route to: $maps_lut{$map.'.rsw'}($map): $x, $y\n", "route";
+sub ai_route { $char->route(@_) }
 
-	# I can't use 'use' because of circular dependencies.
-	require Task::Route;
-	require Task::MapRoute;
-
-	my $task;
-	my @params = (
-		x => $x,
-		y => $y,
-		maxDistance => $args{maxRouteDistance},
-		maxTime => $args{maxRouteTime},
-		distFromGoal => $args{distFromGoal},
-		pyDistFromGoal => $args{pyDistFromGoal},
-		avoidWalls => !$args{noAvoidWalls},
-		notifyUponArrival => $args{notifyUponArrival}
-	);
-	if ($args{noMapRoute} || !defined($map)) {
-		$task = new Task::Route(@params);
-	} else {
-		$task = new Task::MapRoute(map => $map, @params);
-	}
-	$task->{attackID} = $args{attackID};
-	$task->{attackOnRoute} = $args{attackOnRoute};
-	$task->{noSitAuto} = $args{noSitAuto};
-	$task->{LOSSubRoute} = $args{LOSSubRoute};
-
-	AI::queue("route", $task);
-}
-
 #sellAuto for items_control - chobit andy 20030210
 sub ai_sellAutoCheck {
 	foreach my $item (@{$char->inventory->getItems()}) {
@@ -680,11 +647,9 @@
 	debug "Targeting for Gather: $items{$ID}{name} ($items{$ID}{binID})\n";
 }
 
-sub move { $char->move(@_) }
-
 sub sit {
 	require Task::SitStand;
-	my $task = new Task::SitStand(mode => 'sit', wait => $timeout{ai_sit_wait}{timeout});
+	my $task = new Task::SitStand(actor => $char, mode => 'sit', wait => $timeout{ai_sit_wait}{timeout});
 	AI::queue("sitting", $task);
 	if (defined $config{sitAuto_look} && !$config{sitAuto_look_from_wall}) {
 		Misc::look($config{sitAuto_look});
@@ -712,7 +677,7 @@
 
 sub stand {
 	require Task::SitStand;
-	my $task = new Task::SitStand(mode => 'stand', wait => $timeout{ai_stand_wait}{timeout});
+	my $task = new Task::SitStand(actor => $char, mode => 'stand', wait => $timeout{ai_stand_wait}{timeout});
 	AI::queue("standing", $task);
 }
 
Index: Actor/You.pm
===================================================================
--- Actor/You.pm	(revision 7684)
+++ Actor/You.pm	(working copy)
@@ -356,6 +356,8 @@
 	$messageSender->sendMove(@{Utils::calcPosition($self)}{qw(x y)});
 }
 
+sub sendSit { $messageSender->sendAction(undef, 2) }
+sub sendStand { $messageSender->sendAction(undef, 3) }
 sub sendMove { $messageSender->sendMove(@_[1, 2]) }
 
 1;

User avatar
kLabMouse
Administrator
Administrator
Posts: 1301
Joined: 24 Apr 2008, 12:02

Re: Movement in tasks, no more processMove

#2 Post by kLabMouse »

Big One.
One thing.
the 'actor' param should be $char by default. If undef, or not Actor provided for param 'actor'.

Technology
Super Moderators
Super Moderators
Posts: 801
Joined: 06 May 2008, 12:47
Noob?: No

Re: Movement in tasks, no more processMove

#3 Post by Technology »

RIP
Basic Skill level 3 is required in order to sit or stand.
BUG: Actor::move(0, 0) called!
and on top of that, also the redundant route specific and the non actor aware task code!?

The above "undocumented features" will truely be missed.................................. NOT! :lol:
/end sarcasm

(btw, i like what you just did there Harvest, kudo's to you)

EDIT:
i believe the following piece of code below was copypasted but allow me to nitpick anyways:

Code: Select all

my ($ai_seq, $ai_seq_args) = $self->isa('Actor::You') ? (\@AI::ai_seq, \@AI::ai_seq_args) : (@{$self}{qw(slave_ai_seq slave_ai_seq_args)});
i'd suggest we remove this code in the long run as it makes little sense to have the Actor class know about the Actor::You class.
One ST0 to rule them all? One PE viewer to find them!
One ST_kRO to bring them all and in the darkness bind them...

Mount Doom awaits us, fellowship of OpenKore!

EternalHarvest
Developers
Developers
Posts: 1798
Joined: 05 Dec 2008, 05:42
Noob?: Yes

Re: Movement in tasks, no more processMove

#4 Post by EternalHarvest »

kLabMouse wrote: the 'actor' param should be $char by default. If undef, or not Actor provided for param 'actor'
There are so many plugins that use current tasks? This behavior can masquerade the case when somebody forgets to specify the actor.
Technology wrote:RIP
Basic Skill level 3 is required in order to sit or stand.
BUG: Actor::move(0, 0) called!
I'm not sure about these two - they're not modified in this patch.
i believe the following piece of code below was copypasted but allow me to nitpick anyways:

Code: Select all

my ($ai_seq, $ai_seq_args) = $self->isa('Actor::You') ? (\@AI::ai_seq, \@AI::ai_seq_args) : (@{$self}{qw(slave_ai_seq slave_ai_seq_args)});
i'd suggest we remove this code in the long run as it makes little sense to have the Actor class know about the Actor::You class.
Well that wasn't copypasted, but was added in desire to change as less as possible existing logic in this particular patch. It surely should be removed when we move global AI vars to Actor::You to make them the same as Homunculus ones.

New issue: now if you can't hit the monster (you miss), anti-bugposition movement would report "tried too long to move" as it tries to move to the same position you're already at. The previous version just did not report any movement timeous in "move" action. Can be fixed later, I guess.

I want to convert all Homunculus AI to tasks after that (there's only attack, follow and feeding left), so we can finally grasp non-queue based AI and experiment with it.

Technology
Super Moderators
Super Moderators
Posts: 801
Joined: 06 May 2008, 12:47
Noob?: No

Re: Movement in tasks, no more processMove

#5 Post by Technology »

Ah, when i wrote copypaste i really should have said "refactor but keeping logic intact" ;)

Hmm, seems like i cheered too soon for the "basic level 3 blabla" bug.
But now to think of it and seeing the code, it could occur when wrong charblocksize is used and the char's job is recognized falsely as novice. (prolly by job ID 0 due to wrong unpacking)

And it looks like this is finally gone now no?

Code: Select all

-   unless ($x || $y) {
-      error "BUG: Actor::move(0, 0) called!\n";
-      return;
-   }
Anyhow, great work.
One ST0 to rule them all? One PE viewer to find them!
One ST_kRO to bring them all and in the darkness bind them...

Mount Doom awaits us, fellowship of OpenKore!

EternalHarvest
Developers
Developers
Posts: 1798
Joined: 05 Dec 2008, 05:42
Noob?: Yes

Re: Movement in tasks, no more processMove

#6 Post by EternalHarvest »

It's still here in Task::Move anyway.
EternalHarvest wrote:

Code: Select all

Index: Task/Move.pm
===================================================================
-	if ($args{x} == 0 || $args{y} == 0) {
+	unless ($args{actor}->isa('Actor') and $args{x} != 0 and $args{y} != 0) {

Technology
Super Moderators
Super Moderators
Posts: 801
Joined: 06 May 2008, 12:47
Noob?: No

Re: Movement in tasks, no more processMove

#7 Post by Technology »

EternalHarvest wrote:It's still here in Task::Move anyway.
EternalHarvest wrote:

Code: Select all

Index: Task/Move.pm
===================================================================
-	if ($args{x} == 0 || $args{y} == 0) {
+	unless ($args{actor}->isa('Actor') and $args{x} != 0 and $args{y} != 0) {
oh ic, well i guess one day... ;)
One ST0 to rule them all? One PE viewer to find them!
One ST_kRO to bring them all and in the darkness bind them...

Mount Doom awaits us, fellowship of OpenKore!

User avatar
kLabMouse
Administrator
Administrator
Posts: 1301
Joined: 24 Apr 2008, 12:02

Re: Movement in tasks, no more processMove

#8 Post by kLabMouse »

Already in SVN. Nice.

Locked