[Scummvm-git-logs] scummvm master -> 404371365a81262f9af8796b80301f6ed8b3c5ab

mduggan mgithub at guarana.org
Thu Dec 24 10:30:07 UTC 2020


This automated email contains information about 7 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
2d7d09bd2c ULTIMA8: Return sprite pid from Cruader fireWeapon to better match original.
31907819a0 ULTIMA8: Split Crusader movement to separate class
df46296f02 ULTIMA8: Add 16 dir support for Direction_Get
ebae8ca44b ULTIMA8: Fix crusader firing in a few ways:
bf4f61b356 ULTIMA8: Crusader: Fix robot death and make absolute anims a bit cleaner.
51c8f66d97 ULTIMA8: Provide quick helper functions for mouse
404371365a ULTIMA8: Reduce code duplication in mover procs


Commit: 2d7d09bd2cc8785a0a2aaf2e1d262c2eefaf95c4
    https://github.com/scummvm/scummvm/commit/2d7d09bd2cc8785a0a2aaf2e1d262c2eefaf95c4
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2020-12-24T14:00:15+09:00

Commit Message:
ULTIMA8: Return sprite pid from Cruader fireWeapon to better match original.

Changed paths:
    engines/ultima/ultima8/world/item.cpp


diff --git a/engines/ultima/ultima8/world/item.cpp b/engines/ultima/ultima8/world/item.cpp
index 74962a590f..ccda99b3cd 100644
--- a/engines/ultima/ultima8/world/item.cpp
+++ b/engines/ultima/ultima8/world/item.cpp
@@ -1216,7 +1216,7 @@ uint16 Item::fireWeapon(int32 x, int32 y, int32 z, Direction dir, int firetype,
 		// is clean for both Item and Actor.
 		const Actor *thisactor = dynamic_cast<Actor *>(this);
 		if (thisactor) {
-			// TODO: Get damage for active inventory item, and do something with
+			// TODO: Get damage for active inventory item, and dirmode of last
 			// animation here (lines 185~208 of disasm)
 		}
 
@@ -1225,6 +1225,7 @@ uint16 Item::fireWeapon(int32 x, int32 y, int32 z, Direction dir, int firetype,
 			if (this != getControlledActor()) {
 				target = getControlledActor();
 			} else {
+				// TODO: this should be dirmode of last animation (above)
 				target = currentmap->findBestTargetItem(ix, iy, dir, dirmode_8dirs);
 			}
 		}
@@ -1263,6 +1264,7 @@ uint16 Item::fireWeapon(int32 x, int32 y, int32 z, Direction dir, int firetype,
 		// handle differently.
 
 		int numshots = firetypedat->getNumShots();
+		uint16 spriteprocpid = 0;
 		for (int i = 0; i < numshots; i++) {
 			SuperSpriteProcess *ssp;
 			CrosshairProcess *chp = CrosshairProcess::get_instance();
@@ -1292,10 +1294,10 @@ uint16 Item::fireWeapon(int32 x, int32 y, int32 z, Direction dir, int firetype,
 										 ix, iy, iz, ssx, ssy, ssz, firetype,
 										 damage, _objId, targetid, someflag);
 			Kernel::get_instance()->addProcess(ssp);
+			spriteprocpid = ssp->getPid();
 		}
+		return spriteprocpid;
 	}
-
-	return 0;
 }
 
 uint16 Item::fireDistance(Item *other, Direction dir, int16 xoff, int16 yoff, int16 zoff) {


Commit: 31907819a0f0f8fc66c7e5736e2c71d6f39a391a
    https://github.com/scummvm/scummvm/commit/31907819a0f0f8fc66c7e5736e2c71d6f39a391a
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2020-12-24T14:00:15+09:00

Commit Message:
ULTIMA8: Split Crusader movement to separate class

Currently there is a lot of duplicated code, but this allows the Crusader
movement to be adjusted a lot to match the original without breaking U8
movement.

Changed paths:
  A engines/ultima/ultima8/world/actors/cru_avatar_mover_process.cpp
  A engines/ultima/ultima8/world/actors/cru_avatar_mover_process.h
  A engines/ultima/ultima8/world/actors/u8_avatar_mover_process.cpp
  A engines/ultima/ultima8/world/actors/u8_avatar_mover_process.h
    engines/ultima/module.mk
    engines/ultima/ultima8/misc/direction_util.h
    engines/ultima/ultima8/ultima8.cpp
    engines/ultima/ultima8/world/actors/animation.cpp
    engines/ultima/ultima8/world/actors/avatar_mover_process.cpp
    engines/ultima/ultima8/world/actors/avatar_mover_process.h


diff --git a/engines/ultima/module.mk b/engines/ultima/module.mk
index 0b0a1eeb37..ac343490e0 100644
--- a/engines/ultima/module.mk
+++ b/engines/ultima/module.mk
@@ -566,6 +566,7 @@ MODULE_OBJS := \
 	ultima8/world/actors/avatar_mover_process.o \
 	ultima8/world/actors/battery_charger_process.o \
 	ultima8/world/actors/clear_feign_death_process.o \
+	ultima8/world/actors/cru_avatar_mover_process.o \
 	ultima8/world/actors/cru_healer_process.o \
 	ultima8/world/actors/combat_dat.o \
 	ultima8/world/actors/combat_process.o \
@@ -583,7 +584,8 @@ MODULE_OBJS := \
 	ultima8/world/actors/scheduler_process.o \
 	ultima8/world/actors/surrender_process.o \
 	ultima8/world/actors/targeted_anim_process.o \
-	ultima8/world/actors/teleport_to_egg_process.o
+	ultima8/world/actors/teleport_to_egg_process.o \
+	ultima8/world/actors/u8_avatar_mover_process.o
 
 # This module can be built as a plugin
 ifeq ($(ENABLE_ULTIMA), DYNAMIC_PLUGIN)
diff --git a/engines/ultima/ultima8/misc/direction_util.h b/engines/ultima/ultima8/misc/direction_util.h
index 927f9dab21..75fd8f0875 100644
--- a/engines/ultima/ultima8/misc/direction_util.h
+++ b/engines/ultima/ultima8/misc/direction_util.h
@@ -37,7 +37,7 @@ namespace Ultima8 {
 inline int Direction_XFactor(Direction dir) {
 	static const int _x_fact[] = {  0, +1, +1, +1,  0, -1, -1, -1 };
 	//static const int _x_fact16[] = {  0, +1, +2, +2, +2, +2, +2, +1, 0, -1, -2, -2, -2, -2, -2, -1 };
-	// TODO: AnimPrimativeProcess uses the below table.. what's the other table for?
+	// TODO: AnimPrimitiveProcess uses the below table.. what's the other table for?
 	// (same for y)
 	static const int _x_fact16[] = {  0, +1, +1, +2, +1, +2, +1, +1, 0, -1, -1, -2, -1, -2, -1, -1 };
 
diff --git a/engines/ultima/ultima8/ultima8.cpp b/engines/ultima/ultima8/ultima8.cpp
index e71ad528d9..913c748d3c 100644
--- a/engines/ultima/ultima8/ultima8.cpp
+++ b/engines/ultima/ultima8/ultima8.cpp
@@ -100,7 +100,8 @@
 #include "ultima/ultima8/world/snap_process.h"
 #include "ultima/ultima8/world/crosshair_process.h"
 #include "ultima/ultima8/world/actors/pathfinder_process.h"
-#include "ultima/ultima8/world/actors/avatar_mover_process.h"
+#include "ultima/ultima8/world/actors/u8_avatar_mover_process.h"
+#include "ultima/ultima8/world/actors/cru_avatar_mover_process.h"
 #include "ultima/ultima8/world/actors/resurrection_process.h"
 #include "ultima/ultima8/world/actors/clear_feign_death_process.h"
 #include "ultima/ultima8/world/actors/loiter_process.h"
@@ -237,8 +238,12 @@ bool Ultima8Engine::startup() {
 		ProcessLoader<ActorAnimProcess>::load);
 	_kernel->addProcessLoader("TargetedAnimProcess",
 		ProcessLoader<TargetedAnimProcess>::load);
-	_kernel->addProcessLoader("AvatarMoverProcess",
-		ProcessLoader<AvatarMoverProcess>::load);
+	_kernel->addProcessLoader("AvatarMoverProcess", // parent class for backward compatibility
+		ProcessLoader<U8AvatarMoverProcess>::load);
+	_kernel->addProcessLoader("U8AvatarMoverProcess",
+		ProcessLoader<U8AvatarMoverProcess>::load);
+	_kernel->addProcessLoader("CruAvatarMoverProcess",
+		ProcessLoader<CruAvatarMoverProcess>::load);
 	_kernel->addProcessLoader("QuickAvatarMoverProcess",
 		ProcessLoader<QuickAvatarMoverProcess>::load);
 	_kernel->addProcessLoader("PathfinderProcess",
@@ -1142,7 +1147,10 @@ bool Ultima8Engine::newGame(int saveSlot) {
 	CameraProcess::SetCameraProcess(new CameraProcess(1)); // Follow Avatar
 
 	debugN(MM_INFO, "Create persistent Processes...\n");
-	_avatarMoverProcess = new AvatarMoverProcess();
+	if (GAME_IS_U8)
+		_avatarMoverProcess = new U8AvatarMoverProcess();
+	else
+		_avatarMoverProcess = new CruAvatarMoverProcess();
 	_kernel->addProcess(_avatarMoverProcess);
 
 	_kernel->addProcess(new HealProcess());
diff --git a/engines/ultima/ultima8/world/actors/animation.cpp b/engines/ultima/ultima8/world/actors/animation.cpp
index d8d8776d63..20e4575009 100644
--- a/engines/ultima/ultima8/world/actors/animation.cpp
+++ b/engines/ultima/ultima8/world/actors/animation.cpp
@@ -37,6 +37,7 @@ bool isCombatAnim(const Sequence anim) {
 	case kick:
 	case startBlock:
 	case stopBlock:
+	case fire2:
 		return true;
 	default:
 		return false;
diff --git a/engines/ultima/ultima8/world/actors/avatar_mover_process.cpp b/engines/ultima/ultima8/world/actors/avatar_mover_process.cpp
index dd0c3cabb2..195fa8d048 100644
--- a/engines/ultima/ultima8/world/actors/avatar_mover_process.cpp
+++ b/engines/ultima/ultima8/world/actors/avatar_mover_process.cpp
@@ -40,12 +40,8 @@
 namespace Ultima {
 namespace Ultima8 {
 
-// p_dynamic_cast stuff
-DEFINE_RUNTIME_CLASSTYPE_CODE(AvatarMoverProcess)
-
 AvatarMoverProcess::AvatarMoverProcess() : Process(),
 		_lastFrame(0), _lastAttack(0), _idleTime(0),
-		_lastHeadShakeAnim(Animation::lookLeft),
 		_movementFlags(0) {
 	_type = 1; // CONSTANT! (type 1 = persistent)
 }
@@ -63,15 +59,13 @@ void AvatarMoverProcess::run() {
 		return;
 	_lastFrame = framenum;
 
-
-	MainActor *avatar = getMainActor();
-
 	// busy, so don't move
 	if (kernel->getNumProcesses(1, ActorAnimProcess::ACTOR_ANIM_PROC_TYPE) > 0) {
 		_idleTime = 0;
 		return;
 	}
 
+	MainActor *avatar = getMainActor();
 
 	if (avatar->getLastAnim() == Animation::hang) {
 		handleHangingMode();
@@ -91,772 +85,24 @@ void AvatarMoverProcess::run() {
 		handleNormalMode();
 }
 
-void AvatarMoverProcess::handleHangingMode() {
-	bool stasis = Ultima8Engine::get_instance()->isAvatarInStasis();
-
-	_idleTime = 0;
-
-	if (stasis)
-		return;
-
-	bool m0clicked = false;
-	//bool m1clicked = false;
-	if (!_mouseButton[0].isState(MBS_HANDLED) &&
-		!_mouseButton[0].curWithinDblClkTimeout()) {
-		m0clicked = true;
-		_mouseButton[0].setState(MBS_HANDLED);
-	}
-	if (!_mouseButton[1].isState(MBS_HANDLED) &&
-	    !_mouseButton[1].curWithinDblClkTimeout()) {
-		//m1clicked = true;
-		_mouseButton[1].setState(MBS_HANDLED);
-	}
-
-	// if left mouse is down, try to climb up
-
-	if (_mouseButton[0].isState(MBS_DOWN) &&
-	        (!_mouseButton[0].isState(MBS_HANDLED) || m0clicked)) {
-		_mouseButton[0].setState(MBS_HANDLED);
-		_mouseButton[0]._lastDown = 0;
-		MainActor *avatar = getMainActor();
-
-		if (avatar->tryAnim(Animation::climb40, dir_current) == Animation::SUCCESS) {
-			avatar->ensureGravityProcess()->terminate();
-			waitFor(avatar->doAnim(Animation::climb40, dir_current));
-		}
-	}
-}
-
-void AvatarMoverProcess::handleCombatMode() {
-	Mouse *mouse = Mouse::get_instance();
-	MainActor *avatar = getMainActor();
-	Animation::Sequence lastanim = avatar->getLastAnim();
-	Direction direction = avatar->getDir();
-	bool stasis = Ultima8Engine::get_instance()->isAvatarInStasis();
-
-	int32 mx, my;
-	mouse->getMouseCoords(mx, my);
-	unsigned int mouselength = mouse->getMouseLength(mx, my);
-
-	Direction mousedir = mouse->getMouseDirectionWorld(mx, my);
-
-	// never idle when in combat
-	_idleTime = 0;
-
-	// If Avatar has fallen down, stand up.
-	if (lastanim == Animation::die || lastanim == Animation::fallBackwards) {
-		if (!stasis)
-			waitFor(avatar->doAnim(Animation::standUp, mousedir));
-		return;
-	}
-
-	// if we were blocking, and no longer holding the mouse, stop
-	if (lastanim == Animation::startBlock &&
-	        !_mouseButton[0].isState(MBS_DOWN)) {
-		waitFor(avatar->doAnim(Animation::stopBlock, direction));
-		return;
-	}
-
-	// can't do any new actions if in stasis
-	if (stasis)
-		return;
-
-	bool m0clicked = false;
-	bool m1clicked = false;
-
-	if (!_mouseButton[0].isState(MBS_HANDLED) &&
-	    !_mouseButton[0].curWithinDblClkTimeout()) {
-		m0clicked = true;
-		_mouseButton[0].setState(MBS_HANDLED);
-	}
-
-	if (!_mouseButton[1].isState(MBS_HANDLED) &&
-	    !_mouseButton[1].curWithinDblClkTimeout()) {
-		m1clicked = true;
-		_mouseButton[1].setState(MBS_HANDLED);
-	}
-
-	if (!_mouseButton[0].isState(MBS_DOWN)) {
-		clearMovementFlag(MOVE_MOUSE_DIRECTION);
-	}
-
-	if (_mouseButton[0].isState(MBS_DOWN) &&
-	        _mouseButton[0].isState(MBS_HANDLED) && _mouseButton[0]._lastDown > 0) {
-		// left click-and-hold = block
-		if (lastanim == Animation::startBlock)
-			return;
-
-//		pout << "AvatarMover: combat block" << Std::endl;
-
-		if (checkTurn(mousedir, false))
-			return;
-
-		waitFor(avatar->doAnim(Animation::startBlock, mousedir));
-		return;
-	}
-
-	if (_mouseButton[0].isUnhandledDoubleClick()) {
-		_mouseButton[0].setState(MBS_HANDLED);
-		_mouseButton[0]._lastDown = 0;
-
-		if (canAttack()) {
-			// double left click = attack
-//			pout << "AvatarMover: combat attack" << Std::endl;
-
-			if (checkTurn(mousedir, true))
-				return;
-
-			waitFor(avatar->doAnim(Animation::attack, mousedir));
-			_lastAttack = _lastFrame;
-
-			// attacking gives str/dex
-			avatar->accumulateStr(1 + (getRandom() % 2));
-			avatar->accumulateDex(2 + (getRandom() % 2));
-		}
-
-		return;
-	}
-
-	if (_mouseButton[1].isUnhandledDoubleClick()) {
-		_mouseButton[1].setState(MBS_HANDLED);
-		_mouseButton[1]._lastDown = 0;
-
-		Gump *desktopgump = Ultima8Engine::get_instance()->getDesktopGump();
-		if (desktopgump->TraceObjId(mx, my) == 1) {
-			// double right click on avatar = toggle combat mode
-			avatar->toggleInCombat();
-			waitFor(avatar->doAnim(Animation::unreadyWeapon, direction));
-			return;
-		}
-
-		if (canAttack()) {
-			// double right click = kick
-//			pout << "AvatarMover: combat kick" << Std::endl;
-
-			if (checkTurn(mousedir, false))
-				return;
-
-			waitFor(avatar->doAnim(Animation::kick, mousedir));
-			_lastAttack = _lastFrame;
-
-			// kicking gives str/dex
-			avatar->accumulateStr(1 + (getRandom() % 2));
-			avatar->accumulateDex(2 + (getRandom() % 2));
-		}
-
-		return;
-	}
-
-	if (_mouseButton[1].isState(MBS_DOWN) && _mouseButton[1].isState(MBS_HANDLED)) {
-		// Note: Orginal game allowed a move animation on a single right click.
-		// This implementation needs right mouse to be held. 
-		setMovementFlag(MOVE_MOUSE_DIRECTION);
-
-		if (checkTurn(mousedir, true))
-			return;
-
-		//!! TODO: check if you can actually take this step
-		Direction nextdir = mousedir;
-		Animation::Sequence nextanim;
-
-		if (lastanim == Animation::run) {
-			// want to run while in combat mode?
-			// first sheath weapon
-			nextanim = Animation::readyWeapon;
-		} else if (Direction_Invert(direction) == mousedir) {
-			nextanim = Animation::retreat;
-			nextdir = direction;
-		} else {
-			nextanim = Animation::advance;
-		}
-
-		if (mouselength == 2) {
-			// Take a step before running
-			nextanim = Animation::walk;
-			avatar->setActorFlag(Actor::ACT_COMBATRUN);
-			avatar->toggleInCombat();
-			MusicProcess::get_instance()->playCombatMusic(110); // CONSTANT!!
-		}
-
-		nextanim = Animation::checkWeapon(nextanim, lastanim);
-		waitFor(avatar->doAnim(nextanim, nextdir));
-		return;
-	}
-
-	// if clicked, turn in mouse direction
-	if (m0clicked || m1clicked)
-		if (checkTurn(mousedir, false))
-			return;
-
-	bool moving = (lastanim == Animation::advance || lastanim == Animation::retreat);
-
-	DirectionMode dirmode = avatar->animDirMode(Animation::combatStand);
-
-	//  if we are trying to move, allow change direction only after move occurs to avoid spinning
-	if (moving || !hasMovementFlags(MOVE_FORWARD | MOVE_BACK)) {
-		if (hasMovementFlags(MOVE_TURN_LEFT)) {
-			direction = Direction_OneLeft(direction, dirmode);
-		}
-
-		if (hasMovementFlags(MOVE_TURN_RIGHT)) {
-			direction = Direction_OneRight(direction, dirmode);
-		}
-	}
-
-	if (hasMovementFlags(MOVE_FORWARD)) {
-		Animation::Sequence nextanim = Animation::advance;
-
-		if (lastanim == Animation::run) {
-			// want to run while in combat mode?
-			// first sheath weapon
-			nextanim = Animation::readyWeapon;
-		} 
-
-		if (hasMovementFlags(MOVE_RUN)) {
-			// Take a step before running
-			nextanim = Animation::walk;
-			avatar->setActorFlag(Actor::ACT_COMBATRUN);
-			avatar->toggleInCombat();
-			MusicProcess::get_instance()->playCombatMusic(110); // CONSTANT!!
-		}
-
-		nextanim = Animation::checkWeapon(nextanim, lastanim);
-		waitFor(avatar->doAnim(nextanim, direction));
-		return;
-	}
-
-	if (hasMovementFlags(MOVE_BACK)) {
-		waitFor(avatar->doAnim(Animation::retreat, direction));
-		return;
-	}
-
-	int y = 0;
-	int x = 0;
-	if (hasMovementFlags(MOVE_UP)) {
-		y++;
-	}
-	if (hasMovementFlags(MOVE_DOWN)) {
-		y--;
-	}
-	if (hasMovementFlags(MOVE_LEFT)) {
-		x--;
-	}
-	if (hasMovementFlags(MOVE_RIGHT)) {
-		x++;
-	}
-
-	if (x != 0 || y != 0) {
-		Direction nextdir = Direction_Get(y, x, dirmode_8dirs);
-
-		if (checkTurn(nextdir, true))
-			return;
-
-		Animation::Sequence nextanim;
-		if (lastanim == Animation::run) {
-			// want to run while in combat mode?
-			// first sheath weapon
-			nextanim = Animation::readyWeapon;
-		} else if (Direction_Invert(direction) == nextdir) {
-			nextanim = Animation::retreat;
-			nextdir = direction;
-		} else {
-			nextanim = Animation::advance;
-		}
-
-		if (hasMovementFlags(MOVE_RUN)) {
-			// Take a step before running
-			nextanim = Animation::walk;
-			avatar->setActorFlag(Actor::ACT_COMBATRUN);
-			avatar->toggleInCombat();
-			MusicProcess::get_instance()->playCombatMusic(110); // CONSTANT!!
-		}
-
-		nextanim = Animation::checkWeapon(nextanim, lastanim);
-		waitFor(avatar->doAnim(nextanim, nextdir));
-		return;
-	}
-
-	if (checkTurn(direction, false))
-		return;
 
-	// not doing anything in particular? stand
-	// TODO: make sure falling works properly.
-	if (lastanim != Animation::combatStand) {
-		Animation::Sequence nextanim = Animation::combatStand;
-		nextanim = Animation::checkWeapon(nextanim, lastanim);
-		waitFor(avatar->doAnim(nextanim, direction));
-	}
-}
-
-void AvatarMoverProcess::handleNormalMode() {
-	Ultima8Engine *guiapp = Ultima8Engine::get_instance();
-	Mouse *mouse = Mouse::get_instance();
+void AvatarMoverProcess::tryAttack() {
 	MainActor *avatar = getMainActor();
-	Animation::Sequence lastanim = avatar->getLastAnim();
-	Direction direction = avatar->getDir();
-	bool stasis = guiapp->isAvatarInStasis();
-	bool combatRun = avatar->hasActorFlags(Actor::ACT_COMBATRUN);
-
-	int32 mx, my;
-	mouse->getMouseCoords(mx, my);
-	unsigned int mouselength = mouse->getMouseLength(mx, my);
-	Direction mousedir = mouse->getMouseDirectionWorld(mx, my);
-
-	// Store current idle time. (Also see end of function.)
-	uint32 currentIdleTime = _idleTime;
-	_idleTime = 0;
-
-	// User toggled combat while in combatRun
-	if (avatar->isInCombat()) {
-		avatar->clearActorFlag(Actor::ACT_COMBATRUN);
-		avatar->toggleInCombat();
-	}
-
-	// If Avatar has fallen down, stand up.
-	if (lastanim == Animation::die || lastanim == Animation::fallBackwards) {
-		if (!stasis) {
-			waitFor(avatar->doAnim(Animation::standUp, direction));
-		}
-		return;
-	}
-
-	// If still in combat stance, sheathe weapon
-	if (!stasis && Animation::isCombatAnim(lastanim)) {
-		ProcId anim1 = avatar->doAnim(Animation::unreadyWeapon, direction);
-		ProcId anim2 = avatar->doAnim(Animation::stand, direction);
-		Process *anim2p = Kernel::get_instance()->getProcess(anim2);
-		anim2p->waitFor(anim1);
-		waitFor(anim2);
-
-		return;
-	}
-
-	bool m0clicked = false;
-	bool m1clicked = false;
-
-	// check mouse state to see what needs to be done
-	if (!_mouseButton[0].isState(MBS_HANDLED) &&
-		!_mouseButton[0].curWithinDblClkTimeout()) {
-		m0clicked = true;
-		_mouseButton[0].setState(MBS_HANDLED);
-	}
-
-	if (!_mouseButton[1].isState(MBS_HANDLED) &&
-	    !_mouseButton[1].curWithinDblClkTimeout()) {
-		m1clicked = true;
-		_mouseButton[1].setState(MBS_HANDLED);
-	}
-
-	if (!_mouseButton[1].isState(MBS_DOWN)) {
-		clearMovementFlag(MOVE_MOUSE_DIRECTION);
-	}
-
-	if (_mouseButton[1].isState(MBS_DOWN) && _mouseButton[1].isState(MBS_HANDLED)) {
-		// Note: Orginal game allowed a move animation on a single right click.
-		// This implementation needs right mouse to be held. 
-		setMovementFlag(MOVE_MOUSE_DIRECTION);
-	}
-
-	if (!hasMovementFlags(MOVE_ANY_DIRECTION)) {
-		// if we were running in combat mode, slow to a walk, draw weapon
-		// (even in stasis)
-		if (combatRun) {
-			avatar = getMainActor();
-			avatar->clearActorFlag(Actor::ACT_COMBATRUN);
-			avatar->toggleInCombat();
-
-			// If we were running, slow to a walk before drawing weapon.
-			// Note: Original game did not check last animation and always took an extra walk.
-			if (lastanim == Animation::run || lastanim == Animation::runningJump) {
-				ProcId walkpid = avatar->doAnim(Animation::walk, direction);
-				ProcId drawpid = avatar->doAnim(Animation::readyWeapon, direction);
-				Process *drawproc = Kernel::get_instance()->getProcess(drawpid);
-				drawproc->waitFor(walkpid);
-				waitFor(drawpid);
-				return;
-			}
-
-			waitFor(avatar->doAnim(Animation::readyWeapon, direction));
-			return;
-		}
-
-		// if we were running, slow to a walk before stopping
-		// (even in stasis)
-		if (lastanim == Animation::run) {
-			ProcId walkpid = avatar->doAnim(Animation::walk, direction);
-			ProcId standpid = avatar->doAnim(Animation::stand, direction);
-			Process *standproc = Kernel::get_instance()->getProcess(standpid);
-			standproc->waitFor(walkpid);
-			waitFor(standpid);
-			return;
-		}
-
-		// TODO: if we were hanging, fall
-	}
-
-	// can't do any new actions if in stasis
-	if (stasis)
-		return;
-
-	// both mouse buttons down and not yet handled, check for jump.
-	if (!_mouseButton[0].isState(MBS_HANDLED) && !_mouseButton[1].isState(MBS_HANDLED)) {
-		// Take action if both were clicked within
-		// double-click timeout of each other.
-		// notice these are all unsigned.
-		uint32 down = _mouseButton[1]._curDown;
-		if (_mouseButton[0]._curDown < down) {
-			down = down - _mouseButton[0]._curDown;
-		} else {
-			down = _mouseButton[0]._curDown - down;
-		}
-
-		if (down < DOUBLE_CLICK_TIMEOUT) {
-			// Both buttons pressed within the timeout
-			_mouseButton[0].setState(MBS_HANDLED);
-			_mouseButton[1].setState(MBS_HANDLED);
-			setMovementFlag(MOVE_JUMP);
-		}
-	}
-
-	if ((!_mouseButton[0].isState(MBS_HANDLED) || m0clicked) && hasMovementFlags(MOVE_ANY_DIRECTION | MOVE_STEP)) {
-		_mouseButton[0].setState(MBS_HANDLED);
-		// We got a left mouse down while already moving in any direction or holding the step button.
-		// CHECKME: check what needs to happen when keeping left pressed
-		setMovementFlag(MOVE_JUMP);
-	}
-
-	if (_mouseButton[1].isUnhandledDoubleClick()) {
-		Gump *desktopgump = Ultima8Engine::get_instance()->getDesktopGump();
-		if (desktopgump->TraceObjId(mx, my) == 1) {
-			// double right click on avatar = toggle combat mode
-			_mouseButton[1].setState(MBS_HANDLED);
-			_mouseButton[1]._lastDown = 0;
-
-			avatar->toggleInCombat();
-			waitFor(avatar->doAnim(Animation::readyWeapon, direction));
-			return;
-		}
-	}
-
-	if (hasMovementFlags(MOVE_JUMP) && hasMovementFlags(MOVE_ANY_DIRECTION)) {
-		clearMovementFlag(MOVE_JUMP);
-
-		if (hasMovementFlags(MOVE_MOUSE_DIRECTION)) {
-			if (checkTurn(mousedir, false))
-				return;
-		}
-
-		Animation::Sequence nextanim = Animation::jump;
-		// check if we need to do a running jump
-		if (lastanim == Animation::run || lastanim == Animation::runningJump) {
-			nextanim = Animation::runningJump;
-		}
-		else if (avatar->hasActorFlags(Actor::ACT_AIRWALK)) {
-			nextanim = Animation::airwalkJump;
-		}
-		else if ((hasMovementFlags(MOVE_MOUSE_DIRECTION) && mouselength == 0) || hasMovementFlags(MOVE_STEP)) {
-			nextanim = Animation::jumpUp;
-		}
-
-		nextanim = Animation::checkWeapon(nextanim, lastanim);
-		waitFor(avatar->doAnim(nextanim, direction));
-		return;
-	}
-
-	if (hasMovementFlags(MOVE_JUMP)) {
-		clearMovementFlag(MOVE_JUMP);
-
-		if (checkTurn(mousedir, false))
-			return;
-
-		Animation::Sequence nextanim = Animation::jumpUp;
-		if (mouselength > 0) {
-			nextanim = Animation::jump;
-		}
-
-		// check if there's something we can climb up onto here
-		Animation::Sequence climbanim = Animation::climb72;
-		while (climbanim >= Animation::climb16) {
-			if (avatar->tryAnim(climbanim, direction) ==
-				Animation::SUCCESS) {
-				nextanim = climbanim;
-			}
-			climbanim = static_cast<Animation::Sequence>(climbanim - 1);
-		}
-
-		if (nextanim == Animation::jump) {
-			jump(Animation::jump, direction);
-		}
-		else {
-			if (nextanim != Animation::jumpUp) {
-				// climbing gives str/dex
-				avatar->accumulateStr(2 + nextanim - Animation::climb16);
-				avatar->accumulateDex(2 * (2 + nextanim - Animation::climb16));
-			}
-			nextanim = Animation::checkWeapon(nextanim, lastanim);
-			waitFor(avatar->doAnim(nextanim, direction));
-		}
-		return;
-	}
-
-	if (hasMovementFlags(MOVE_MOUSE_DIRECTION)) {
-		Animation::Sequence nextanim = Animation::step;
-
-		if (mouselength == 1) {
-			nextanim = Animation::walk;
-		} else if (mouselength == 2) {
-			if (lastanim == Animation::run
-			        || lastanim == Animation::runningJump
-			        || lastanim == Animation::walk)
-				nextanim = Animation::run;
-			else
-				nextanim = Animation::walk;
-		}
-
-		step(nextanim, mousedir);
-		return;
-	}
-
-	if (m1clicked)
-		if (checkTurn(mousedir, false))
-			return;
-
-	bool moving = (lastanim == Animation::step || lastanim == Animation::run || lastanim == Animation::walk);
-
-	DirectionMode dirmode = avatar->animDirMode(Animation::step);
-
-	//  if we are trying to move, allow change direction only after move occurs to avoid spinning
-	if (moving || !hasMovementFlags(MOVE_FORWARD | MOVE_BACK)) {
-		if (hasMovementFlags(MOVE_TURN_LEFT)) {
-			direction = Direction_OneLeft(direction, dirmode);
-		}
-
-		if (hasMovementFlags(MOVE_TURN_RIGHT)) {
-			direction = Direction_OneRight(direction, dirmode);
-		}
-	}
-
-	Animation::Sequence nextanim = Animation::walk;
-
-	if (hasMovementFlags(MOVE_STEP)) {
-		nextanim = Animation::step;
-	} else if (hasMovementFlags(MOVE_RUN)) {
-		if (lastanim == Animation::run
-			    || lastanim == Animation::runningJump
-			    || lastanim == Animation::walk)
-			nextanim = Animation::run;
-		else
-			nextanim = Animation::walk;
-	}
-
-	if (hasMovementFlags(MOVE_FORWARD)) {
-		step(nextanim, direction);
-		return;
-	}
-
-	if (hasMovementFlags(MOVE_BACK)) {
-		step(nextanim, Direction_Invert(direction));
-
-		// flip to move forward once turned
-		setMovementFlag(MOVE_FORWARD);
-		return;
-	}
-
-	int y = 0;
-	int x = 0;
-	if (hasMovementFlags(MOVE_UP)) {
-		y++;
-	}
-	if (hasMovementFlags(MOVE_DOWN)) {
-		y--;
-	}
-	if (hasMovementFlags(MOVE_LEFT)) {
-		x--;
-	}
-	if (hasMovementFlags(MOVE_RIGHT)) {
-		x++;
-	}
-
-	if (x != 0 || y != 0) {
-		direction = Direction_Get(y, x, dirmode_8dirs);
-		step(nextanim, direction);
-		return;
-	}
-
-	if (checkTurn(direction, moving))
-		return;
-
-	// doing another animation?
-	if (Kernel::get_instance()->getNumProcesses(1, ActorAnimProcess::ACTOR_ANIM_PROC_TYPE))
-		return;
-
-	// if we were running, slow to a walk before stopping
-	if (lastanim == Animation::run) {
-		waitFor(avatar->doAnim(Animation::walk, direction));
-		return;
-	}
-
-	// not doing anything in particular? stand
-	if (lastanim != Animation::stand && currentIdleTime == 0) {
-		waitFor(avatar->doAnim(Animation::stand, direction));
-		return;
-	}
-
-	// idle
-	_idleTime = currentIdleTime + 1;
-
-	// currently shaking head?
-	if (lastanim == Animation::lookLeft || lastanim == Animation::lookRight) {
-		if ((getRandom() % 1500) + 30 < _idleTime) {
-			_lastHeadShakeAnim = lastanim;
-			waitFor(avatar->doAnim(Animation::stand, direction));
-			_idleTime = 1;
-			return;
-		}
+	Direction dir = avatar->getDir();
+	if (!avatar->isInCombat()) {
+		avatar->setInCombat(0);
+		waitFor(avatar->doAnim(Animation::readyWeapon, dir));
 	} else {
-		if ((getRandom() % 3000) + 150 < _idleTime) {
-			if (getRandom() % 5 == 0)
-				nextanim = _lastHeadShakeAnim;
-			else if (_lastHeadShakeAnim == Animation::lookLeft)
-				nextanim = Animation::lookRight;
-			else
-				nextanim = Animation::lookLeft;
-			waitFor(avatar->doAnim(nextanim, direction));
-			_idleTime = 1;
-			return;
-		}
-	}
-}
-
-void AvatarMoverProcess::step(Animation::Sequence action, Direction direction,
-                              bool adjusted) {
-	assert(action == Animation::step || action == Animation::walk ||
-	       action == Animation::run);
-
-	MainActor *avatar = getMainActor();
-	Animation::Sequence lastanim = avatar->getLastAnim();
-
-	Animation::Result res = avatar->tryAnim(action, direction);
-
-	Direction stepdir = direction;
-
-	if (res == Animation::FAILURE ||
-	        (action == Animation::step && res == Animation::END_OFF_LAND)) {
-		debug(6, "Step: end off land dir %d, try other dir", stepdir);
-		Direction altdir1 = Direction_OneRight(stepdir, dirmode_8dirs);
-		Direction altdir2 = Direction_OneLeft(stepdir, dirmode_8dirs);
-
-		res = avatar->tryAnim(action, altdir1);
-		if (res == Animation::FAILURE ||
-		        (action == Animation::step && res == Animation::END_OFF_LAND)) {
-			debug(6, "Step: end off land dir %d, altdir1 %d failed, try altdir2 %d", stepdir, altdir1, altdir2);
-			res = avatar->tryAnim(action, altdir2);
-			if (res == Animation::FAILURE ||
-			        (action == Animation::step && res == Animation::END_OFF_LAND)) {
-				// Can't walk in this direction.
-				// Try to take a smaller step
-
-				if (action == Animation::walk) {
-					debug(6, "Step: end off land both altdirs failed, smaller step (step)");
-					step(Animation::step, direction, true);
-					return;
-				} else if (action == Animation::run) {
-					debug(6, "Step: end off land both altdirs failed, smaller step (walk)");
-					step(Animation::walk, direction, true);
-					return;
-				}
-
-			} else {
-				stepdir = altdir2;
+		if (canAttack()) {
+			waitFor(avatar->doAnim(Animation::attack, dir));
+			if (GAME_IS_CRUSADER) {
+				// FIXME: put some real values in here.
+				int32 xs, ys, zs;
+				avatar->getFootpadWorld(xs, ys, zs);
+				avatar->fireWeapon(xs / 2, ys / 2, zs / 2, dir, 1, 1);
 			}
-		} else {
-			stepdir = altdir1;
 		}
-
-
 	}
-
-	if (action == Animation::step && res == Animation::END_OFF_LAND &&
-	        lastanim != Animation::keepBalance && !adjusted) {
-		if (checkTurn(stepdir, false))
-			return;
-		debug(6, "Step: end off land both altdirs failed, keep balance.");
-		waitFor(avatar->doAnim(Animation::keepBalance, stepdir));
-		return;
-	}
-
-	if (action == Animation::step && res == Animation::FAILURE) {
-		action = Animation::stand;
-	}
-
-
-	bool moving = (action == Animation::run || action == Animation::walk);
-
-	if (checkTurn(stepdir, moving))
-		return;
-
-	debug(6, "Step: step ok: action %d dir %d", action, stepdir);
-	action = Animation::checkWeapon(action, lastanim);
-	waitFor(avatar->doAnim(action, stepdir));
-}
-
-void AvatarMoverProcess::jump(Animation::Sequence action, Direction direction) {
-	MainActor *avatar = getMainActor();
-
-	// running jump
-	if (action == Animation::runningJump) {
-		waitFor(avatar->doAnim(action, direction));
-		return;
-	}
-
-	// airwalk
-	if (avatar->hasActorFlags(Actor::ACT_AIRWALK) &&
-	        action == Animation::jump) {
-		waitFor(avatar->doAnim(Animation::airwalkJump, direction));
-		return;
-	}
-
-	bool targeting;
-	SettingManager::get_instance()->get("targetedjump", targeting);
-
-	if (targeting) {
-		Mouse *mouse = Mouse::get_instance();
-		int32 coords[3];
-		int32 mx, my;
-		mouse->getMouseCoords(mx, my);
-		GameMapGump *gameMap = Ultima8Engine::get_instance()->getGameMapGump();
-		// We need the Gump's x/y for TraceCoordinates
-		gameMap->ScreenSpaceToGump(mx, my);
-		ObjId targetId = gameMap->TraceCoordinates(mx, my, coords);
-		Item *target = getItem(targetId);
-
-		int32 ax, ay, az;
-		avatar->getCentre(ax, ay, az);
-
-		int32 xrange = abs(ax - coords[0]);
-		int32 yrange = abs(ay - coords[1]);
-		int maxrange = avatar->getStr() * 32;
-
-		if (target && target->getShapeInfo()->is_land() &&
-		        xrange < maxrange && yrange < maxrange) {
-			// Original also only lets you jump at the Z_FACE
-			Process *p = new TargetedAnimProcess(avatar, Animation::jumpUp,
-			                                     direction, coords);
-			waitFor(Kernel::get_instance()->addProcess(p));
-			return;
-		}
-		// invalid target or out of range
-		waitFor(avatar->doAnim(Animation::shakeHead, direction));
-	} else {
-		waitFor(avatar->doAnim(Animation::jump, direction));
-	}
-}
-
-void AvatarMoverProcess::turnToDirection(Direction direction) {
-	MainActor *avatar = getMainActor();
-	uint16 turnpid = avatar->turnTowardDir(direction);
-	if (turnpid)
-		waitFor(turnpid);
 }
 
 bool AvatarMoverProcess::checkTurn(Direction direction, bool moving) {
@@ -870,9 +116,9 @@ bool AvatarMoverProcess::checkTurn(Direction direction, bool moving) {
 		Animation::Sequence lastanim = avatar->getLastAnim();
 
 		if (moving &&
-		        (lastanim == Animation::walk || lastanim == Animation::run ||
-		         lastanim == Animation::combatStand) &&
-		        (ABS(direction - curdir) + 2) % 16 <= 4) {
+				(lastanim == Animation::walk || lastanim == Animation::run ||
+				 lastanim == Animation::combatStand) &&
+				(ABS(direction - curdir) + 2) % 16 <= 4) {
 			// don't need to explicitly do a turn animation
 			return false;
 		}
@@ -890,30 +136,11 @@ bool AvatarMoverProcess::checkTurn(Direction direction, bool moving) {
 	return false;
 }
 
-bool AvatarMoverProcess::canAttack() {
-	MainActor *avatar = getMainActor();
-	if (GAME_IS_CRUSADER)
-		return avatar->isInCombat();
-	return (_lastFrame > _lastAttack + (25 - avatar->getDex()));
-}
-
-void AvatarMoverProcess::tryAttack() {
+void AvatarMoverProcess::turnToDirection(Direction direction) {
 	MainActor *avatar = getMainActor();
-	Direction dir = avatar->getDir();
-	if (!avatar->isInCombat()) {
-		avatar->setInCombat(0);
-		waitFor(avatar->doAnim(Animation::readyWeapon, dir));
-	} else {
-		if (canAttack()) {
-			waitFor(avatar->doAnim(Animation::attack, dir));
-			if (GAME_IS_CRUSADER) {
-				// FIXME: put some real values in here.
-				int32 xs, ys, zs;
-				avatar->getFootpadWorld(xs, ys, zs);
-				avatar->fireWeapon(xs / 2, ys / 2, zs / 2, dir, 1, 1);
-			}
-		}
-	}
+	uint16 turnpid = avatar->turnTowardDir(direction);
+	if (turnpid)
+		waitFor(turnpid);
 }
 
 void AvatarMoverProcess::onMouseDown(int button, int32 mx, int32 my) {
@@ -959,7 +186,6 @@ void AvatarMoverProcess::saveData(Common::WriteStream *ws) {
 
 	ws->writeUint32LE(_lastAttack);
 	ws->writeUint32LE(_idleTime);
-	ws->writeUint16LE(static_cast<uint8>(_lastHeadShakeAnim));
 }
 
 bool AvatarMoverProcess::loadData(Common::ReadStream *rs, uint32 version) {
@@ -967,7 +193,6 @@ bool AvatarMoverProcess::loadData(Common::ReadStream *rs, uint32 version) {
 
 	_lastAttack = rs->readUint32LE();
 	_idleTime = rs->readUint32LE();
-	_lastHeadShakeAnim = static_cast<Animation::Sequence>(rs->readUint16LE());
 
 	return true;
 }
diff --git a/engines/ultima/ultima8/world/actors/avatar_mover_process.h b/engines/ultima/ultima8/world/actors/avatar_mover_process.h
index 494d0a8669..1d4dd019d2 100644
--- a/engines/ultima/ultima8/world/actors/avatar_mover_process.h
+++ b/engines/ultima/ultima8/world/actors/avatar_mover_process.h
@@ -30,25 +30,23 @@
 namespace Ultima {
 namespace Ultima8 {
 
+/**
+ * Base class for mover processes that decide which animation to
+ * do next based on last anim and keyboard / mouse / etc.
+ */
 class AvatarMoverProcess : public Process {
 public:
 	AvatarMoverProcess();
 	~AvatarMoverProcess() override;
 
-	// p_dynamic_cast stuff
-	ENABLE_RUNTIME_CLASSTYPE()
-
 	void run() override;
 
-	void onMouseDown(int button, int32 mx, int32 my);
-	void onMouseUp(int button);
-
 	void resetIdleTime() {
 		_idleTime = 0;
 	}
 
 	bool loadData(Common::ReadStream *rs, uint32 version);
-	void saveData(Common::WriteStream *ws) override;
+	virtual void saveData(Common::WriteStream *ws) override;
 
 	bool hasMovementFlags(uint32 flags) const {
 		return (_movementFlags & flags) != 0;
@@ -60,7 +58,10 @@ public:
 		_movementFlags &= ~mask;
 	}
 
-	void tryAttack();
+	void onMouseDown(int button, int32 mx, int32 my);
+	void onMouseUp(int button);
+
+	virtual void tryAttack() = 0;
 
 	enum MovementFlags {
 		MOVE_MOUSE_DIRECTION = 0x001,
@@ -83,16 +84,15 @@ public:
 		MOVE_ANY_DIRECTION = MOVE_MOUSE_DIRECTION | MOVE_FORWARD | MOVE_BACK | MOVE_LEFT | MOVE_RIGHT | MOVE_UP | MOVE_DOWN
 	};
 
-private:
-	void handleHangingMode();
-	void handleCombatMode();
-	void handleNormalMode();
+protected:
+	virtual void handleHangingMode() = 0;
+	virtual void handleCombatMode() = 0;
+	virtual void handleNormalMode() = 0;
+
+	virtual bool canAttack() = 0;
 
-	void step(Animation::Sequence action, Direction direction, bool adjusted = false);
-	void jump(Animation::Sequence action, Direction direction);
 	void turnToDirection(Direction direction);
 	bool checkTurn(Direction direction, bool moving);
-	bool canAttack();
 
 	uint32 _lastFrame;
 
@@ -101,8 +101,7 @@ private:
 
 	// shake head when idle
 	uint32 _idleTime;
-	Animation::Sequence _lastHeadShakeAnim;
-	
+
 	MButton _mouseButton[2];
 
 	uint32 _movementFlags;
diff --git a/engines/ultima/ultima8/world/actors/cru_avatar_mover_process.cpp b/engines/ultima/ultima8/world/actors/cru_avatar_mover_process.cpp
new file mode 100644
index 0000000000..82b0b19c19
--- /dev/null
+++ b/engines/ultima/ultima8/world/actors/cru_avatar_mover_process.cpp
@@ -0,0 +1,450 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "ultima/ultima8/misc/pent_include.h"
+#include "ultima/ultima8/world/actors/cru_avatar_mover_process.h"
+#include "ultima/ultima8/world/actors/animation.h"
+#include "ultima/ultima8/ultima8.h"
+#include "ultima/ultima8/world/actors/main_actor.h"
+#include "ultima/ultima8/gumps/game_map_gump.h"
+#include "ultima/ultima8/kernel/kernel.h"
+#include "ultima/ultima8/world/actors/actor_anim_process.h"
+#include "ultima/ultima8/world/actors/targeted_anim_process.h"
+#include "ultima/ultima8/world/get_object.h"
+#include "ultima/ultima8/world/current_map.h"
+#include "ultima/ultima8/world/world.h"
+#include "ultima/ultima8/misc/direction.h"
+#include "ultima/ultima8/misc/direction_util.h"
+
+namespace Ultima {
+namespace Ultima8 {
+
+// p_dynamic_cast stuff
+DEFINE_RUNTIME_CLASSTYPE_CODE(CruAvatarMoverProcess)
+
+CruAvatarMoverProcess::CruAvatarMoverProcess() : AvatarMoverProcess() {
+}
+
+
+CruAvatarMoverProcess::~CruAvatarMoverProcess() {
+}
+
+
+void CruAvatarMoverProcess::handleHangingMode() {
+	// No hanging in crusader, this shouldn't happen?
+	assert(false);
+}
+
+void CruAvatarMoverProcess::handleCombatMode() {
+	MainActor *avatar = getMainActor();
+	Animation::Sequence lastanim = avatar->getLastAnim();
+	Direction direction = avatar->getDir();
+	bool stasis = Ultima8Engine::get_instance()->isAvatarInStasis();
+
+	// never idle when in combat
+	_idleTime = 0;
+
+	// If Avatar has fallen down, stand up
+	if (lastanim == Animation::die || lastanim == Animation::fallBackwards) {
+		if (!stasis) {
+			waitFor(avatar->doAnim(Animation::standUp, direction));
+		}
+		return;
+	}
+
+	// can't do any new actions if in stasis
+	if (stasis)
+		return;
+
+	bool moving = (lastanim == Animation::advance || lastanim == Animation::retreat);
+
+	DirectionMode dirmode = avatar->animDirMode(Animation::combatStand);
+
+	//  if we are trying to move, allow change direction only after move occurs to avoid spinning
+	if (moving || !hasMovementFlags(MOVE_FORWARD | MOVE_BACK)) {
+		if (hasMovementFlags(MOVE_TURN_LEFT)) {
+			direction = Direction_OneLeft(direction, dirmode);
+		}
+
+		if (hasMovementFlags(MOVE_TURN_RIGHT)) {
+			direction = Direction_OneRight(direction, dirmode);
+		}
+	}
+
+	if (hasMovementFlags(MOVE_FORWARD)) {
+		Animation::Sequence nextanim;
+		if (hasMovementFlags(MOVE_STEP)) {
+			nextanim = Animation::advance;
+		} else if (hasMovementFlags(MOVE_RUN)) {
+			// Take a step before running
+			avatar->toggleInCombat();
+			if (lastanim != Animation::startRun)
+				nextanim = Animation::startRun;
+			else
+				nextanim = Animation::run;
+		} else {
+			// moving from combat stows weapon
+			nextanim = Animation::walk;
+			avatar->toggleInCombat();
+		}
+
+		nextanim = Animation::checkWeapon(nextanim, lastanim);
+		step(nextanim, direction);
+		return;
+	}
+
+	if (hasMovementFlags(MOVE_BACK)) {
+		waitFor(avatar->doAnim(Animation::retreat, direction));
+		return;
+	}
+
+	int y = 0;
+	int x = 0;
+	if (hasMovementFlags(MOVE_UP)) {
+		y++;
+	}
+	if (hasMovementFlags(MOVE_DOWN)) {
+		y--;
+	}
+	if (hasMovementFlags(MOVE_LEFT)) {
+		x--;
+	}
+	if (hasMovementFlags(MOVE_RIGHT)) {
+		x++;
+	}
+
+	if (x != 0 || y != 0) {
+		Direction nextdir = Direction_Get(y, x, dirmode_8dirs);
+
+		if (checkTurn(nextdir, true))
+			return;
+
+		Animation::Sequence nextanim;
+		if (lastanim == Animation::run) {
+			// want to run while in combat mode?
+			// first sheath weapon
+			nextanim = Animation::readyWeapon;
+		} else if (Direction_Invert(direction) == nextdir) {
+			nextanim = Animation::retreat;
+			nextdir = direction;
+		}
+
+		if (hasMovementFlags(MOVE_RUN)) {
+			// Take a step before running
+			nextanim = Animation::startRun;
+			avatar->toggleInCombat();
+		}
+
+		nextanim = Animation::checkWeapon(nextanim, lastanim);
+		step(nextanim, nextdir);
+		return;
+	}
+
+	if (checkTurn(direction, false))
+		return;
+
+	// not doing anything in particular? stand
+	if (lastanim != Animation::combatStand) {
+		Animation::Sequence nextanim = Animation::combatStand;
+		nextanim = Animation::checkWeapon(nextanim, lastanim);
+		waitFor(avatar->doAnim(nextanim, direction));
+	}
+}
+
+void CruAvatarMoverProcess::handleNormalMode() {
+	Ultima8Engine *guiapp = Ultima8Engine::get_instance();
+	MainActor *avatar = getMainActor();
+	Animation::Sequence lastanim = avatar->getLastAnim();
+	Direction direction = avatar->getDir();
+	bool stasis = guiapp->isAvatarInStasis();
+
+	// Store current idle time. (Also see end of function.)
+	uint32 currentIdleTime = _idleTime;
+	_idleTime = 0;
+
+	// User toggled combat while in combatRun
+	if (avatar->isInCombat()) {
+		avatar->clearActorFlag(Actor::ACT_COMBATRUN);
+		avatar->toggleInCombat();
+	}
+
+	// If Avatar has fallen down do nothing
+	if (lastanim == Animation::die || lastanim == Animation::fallBackwards) {
+		if (!stasis) {
+			waitFor(avatar->doAnim(Animation::standUp, direction));
+		}
+		return;
+	}
+
+	// If still in combat stance, sheathe weapon
+	if (!stasis && Animation::isCombatAnim(lastanim)) {
+		ProcId anim1 = avatar->doAnim(Animation::unreadyWeapon, direction);
+		ProcId anim2 = avatar->doAnim(Animation::stand, direction);
+		Process *anim2p = Kernel::get_instance()->getProcess(anim2);
+		anim2p->waitFor(anim1);
+		waitFor(anim2);
+
+		return;
+	}
+
+	if (!hasMovementFlags(MOVE_ANY_DIRECTION)) {
+		// if we were running, slow to a walk before stopping
+		// (even in stasis)
+		if (lastanim == Animation::run) {
+			ProcId walkpid = avatar->doAnim(Animation::walk, direction);
+			ProcId standpid = avatar->doAnim(Animation::stand, direction);
+			Process *standproc = Kernel::get_instance()->getProcess(standpid);
+			standproc->waitFor(walkpid);
+			waitFor(standpid);
+			return;
+		}
+	}
+
+	// can't do any new actions if in stasis
+	if (stasis)
+		return;
+
+	if (hasMovementFlags(MOVE_JUMP) && hasMovementFlags(MOVE_ANY_DIRECTION)) {
+		clearMovementFlag(MOVE_JUMP);
+
+		Animation::Sequence nextanim = Animation::jump;
+		// check if we need to do a running jump
+		if (lastanim == Animation::run || lastanim == Animation::runningJump) {
+			nextanim = Animation::runningJump;
+		}
+		else if (avatar->hasActorFlags(Actor::ACT_AIRWALK)) {
+			nextanim = Animation::airwalkJump;
+		}
+
+		nextanim = Animation::checkWeapon(nextanim, lastanim);
+		waitFor(avatar->doAnim(nextanim, direction));
+		return;
+	}
+
+	if (hasMovementFlags(MOVE_JUMP)) {
+		clearMovementFlag(MOVE_JUMP);
+
+		Animation::Sequence nextanim = Animation::jumpUp;
+
+		if (nextanim == Animation::jump) {
+			jump(Animation::jump, direction);
+		} else {
+			nextanim = Animation::checkWeapon(nextanim, lastanim);
+			waitFor(avatar->doAnim(nextanim, direction));
+		}
+		return;
+	}
+
+	bool moving = (lastanim == Animation::step || lastanim == Animation::run || lastanim == Animation::walk);
+
+	DirectionMode dirmode = avatar->animDirMode(Animation::step);
+
+	//  if we are trying to move, allow change direction only after move occurs to avoid spinning
+	if (moving || !hasMovementFlags(MOVE_FORWARD | MOVE_BACK)) {
+		if (hasMovementFlags(MOVE_TURN_LEFT)) {
+			direction = Direction_OneLeft(direction, dirmode);
+		}
+
+		if (hasMovementFlags(MOVE_TURN_RIGHT)) {
+			direction = Direction_OneRight(direction, dirmode);
+		}
+	}
+
+	Animation::Sequence nextanim = Animation::walk;
+
+	if (hasMovementFlags(MOVE_STEP)) {
+		nextanim = Animation::step;
+	} else if (hasMovementFlags(MOVE_RUN)) {
+		if (lastanim == Animation::run
+			    || lastanim == Animation::runningJump
+			    || lastanim == Animation::walk)
+			nextanim = Animation::run;
+		else
+			nextanim = Animation::walk;
+	}
+
+	if (hasMovementFlags(MOVE_FORWARD)) {
+		step(nextanim, direction);
+		return;
+	}
+
+	if (hasMovementFlags(MOVE_BACK)) {
+		step(nextanim, Direction_Invert(direction));
+
+		// flip to move forward once turned
+		setMovementFlag(MOVE_FORWARD);
+		return;
+	}
+
+	int y = 0;
+	int x = 0;
+	if (hasMovementFlags(MOVE_UP)) {
+		y++;
+	}
+	if (hasMovementFlags(MOVE_DOWN)) {
+		y--;
+	}
+	if (hasMovementFlags(MOVE_LEFT)) {
+		x--;
+	}
+	if (hasMovementFlags(MOVE_RIGHT)) {
+		x++;
+	}
+
+	if (x != 0 || y != 0) {
+		direction = Direction_Get(y, x, dirmode_8dirs);
+		step(nextanim, direction);
+		return;
+	}
+
+	if (checkTurn(direction, moving))
+		return;
+
+	// doing another animation?
+	if (Kernel::get_instance()->getNumProcesses(1, ActorAnimProcess::ACTOR_ANIM_PROC_TYPE))
+		return;
+
+	// if we were running, slow to a walk before stopping
+	if (lastanim == Animation::run) {
+		waitFor(avatar->doAnim(Animation::walk, direction));
+		return;
+	}
+
+	// not doing anything in particular? stand
+	if (lastanim != Animation::stand && currentIdleTime == 0) {
+		waitFor(avatar->doAnim(Animation::stand, direction));
+		return;
+	}
+
+	// idle
+	_idleTime = currentIdleTime + 1;
+}
+
+void CruAvatarMoverProcess::step(Animation::Sequence action, Direction direction,
+                              bool adjusted) {
+	MainActor *avatar = getMainActor();
+	Animation::Sequence lastanim = avatar->getLastAnim();
+
+	Animation::Result res = avatar->tryAnim(action, direction);
+
+	if (res != Animation::SUCCESS) {
+		World *world = World::get_instance();
+		CurrentMap *currentmap = world->getCurrentMap();
+
+		// Search right/left gradually increasing distance to see if we can make the move work.
+
+		Direction dir_right = Direction_TurnByDelta(direction, 4, dirmode_16dirs);
+		Direction dir_left = Direction_TurnByDelta(direction, -4, dirmode_16dirs);
+		Point3 origpt;
+		avatar->getLocation(origpt);
+		static const int ADJUSTMENTS[] = {0x10, 0x10, 0x20, 0x20, 0x30, 0x30,
+			0x40, 0x40, 0x50, 0x50};
+
+		for (int i = 0; i < ARRAYSIZE(ADJUSTMENTS); i++) {
+			Direction testdir = (i % 2 ? dir_left : dir_right);
+			int32 x = origpt.x + Direction_XFactor(testdir) * ADJUSTMENTS[i];
+			int32 y = origpt.y + Direction_YFactor(testdir) * ADJUSTMENTS[i];
+			int32 z = origpt.z;
+			if (currentmap->isValidPosition(x, y, z, avatar->getShape(), avatar->getObjId(),
+											nullptr, nullptr, nullptr)) {
+				avatar->setLocation(x, y, z);
+				res = avatar->tryAnim(action, direction);
+				if (res == Animation::SUCCESS)
+					break;
+			}
+		}
+
+		if (res != Animation::SUCCESS) {
+			// reset location, couldn't move.
+			avatar->setLocation(origpt.x, origpt.y, origpt.z);
+		}
+	}
+
+	if ((action == Animation::step || action == Animation::advance ||
+		 action == Animation::retreat || action == Animation::run ||
+		 action == Animation::startRun || action == Animation::walk)
+		&& res == Animation::FAILURE) {
+		action = Animation::stand;
+	}
+
+	bool moving = (action == Animation::run || action == Animation::walk);
+
+	if (checkTurn(direction, moving))
+		return;
+
+	debug(6, "Cru avatar step: picked action %d dir %d (test result %d)", action, direction, res);
+	action = Animation::checkWeapon(action, lastanim);
+	waitFor(avatar->doAnim(action, direction));
+}
+
+void CruAvatarMoverProcess::jump(Animation::Sequence action, Direction direction) {
+	MainActor *avatar = getMainActor();
+
+	// running jump
+	if (action == Animation::runningJump) {
+		waitFor(avatar->doAnim(action, direction));
+		return;
+	}
+
+	// airwalk
+	if (avatar->hasActorFlags(Actor::ACT_AIRWALK) &&
+	        action == Animation::jump) {
+		waitFor(avatar->doAnim(Animation::airwalkJump, direction));
+		return;
+	}
+
+	waitFor(avatar->doAnim(Animation::jump, direction));
+}
+
+bool CruAvatarMoverProcess::canAttack() {
+	MainActor *avatar = getMainActor();
+	return avatar->isInCombat();
+}
+
+void CruAvatarMoverProcess::tryAttack() {
+	MainActor *avatar = getMainActor();
+	Direction dir = avatar->getDir();
+	if (!avatar->isInCombat()) {
+		avatar->setInCombat(0);
+		waitFor(avatar->doAnim(Animation::readyWeapon, dir));
+	} else {
+		if (canAttack()) {
+			waitFor(avatar->doAnim(Animation::attack, dir));
+			// FIXME: put some real values in here.
+			int32 xs, ys, zs;
+			avatar->getFootpadWorld(xs, ys, zs);
+			avatar->fireWeapon(xs / 2, ys / 2, zs / 2, dir, 1, 1);
+		}
+	}
+}
+
+void CruAvatarMoverProcess::saveData(Common::WriteStream *ws) {
+	AvatarMoverProcess::saveData(ws);
+}
+
+bool CruAvatarMoverProcess::loadData(Common::ReadStream *rs, uint32 version) {
+	if (!AvatarMoverProcess::loadData(rs, version)) return false;
+	return true;
+}
+
+} // End of namespace Ultima8
+} // End of namespace Ultima
diff --git a/engines/ultima/ultima8/world/actors/cru_avatar_mover_process.h b/engines/ultima/ultima8/world/actors/cru_avatar_mover_process.h
new file mode 100644
index 0000000000..c10fa99cd4
--- /dev/null
+++ b/engines/ultima/ultima8/world/actors/cru_avatar_mover_process.h
@@ -0,0 +1,65 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef WORLD_ACTORS_CRUAVATARMOVERPROCESS_H
+#define WORLD_ACTORS_CRUAVATARMOVERPROCESS_H
+
+#include "ultima/ultima8/kernel/process.h"
+#include "ultima/ultima8/world/actors/animation.h"
+#include "ultima/ultima8/world/actors/avatar_mover_process.h"
+
+namespace Ultima {
+namespace Ultima8 {
+
+/**
+ * Mover process that replicates the feel of Crusader - moving, combat, jumps, etc.
+ * Tries sliding left and right if movement is blocked.  Walking cancels combat.
+ * TODO: Support combat rolls and side-steps.
+ */
+class CruAvatarMoverProcess : public AvatarMoverProcess {
+public:
+	CruAvatarMoverProcess();
+	~CruAvatarMoverProcess() override;
+
+	// p_dynamic_cast stuff
+	ENABLE_RUNTIME_CLASSTYPE()
+
+	bool loadData(Common::ReadStream *rs, uint32 version);
+	void saveData(Common::WriteStream *ws) override;
+
+	void tryAttack() override;
+
+private:
+	void handleHangingMode() override;
+	void handleCombatMode() override;
+	void handleNormalMode() override;
+	bool canAttack() override;
+
+	void step(Animation::Sequence action, Direction direction, bool adjusted = false);
+	void jump(Animation::Sequence action, Direction direction);
+
+};
+
+} // End of namespace Ultima8
+} // End of namespace Ultima
+
+#endif
diff --git a/engines/ultima/ultima8/world/actors/u8_avatar_mover_process.cpp b/engines/ultima/ultima8/world/actors/u8_avatar_mover_process.cpp
new file mode 100644
index 0000000000..5e6ffdc7d3
--- /dev/null
+++ b/engines/ultima/ultima8/world/actors/u8_avatar_mover_process.cpp
@@ -0,0 +1,848 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "ultima/ultima8/misc/pent_include.h"
+#include "ultima/ultima8/world/actors/u8_avatar_mover_process.h"
+#include "ultima/ultima8/world/actors/animation.h"
+#include "ultima/ultima8/ultima8.h"
+#include "ultima/ultima8/world/actors/main_actor.h"
+#include "ultima/ultima8/gumps/game_map_gump.h"
+#include "ultima/ultima8/kernel/kernel.h"
+#include "ultima/ultima8/world/actors/actor_anim_process.h"
+#include "ultima/ultima8/world/actors/targeted_anim_process.h"
+#include "ultima/ultima8/world/actors/avatar_gravity_process.h"
+#include "ultima/ultima8/graphics/shape_info.h"
+#include "ultima/ultima8/conf/setting_manager.h"
+#include "ultima/ultima8/audio/music_process.h"
+#include "ultima/ultima8/world/get_object.h"
+#include "ultima/ultima8/misc/direction.h"
+#include "ultima/ultima8/misc/direction_util.h"
+
+namespace Ultima {
+namespace Ultima8 {
+
+// p_dynamic_cast stuff
+DEFINE_RUNTIME_CLASSTYPE_CODE(U8AvatarMoverProcess)
+
+U8AvatarMoverProcess::U8AvatarMoverProcess() : AvatarMoverProcess(),
+		_lastHeadShakeAnim(Animation::lookLeft) {
+}
+
+
+U8AvatarMoverProcess::~U8AvatarMoverProcess() {
+}
+
+void U8AvatarMoverProcess::handleHangingMode() {
+	bool stasis = Ultima8Engine::get_instance()->isAvatarInStasis();
+
+	_idleTime = 0;
+
+	if (stasis)
+		return;
+
+	bool m0clicked = false;
+	//bool m1clicked = false;
+	if (!_mouseButton[0].isState(MBS_HANDLED) &&
+		!_mouseButton[0].curWithinDblClkTimeout()) {
+		m0clicked = true;
+		_mouseButton[0].setState(MBS_HANDLED);
+	}
+	if (!_mouseButton[1].isState(MBS_HANDLED) &&
+		!_mouseButton[1].curWithinDblClkTimeout()) {
+		//m1clicked = true;
+		_mouseButton[1].setState(MBS_HANDLED);
+	}
+
+	// if left mouse is down, try to climb up
+
+	if (_mouseButton[0].isState(MBS_DOWN) &&
+			(!_mouseButton[0].isState(MBS_HANDLED) || m0clicked)) {
+		_mouseButton[0].setState(MBS_HANDLED);
+		_mouseButton[0]._lastDown = 0;
+		MainActor *avatar = getMainActor();
+
+		if (avatar->tryAnim(Animation::climb40, dir_current) == Animation::SUCCESS) {
+			avatar->ensureGravityProcess()->terminate();
+			waitFor(avatar->doAnim(Animation::climb40, dir_current));
+		}
+	}
+}
+
+void U8AvatarMoverProcess::handleCombatMode() {
+	Mouse *mouse = Mouse::get_instance();
+	MainActor *avatar = getMainActor();
+	Animation::Sequence lastanim = avatar->getLastAnim();
+	Direction direction = avatar->getDir();
+	bool stasis = Ultima8Engine::get_instance()->isAvatarInStasis();
+
+	int32 mx, my;
+	mouse->getMouseCoords(mx, my);
+	unsigned int mouselength = mouse->getMouseLength(mx, my);
+
+	Direction mousedir = mouse->getMouseDirectionWorld(mx, my);
+
+	// never idle when in combat
+	_idleTime = 0;
+
+	// If Avatar has fallen down, stand up.
+	if (lastanim == Animation::die || lastanim == Animation::fallBackwards) {
+		if (!stasis)
+			waitFor(avatar->doAnim(Animation::standUp, mousedir));
+		return;
+	}
+
+	// if we were blocking, and no longer holding the mouse, stop
+	if (lastanim == Animation::startBlock &&
+	        !_mouseButton[0].isState(MBS_DOWN)) {
+		waitFor(avatar->doAnim(Animation::stopBlock, direction));
+		return;
+	}
+
+	// can't do any new actions if in stasis
+	if (stasis)
+		return;
+
+	bool m0clicked = false;
+	bool m1clicked = false;
+
+	if (!_mouseButton[0].isState(MBS_HANDLED) &&
+	    !_mouseButton[0].curWithinDblClkTimeout()) {
+		m0clicked = true;
+		_mouseButton[0].setState(MBS_HANDLED);
+	}
+
+	if (!_mouseButton[1].isState(MBS_HANDLED) &&
+	    !_mouseButton[1].curWithinDblClkTimeout()) {
+		m1clicked = true;
+		_mouseButton[1].setState(MBS_HANDLED);
+	}
+
+	if (!_mouseButton[0].isState(MBS_DOWN)) {
+		clearMovementFlag(MOVE_MOUSE_DIRECTION);
+	}
+
+	if (_mouseButton[0].isState(MBS_DOWN) &&
+	        _mouseButton[0].isState(MBS_HANDLED) && _mouseButton[0]._lastDown > 0) {
+		// left click-and-hold = block
+		if (lastanim == Animation::startBlock)
+			return;
+
+//		pout << "AvatarMover: combat block" << Std::endl;
+
+		if (checkTurn(mousedir, false))
+			return;
+
+		waitFor(avatar->doAnim(Animation::startBlock, mousedir));
+		return;
+	}
+
+	if (_mouseButton[0].isUnhandledDoubleClick()) {
+		_mouseButton[0].setState(MBS_HANDLED);
+		_mouseButton[0]._lastDown = 0;
+
+		if (canAttack()) {
+			// double left click = attack
+//			pout << "AvatarMover: combat attack" << Std::endl;
+
+			if (checkTurn(mousedir, true))
+				return;
+
+			waitFor(avatar->doAnim(Animation::attack, mousedir));
+			_lastAttack = _lastFrame;
+
+			// attacking gives str/dex
+			avatar->accumulateStr(1 + (getRandom() % 2));
+			avatar->accumulateDex(2 + (getRandom() % 2));
+		}
+
+		return;
+	}
+
+	if (_mouseButton[1].isUnhandledDoubleClick()) {
+		_mouseButton[1].setState(MBS_HANDLED);
+		_mouseButton[1]._lastDown = 0;
+
+		Gump *desktopgump = Ultima8Engine::get_instance()->getDesktopGump();
+		if (desktopgump->TraceObjId(mx, my) == 1) {
+			// double right click on avatar = toggle combat mode
+			avatar->toggleInCombat();
+			waitFor(avatar->doAnim(Animation::unreadyWeapon, direction));
+			return;
+		}
+
+		if (canAttack()) {
+			// double right click = kick
+//			pout << "AvatarMover: combat kick" << Std::endl;
+
+			if (checkTurn(mousedir, false))
+				return;
+
+			waitFor(avatar->doAnim(Animation::kick, mousedir));
+			_lastAttack = _lastFrame;
+
+			// kicking gives str/dex
+			avatar->accumulateStr(1 + (getRandom() % 2));
+			avatar->accumulateDex(2 + (getRandom() % 2));
+		}
+
+		return;
+	}
+
+	if (_mouseButton[1].isState(MBS_DOWN) && _mouseButton[1].isState(MBS_HANDLED)) {
+		// Note: Orginal game allowed a move animation on a single right click.
+		// This implementation needs right mouse to be held. 
+		setMovementFlag(MOVE_MOUSE_DIRECTION);
+
+		if (checkTurn(mousedir, true))
+			return;
+
+		//!! TODO: check if you can actually take this step
+		Direction nextdir = mousedir;
+		Animation::Sequence nextanim;
+
+		if (lastanim == Animation::run) {
+			// want to run while in combat mode?
+			// first sheath weapon
+			nextanim = Animation::readyWeapon;
+		} else if (Direction_Invert(direction) == mousedir) {
+			nextanim = Animation::retreat;
+			nextdir = direction;
+		} else {
+			nextanim = Animation::advance;
+		}
+
+		if (mouselength == 2) {
+			// Take a step before running
+			nextanim = Animation::walk;
+			avatar->setActorFlag(Actor::ACT_COMBATRUN);
+			avatar->toggleInCombat();
+			MusicProcess::get_instance()->playCombatMusic(110); // CONSTANT!!
+		}
+
+		nextanim = Animation::checkWeapon(nextanim, lastanim);
+		waitFor(avatar->doAnim(nextanim, nextdir));
+		return;
+	}
+
+	// if clicked, turn in mouse direction
+	if (m0clicked || m1clicked)
+		if (checkTurn(mousedir, false))
+			return;
+
+	bool moving = (lastanim == Animation::advance || lastanim == Animation::retreat);
+
+	DirectionMode dirmode = avatar->animDirMode(Animation::combatStand);
+
+	//  if we are trying to move, allow change direction only after move occurs to avoid spinning
+	if (moving || !hasMovementFlags(MOVE_FORWARD | MOVE_BACK)) {
+		if (hasMovementFlags(MOVE_TURN_LEFT)) {
+			direction = Direction_OneLeft(direction, dirmode);
+		}
+
+		if (hasMovementFlags(MOVE_TURN_RIGHT)) {
+			direction = Direction_OneRight(direction, dirmode);
+		}
+	}
+
+	if (hasMovementFlags(MOVE_FORWARD)) {
+		Animation::Sequence nextanim = Animation::advance;
+
+		if (lastanim == Animation::run) {
+			// want to run while in combat mode?
+			// first sheath weapon
+			nextanim = Animation::readyWeapon;
+		} 
+
+		if (hasMovementFlags(MOVE_RUN)) {
+			// Take a step before running
+			nextanim = Animation::walk;
+			avatar->setActorFlag(Actor::ACT_COMBATRUN);
+			avatar->toggleInCombat();
+			MusicProcess::get_instance()->playCombatMusic(110); // CONSTANT!!
+		}
+
+		nextanim = Animation::checkWeapon(nextanim, lastanim);
+		waitFor(avatar->doAnim(nextanim, direction));
+		return;
+	}
+
+	if (hasMovementFlags(MOVE_BACK)) {
+		waitFor(avatar->doAnim(Animation::retreat, direction));
+		return;
+	}
+
+	int y = 0;
+	int x = 0;
+	if (hasMovementFlags(MOVE_UP)) {
+		y++;
+	}
+	if (hasMovementFlags(MOVE_DOWN)) {
+		y--;
+	}
+	if (hasMovementFlags(MOVE_LEFT)) {
+		x--;
+	}
+	if (hasMovementFlags(MOVE_RIGHT)) {
+		x++;
+	}
+
+	if (x != 0 || y != 0) {
+		Direction nextdir = Direction_Get(y, x, dirmode_8dirs);
+
+		if (checkTurn(nextdir, true))
+			return;
+
+		Animation::Sequence nextanim;
+		if (lastanim == Animation::run) {
+			// want to run while in combat mode?
+			// first sheath weapon
+			nextanim = Animation::readyWeapon;
+		} else if (Direction_Invert(direction) == nextdir) {
+			nextanim = Animation::retreat;
+			nextdir = direction;
+		} else {
+			nextanim = Animation::advance;
+		}
+
+		if (hasMovementFlags(MOVE_RUN)) {
+			// Take a step before running
+			nextanim = Animation::walk;
+			avatar->setActorFlag(Actor::ACT_COMBATRUN);
+			avatar->toggleInCombat();
+			MusicProcess::get_instance()->playCombatMusic(110); // CONSTANT!!
+		}
+
+		nextanim = Animation::checkWeapon(nextanim, lastanim);
+		waitFor(avatar->doAnim(nextanim, nextdir));
+		return;
+	}
+
+	if (checkTurn(direction, false))
+		return;
+
+	// not doing anything in particular? stand
+	// TODO: make sure falling works properly.
+	if (lastanim != Animation::combatStand) {
+		Animation::Sequence nextanim = Animation::combatStand;
+		nextanim = Animation::checkWeapon(nextanim, lastanim);
+		waitFor(avatar->doAnim(nextanim, direction));
+	}
+}
+
+void U8AvatarMoverProcess::handleNormalMode() {
+	Ultima8Engine *guiapp = Ultima8Engine::get_instance();
+	Mouse *mouse = Mouse::get_instance();
+	MainActor *avatar = getMainActor();
+	Animation::Sequence lastanim = avatar->getLastAnim();
+	Direction direction = avatar->getDir();
+	bool stasis = guiapp->isAvatarInStasis();
+	bool combatRun = avatar->hasActorFlags(Actor::ACT_COMBATRUN);
+
+	int32 mx, my;
+	mouse->getMouseCoords(mx, my);
+	unsigned int mouselength = mouse->getMouseLength(mx, my);
+	Direction mousedir = mouse->getMouseDirectionWorld(mx, my);
+
+	// Store current idle time. (Also see end of function.)
+	uint32 currentIdleTime = _idleTime;
+	_idleTime = 0;
+
+	// User toggled combat while in combatRun
+	if (avatar->isInCombat()) {
+		avatar->clearActorFlag(Actor::ACT_COMBATRUN);
+		avatar->toggleInCombat();
+	}
+
+	// If Avatar has fallen down, stand up.
+	if (lastanim == Animation::die || lastanim == Animation::fallBackwards) {
+		if (!stasis) {
+			waitFor(avatar->doAnim(Animation::standUp, direction));
+		}
+		return;
+	}
+
+	// If still in combat stance, sheathe weapon
+	if (!stasis && Animation::isCombatAnim(lastanim)) {
+		ProcId anim1 = avatar->doAnim(Animation::unreadyWeapon, direction);
+		ProcId anim2 = avatar->doAnim(Animation::stand, direction);
+		Process *anim2p = Kernel::get_instance()->getProcess(anim2);
+		anim2p->waitFor(anim1);
+		waitFor(anim2);
+
+		return;
+	}
+
+	bool m0clicked = false;
+	bool m1clicked = false;
+
+	// check mouse state to see what needs to be done
+	if (!_mouseButton[0].isState(MBS_HANDLED) &&
+		!_mouseButton[0].curWithinDblClkTimeout()) {
+		m0clicked = true;
+		_mouseButton[0].setState(MBS_HANDLED);
+	}
+
+	if (!_mouseButton[1].isState(MBS_HANDLED) &&
+	    !_mouseButton[1].curWithinDblClkTimeout()) {
+		m1clicked = true;
+		_mouseButton[1].setState(MBS_HANDLED);
+	}
+
+	if (!_mouseButton[1].isState(MBS_DOWN)) {
+		clearMovementFlag(MOVE_MOUSE_DIRECTION);
+	}
+
+	if (_mouseButton[1].isState(MBS_DOWN) && _mouseButton[1].isState(MBS_HANDLED)) {
+		// Note: Orginal game allowed a move animation on a single right click.
+		// This implementation needs right mouse to be held. 
+		setMovementFlag(MOVE_MOUSE_DIRECTION);
+	}
+
+	if (!hasMovementFlags(MOVE_ANY_DIRECTION)) {
+		// if we were running in combat mode, slow to a walk, draw weapon
+		// (even in stasis)
+		if (combatRun) {
+			avatar = getMainActor();
+			avatar->clearActorFlag(Actor::ACT_COMBATRUN);
+			avatar->toggleInCombat();
+
+			// If we were running, slow to a walk before drawing weapon.
+			// Note: Original game did not check last animation and always took an extra walk.
+			if (lastanim == Animation::run || lastanim == Animation::runningJump) {
+				ProcId walkpid = avatar->doAnim(Animation::walk, direction);
+				ProcId drawpid = avatar->doAnim(Animation::readyWeapon, direction);
+				Process *drawproc = Kernel::get_instance()->getProcess(drawpid);
+				drawproc->waitFor(walkpid);
+				waitFor(drawpid);
+				return;
+			}
+
+			waitFor(avatar->doAnim(Animation::readyWeapon, direction));
+			return;
+		}
+
+		// if we were running, slow to a walk before stopping
+		// (even in stasis)
+		if (lastanim == Animation::run) {
+			ProcId walkpid = avatar->doAnim(Animation::walk, direction);
+			ProcId standpid = avatar->doAnim(Animation::stand, direction);
+			Process *standproc = Kernel::get_instance()->getProcess(standpid);
+			standproc->waitFor(walkpid);
+			waitFor(standpid);
+			return;
+		}
+
+		// TODO: if we were hanging, fall
+	}
+
+	// can't do any new actions if in stasis
+	if (stasis)
+		return;
+
+	// both mouse buttons down and not yet handled, check for jump.
+	if (!_mouseButton[0].isState(MBS_HANDLED) && !_mouseButton[1].isState(MBS_HANDLED)) {
+		// Take action if both were clicked within
+		// double-click timeout of each other.
+		// notice these are all unsigned.
+		uint32 down = _mouseButton[1]._curDown;
+		if (_mouseButton[0]._curDown < down) {
+			down = down - _mouseButton[0]._curDown;
+		} else {
+			down = _mouseButton[0]._curDown - down;
+		}
+
+		if (down < DOUBLE_CLICK_TIMEOUT) {
+			// Both buttons pressed within the timeout
+			_mouseButton[0].setState(MBS_HANDLED);
+			_mouseButton[1].setState(MBS_HANDLED);
+			setMovementFlag(MOVE_JUMP);
+		}
+	}
+
+	if ((!_mouseButton[0].isState(MBS_HANDLED) || m0clicked) && hasMovementFlags(MOVE_ANY_DIRECTION | MOVE_STEP)) {
+		_mouseButton[0].setState(MBS_HANDLED);
+		// We got a left mouse down while already moving in any direction or holding the step button.
+		// CHECKME: check what needs to happen when keeping left pressed
+		setMovementFlag(MOVE_JUMP);
+	}
+
+	if (_mouseButton[1].isUnhandledDoubleClick()) {
+		Gump *desktopgump = Ultima8Engine::get_instance()->getDesktopGump();
+		if (desktopgump->TraceObjId(mx, my) == 1) {
+			// double right click on avatar = toggle combat mode
+			_mouseButton[1].setState(MBS_HANDLED);
+			_mouseButton[1]._lastDown = 0;
+
+			avatar->toggleInCombat();
+			waitFor(avatar->doAnim(Animation::readyWeapon, direction));
+			return;
+		}
+	}
+
+	if (hasMovementFlags(MOVE_JUMP) && hasMovementFlags(MOVE_ANY_DIRECTION)) {
+		clearMovementFlag(MOVE_JUMP);
+
+		if (hasMovementFlags(MOVE_MOUSE_DIRECTION)) {
+			if (checkTurn(mousedir, false))
+				return;
+		}
+
+		Animation::Sequence nextanim = Animation::jump;
+		// check if we need to do a running jump
+		if (lastanim == Animation::run || lastanim == Animation::runningJump) {
+			nextanim = Animation::runningJump;
+		}
+		else if (avatar->hasActorFlags(Actor::ACT_AIRWALK)) {
+			nextanim = Animation::airwalkJump;
+		}
+		else if ((hasMovementFlags(MOVE_MOUSE_DIRECTION) && mouselength == 0) || hasMovementFlags(MOVE_STEP)) {
+			nextanim = Animation::jumpUp;
+		}
+
+		nextanim = Animation::checkWeapon(nextanim, lastanim);
+		waitFor(avatar->doAnim(nextanim, direction));
+		return;
+	}
+
+	if (hasMovementFlags(MOVE_JUMP)) {
+		clearMovementFlag(MOVE_JUMP);
+
+		if (checkTurn(mousedir, false))
+			return;
+
+		Animation::Sequence nextanim = Animation::jumpUp;
+		if (mouselength > 0) {
+			nextanim = Animation::jump;
+		}
+
+		// check if there's something we can climb up onto here
+		Animation::Sequence climbanim = Animation::climb72;
+		while (climbanim >= Animation::climb16) {
+			if (avatar->tryAnim(climbanim, direction) ==
+				Animation::SUCCESS) {
+				nextanim = climbanim;
+			}
+			climbanim = static_cast<Animation::Sequence>(climbanim - 1);
+		}
+
+		if (nextanim == Animation::jump) {
+			jump(Animation::jump, direction);
+		}
+		else {
+			if (nextanim != Animation::jumpUp) {
+				// climbing gives str/dex
+				avatar->accumulateStr(2 + nextanim - Animation::climb16);
+				avatar->accumulateDex(2 * (2 + nextanim - Animation::climb16));
+			}
+			nextanim = Animation::checkWeapon(nextanim, lastanim);
+			waitFor(avatar->doAnim(nextanim, direction));
+		}
+		return;
+	}
+
+	if (hasMovementFlags(MOVE_MOUSE_DIRECTION)) {
+		Animation::Sequence nextanim = Animation::step;
+
+		if (mouselength == 1) {
+			nextanim = Animation::walk;
+		} else if (mouselength == 2) {
+			if (lastanim == Animation::run
+			        || lastanim == Animation::runningJump
+			        || lastanim == Animation::walk)
+				nextanim = Animation::run;
+			else
+				nextanim = Animation::walk;
+		}
+
+		step(nextanim, mousedir);
+		return;
+	}
+
+	if (m1clicked)
+		if (checkTurn(mousedir, false))
+			return;
+
+	bool moving = (lastanim == Animation::step || lastanim == Animation::run || lastanim == Animation::walk);
+
+	DirectionMode dirmode = avatar->animDirMode(Animation::step);
+
+	//  if we are trying to move, allow change direction only after move occurs to avoid spinning
+	if (moving || !hasMovementFlags(MOVE_FORWARD | MOVE_BACK)) {
+		if (hasMovementFlags(MOVE_TURN_LEFT)) {
+			direction = Direction_OneLeft(direction, dirmode);
+		}
+
+		if (hasMovementFlags(MOVE_TURN_RIGHT)) {
+			direction = Direction_OneRight(direction, dirmode);
+		}
+	}
+
+	Animation::Sequence nextanim = Animation::walk;
+
+	if (hasMovementFlags(MOVE_STEP)) {
+		nextanim = Animation::step;
+	} else if (hasMovementFlags(MOVE_RUN)) {
+		if (lastanim == Animation::run
+			    || lastanim == Animation::runningJump
+			    || lastanim == Animation::walk)
+			nextanim = Animation::run;
+		else
+			nextanim = Animation::walk;
+	}
+
+	if (hasMovementFlags(MOVE_FORWARD)) {
+		step(nextanim, direction);
+		return;
+	}
+
+	if (hasMovementFlags(MOVE_BACK)) {
+		step(nextanim, Direction_Invert(direction));
+
+		// flip to move forward once turned
+		setMovementFlag(MOVE_FORWARD);
+		return;
+	}
+
+	int y = 0;
+	int x = 0;
+	if (hasMovementFlags(MOVE_UP)) {
+		y++;
+	}
+	if (hasMovementFlags(MOVE_DOWN)) {
+		y--;
+	}
+	if (hasMovementFlags(MOVE_LEFT)) {
+		x--;
+	}
+	if (hasMovementFlags(MOVE_RIGHT)) {
+		x++;
+	}
+
+	if (x != 0 || y != 0) {
+		direction = Direction_Get(y, x, dirmode_8dirs);
+		step(nextanim, direction);
+		return;
+	}
+
+	if (checkTurn(direction, moving))
+		return;
+
+	// doing another animation?
+	if (Kernel::get_instance()->getNumProcesses(1, ActorAnimProcess::ACTOR_ANIM_PROC_TYPE))
+		return;
+
+	// if we were running, slow to a walk before stopping
+	if (lastanim == Animation::run) {
+		waitFor(avatar->doAnim(Animation::walk, direction));
+		return;
+	}
+
+	// not doing anything in particular? stand
+	if (lastanim != Animation::stand && currentIdleTime == 0) {
+		waitFor(avatar->doAnim(Animation::stand, direction));
+		return;
+	}
+
+	// idle
+	_idleTime = currentIdleTime + 1;
+
+	// currently shaking head?
+	if (lastanim == Animation::lookLeft || lastanim == Animation::lookRight) {
+		if ((getRandom() % 1500) + 30 < _idleTime) {
+			_lastHeadShakeAnim = lastanim;
+			waitFor(avatar->doAnim(Animation::stand, direction));
+			_idleTime = 1;
+			return;
+		}
+	} else {
+		if ((getRandom() % 3000) + 150 < _idleTime) {
+			if (getRandom() % 5 == 0)
+				nextanim = _lastHeadShakeAnim;
+			else if (_lastHeadShakeAnim == Animation::lookLeft)
+				nextanim = Animation::lookRight;
+			else
+				nextanim = Animation::lookLeft;
+			waitFor(avatar->doAnim(nextanim, direction));
+			_idleTime = 1;
+			return;
+		}
+	}
+}
+
+void U8AvatarMoverProcess::step(Animation::Sequence action, Direction direction,
+                              bool adjusted) {
+	assert(action == Animation::step || action == Animation::walk ||
+	       action == Animation::run);
+
+	MainActor *avatar = getMainActor();
+	Animation::Sequence lastanim = avatar->getLastAnim();
+
+	Animation::Result res = avatar->tryAnim(action, direction);
+
+	Direction stepdir = direction;
+
+	if (res == Animation::FAILURE ||
+	        (action == Animation::step && res == Animation::END_OFF_LAND)) {
+		debug(6, "Step: end off land dir %d, try other dir", stepdir);
+		Direction altdir1 = Direction_OneRight(stepdir, dirmode_8dirs);
+		Direction altdir2 = Direction_OneLeft(stepdir, dirmode_8dirs);
+
+		res = avatar->tryAnim(action, altdir1);
+		if (res == Animation::FAILURE ||
+		        (action == Animation::step && res == Animation::END_OFF_LAND)) {
+			debug(6, "Step: end off land dir %d, altdir1 %d failed, try altdir2 %d", stepdir, altdir1, altdir2);
+			res = avatar->tryAnim(action, altdir2);
+			if (res == Animation::FAILURE ||
+			        (action == Animation::step && res == Animation::END_OFF_LAND)) {
+				// Can't walk in this direction.
+				// Try to take a smaller step
+
+				if (action == Animation::walk) {
+					debug(6, "Step: end off land both altdirs failed, smaller step (step)");
+					step(Animation::step, direction, true);
+					return;
+				} else if (action == Animation::run) {
+					debug(6, "Step: end off land both altdirs failed, smaller step (walk)");
+					step(Animation::walk, direction, true);
+					return;
+				}
+
+			} else {
+				stepdir = altdir2;
+			}
+		} else {
+			stepdir = altdir1;
+		}
+	}
+
+	if (action == Animation::step && res == Animation::END_OFF_LAND &&
+	        lastanim != Animation::keepBalance && !adjusted) {
+		if (checkTurn(stepdir, false))
+			return;
+		debug(6, "Step: end off land both altdirs failed, keep balance.");
+		waitFor(avatar->doAnim(Animation::keepBalance, stepdir));
+		return;
+	}
+
+	if (action == Animation::step && res == Animation::FAILURE) {
+		action = Animation::stand;
+	}
+
+
+	bool moving = (action == Animation::run || action == Animation::walk);
+
+	if (checkTurn(stepdir, moving))
+		return;
+
+	debug(6, "Step: step ok: action %d dir %d", action, stepdir);
+	action = Animation::checkWeapon(action, lastanim);
+	waitFor(avatar->doAnim(action, stepdir));
+}
+
+void U8AvatarMoverProcess::jump(Animation::Sequence action, Direction direction) {
+	MainActor *avatar = getMainActor();
+
+	// running jump
+	if (action == Animation::runningJump) {
+		waitFor(avatar->doAnim(action, direction));
+		return;
+	}
+
+	// airwalk
+	if (avatar->hasActorFlags(Actor::ACT_AIRWALK) &&
+	        action == Animation::jump) {
+		waitFor(avatar->doAnim(Animation::airwalkJump, direction));
+		return;
+	}
+
+	bool targeting;
+	SettingManager::get_instance()->get("targetedjump", targeting);
+
+	if (targeting) {
+		Mouse *mouse = Mouse::get_instance();
+		int32 coords[3];
+		int32 mx, my;
+		mouse->getMouseCoords(mx, my);
+		GameMapGump *gameMap = Ultima8Engine::get_instance()->getGameMapGump();
+		// We need the Gump's x/y for TraceCoordinates
+		gameMap->ScreenSpaceToGump(mx, my);
+		ObjId targetId = gameMap->TraceCoordinates(mx, my, coords);
+		Item *target = getItem(targetId);
+
+		int32 ax, ay, az;
+		avatar->getCentre(ax, ay, az);
+
+		int32 xrange = abs(ax - coords[0]);
+		int32 yrange = abs(ay - coords[1]);
+		int maxrange = avatar->getStr() * 32;
+
+		if (target && target->getShapeInfo()->is_land() &&
+		        xrange < maxrange && yrange < maxrange) {
+			// Original also only lets you jump at the Z_FACE
+			Process *p = new TargetedAnimProcess(avatar, Animation::jumpUp,
+			                                     direction, coords);
+			waitFor(Kernel::get_instance()->addProcess(p));
+			return;
+		}
+		// invalid target or out of range
+		waitFor(avatar->doAnim(Animation::shakeHead, direction));
+	} else {
+		waitFor(avatar->doAnim(Animation::jump, direction));
+	}
+}
+
+bool U8AvatarMoverProcess::canAttack() {
+	MainActor *avatar = getMainActor();
+	return (_lastFrame > _lastAttack + (25 - avatar->getDex()));
+}
+
+void U8AvatarMoverProcess::tryAttack() {
+	MainActor *avatar = getMainActor();
+	Direction dir = avatar->getDir();
+	if (!avatar->isInCombat()) {
+		avatar->setInCombat(0);
+		waitFor(avatar->doAnim(Animation::readyWeapon, dir));
+	} else {
+		if (canAttack()) {
+			waitFor(avatar->doAnim(Animation::attack, dir));
+		}
+	}
+}
+
+void U8AvatarMoverProcess::saveData(Common::WriteStream *ws) {
+	AvatarMoverProcess::saveData(ws);
+	// Note: this field used to be the last thing saved in AvatarMoverProcess,
+	// so this relies on it being in the right order here (and loadData) for
+	// backwards compatibility.
+	ws->writeUint16LE(static_cast<uint8>(_lastHeadShakeAnim));
+}
+
+bool U8AvatarMoverProcess::loadData(Common::ReadStream *rs, uint32 version) {
+	if (!AvatarMoverProcess::loadData(rs, version)) return false;
+
+	_lastHeadShakeAnim = static_cast<Animation::Sequence>(rs->readUint16LE());
+
+	return true;
+}
+
+} // End of namespace Ultima8
+} // End of namespace Ultima
diff --git a/engines/ultima/ultima8/world/actors/u8_avatar_mover_process.h b/engines/ultima/ultima8/world/actors/u8_avatar_mover_process.h
new file mode 100644
index 0000000000..81fd454068
--- /dev/null
+++ b/engines/ultima/ultima8/world/actors/u8_avatar_mover_process.h
@@ -0,0 +1,68 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef WORLD_ACTORS_U8AVATARMOVERPROCESS_H
+#define WORLD_ACTORS_U8AVATARMOVERPROCESS_H
+
+#include "ultima/ultima8/kernel/process.h"
+#include "ultima/ultima8/world/actors/animation.h"
+#include "ultima/ultima8/world/actors/avatar_mover_process.h"
+
+namespace Ultima {
+namespace Ultima8 {
+
+/**
+ * Mover process that replicates the feel of U8 - moving, combat, jumps, etc.
+ * Tries turning one quarter turn if movement is blocked.  Running temporarily
+ * stops combat and plays some special movement.
+ */
+class U8AvatarMoverProcess : public AvatarMoverProcess {
+public:
+	U8AvatarMoverProcess();
+	~U8AvatarMoverProcess();
+
+	// p_dynamic_cast stuff
+	ENABLE_RUNTIME_CLASSTYPE()
+
+	bool loadData(Common::ReadStream *rs, uint32 version);
+	void saveData(Common::WriteStream *ws) override;
+
+	void tryAttack() override;
+
+protected:
+	void handleHangingMode() override;
+	void handleCombatMode() override;
+	void handleNormalMode() override;
+	bool canAttack() override;
+
+	void step(Animation::Sequence action, Direction direction, bool adjusted = false);
+	void jump(Animation::Sequence action, Direction direction);
+
+private:
+	Animation::Sequence _lastHeadShakeAnim;
+
+};
+
+} // End of namespace Ultima8
+} // End of namespace Ultima
+
+#endif


Commit: df46296f02c70aa2936d0c905d7d805ea3c04be8
    https://github.com/scummvm/scummvm/commit/df46296f02c70aa2936d0c905d7d805ea3c04be8
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2020-12-24T14:00:15+09:00

Commit Message:
ULTIMA8: Add 16 dir support for Direction_Get

Changed paths:
    engines/ultima/ultima8/misc/direction_util.h


diff --git a/engines/ultima/ultima8/misc/direction_util.h b/engines/ultima/ultima8/misc/direction_util.h
index 75fd8f0875..02892b47b6 100644
--- a/engines/ultima/ultima8/misc/direction_util.h
+++ b/engines/ultima/ultima8/misc/direction_util.h
@@ -25,6 +25,7 @@
 
 #include "ultima/ultima8/misc/direction.h"
 #include "ultima/ultima8/ultima8.h"
+#include "common/math.h"
 
 namespace Ultima {
 namespace Ultima8 {
@@ -69,23 +70,46 @@ inline int Direction_YFactor(Direction dir) {
 inline Direction Direction_Get(int deltay, int deltax, DirectionMode dirmode) {
 	if (deltax == 0)
 		return deltay > 0 ? dir_northwest : dir_southeast;
-	int dydx = (1024 * deltay) / deltax; // Figure 1024*tan.
-	if (dydx >= 0)
-		if (deltax > 0) // Top-right
-			return dydx <= 424 ? dir_northeast : dydx <= 2472 ? dir_north
-				   : dir_northwest;
-		else			// Bottom-left.
-			return dydx <= 424 ? dir_southwest : dydx <= 2472 ? dir_south
+
+	if (dirmode == dirmode_8dirs) {
+		int dydx = (1024 * deltay) / deltax; // Figure 1024*tan.
+		if (dydx >= 0)
+			if (deltax > 0) // Top-right
+				return dydx <= 424 ? dir_northeast : dydx <= 2472 ? dir_north
+					   : dir_northwest;
+			else			// Bottom-left.
+				return dydx <= 424 ? dir_southwest : dydx <= 2472 ? dir_south
+					   : dir_southeast;
+		else if (deltax > 0) // Bottom-right.
+			return dydx >= -424 ? dir_northeast : dydx >= -2472 ? dir_east
 				   : dir_southeast;
-	else if (deltax > 0) // Bottom-right.
-		return dydx >= -424 ? dir_northeast : dydx >= -2472 ? dir_east
-			   : dir_southeast;
-	else			// Top-left
-		return dydx >= -424 ? dir_southwest : dydx >= -2472 ? dir_west
-			   : dir_northwest;
+		else			// Top-left
+			return dydx >= -424 ? dir_southwest : dydx >= -2472 ? dir_west
+				   : dir_northwest;
+	} else {
+		double angle = Common::rad2deg(atan2(deltay, deltax));
+		if (angle < 11.25)		 return dir_northwest;
+		else if (angle < 33.75)  return dir_nnw;
+		else if (angle < 56.25)  return dir_north;
+		else if (angle < 78.75)  return dir_nne;
+		else if (angle < 101.25) return dir_northeast;
+		else if (angle < 123.75) return dir_ene;
+		else if (angle < 146.25) return dir_east;
+		else if (angle < 168.75) return dir_ese;
+		else if (angle < 191.25) return dir_southeast;
+		else if (angle < 213.75) return dir_sse;
+		else if (angle < 236.25) return dir_south;
+		else if (angle < 258.75) return dir_ssw;
+		else if (angle < 281.25) return dir_southwest;
+		else if (angle < 303.75) return dir_wsw;
+		else if (angle < 326.25) return dir_west;
+		else if (angle < 348.75) return dir_wnw;
+		return dir_northwest;
+	}
 }
 
 inline Direction Direction_GetWorldDir(int deltay, int deltax, DirectionMode dirmode) {
+	// TODO: Implement 16 directions here.
 	if (deltax == 0) {
 		if (deltay == 0) return dir_northeast; // for better compatibility with U8
 		return deltay > 0 ? dir_south : dir_north;


Commit: ebae8ca44bc1c1cb06b2471bb4e64b8d0570695c
    https://github.com/scummvm/scummvm/commit/ebae8ca44bc1c1cb06b2471bb4e64b8d0570695c
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2020-12-24T14:00:15+09:00

Commit Message:
ULTIMA8: Fix crusader firing in a few ways:

* Remove explicit call to fire, as it is called from animation
* Fix fire z value from anim flags (should be unsigned)
* Use correct starting location in firedistance intrinsic

Changed paths:
    engines/ultima/ultima8/world/actors/actor_anim_process.cpp
    engines/ultima/ultima8/world/actors/anim_action.h
    engines/ultima/ultima8/world/actors/cru_avatar_mover_process.cpp
    engines/ultima/ultima8/world/item.cpp
    engines/ultima/ultima8/world/item.h
    engines/ultima/ultima8/world/super_sprite_process.cpp
    engines/ultima/ultima8/world/super_sprite_process.h


diff --git a/engines/ultima/ultima8/world/actors/actor_anim_process.cpp b/engines/ultima/ultima8/world/actors/actor_anim_process.cpp
index 8673129ac9..9762e5dd9b 100644
--- a/engines/ultima/ultima8/world/actors/actor_anim_process.cpp
+++ b/engines/ultima/ultima8/world/actors/actor_anim_process.cpp
@@ -514,7 +514,7 @@ void ActorAnimProcess::doFireWeaponCru(Actor *a, const AnimFrame *f) {
 		return;
 
 	a->fireWeapon(f->cru_attackx(), f->cru_attacky(), f->cru_attackz(),
-				  dir_current, wpninfo->_weaponInfo->_damageType, 1);
+				  a->getDir(), wpninfo->_weaponInfo->_damageType, 1);
 
 	AudioProcess *audioproc = AudioProcess::get_instance();
 	if (audioproc)
diff --git a/engines/ultima/ultima8/world/actors/anim_action.h b/engines/ultima/ultima8/world/actors/anim_action.h
index 683cd9ae2c..5420eadfc6 100644
--- a/engines/ultima/ultima8/world/actors/anim_action.h
+++ b/engines/ultima/ultima8/world/actors/anim_action.h
@@ -71,7 +71,7 @@ struct AnimFrame {
 	}
 
 	// Note: The next 3 functions each have a 4-bit
-	// signed value to unpack from the flags.
+	// value to unpack from the flags. x/y are signed, z is unsigned.
 	inline int cru_attackx() const {
 		uint32 rawx = (_flags & 0x00000780) << 5;
 		int16  signedx = static_cast<int16>(rawx) >> 12;
@@ -84,8 +84,7 @@ struct AnimFrame {
 	}
 
 	inline int cru_attackz() const {
-		uint32 rawz = (_flags & 0x0F000000) >> 20;
-		return static_cast<int8>(rawz) / 2;
+		return (_flags & 0x0F000000) >> 21;
 	}
 
 	inline bool is_cruattack() const {
diff --git a/engines/ultima/ultima8/world/actors/cru_avatar_mover_process.cpp b/engines/ultima/ultima8/world/actors/cru_avatar_mover_process.cpp
index 82b0b19c19..b96306468c 100644
--- a/engines/ultima/ultima8/world/actors/cru_avatar_mover_process.cpp
+++ b/engines/ultima/ultima8/world/actors/cru_avatar_mover_process.cpp
@@ -428,11 +428,8 @@ void CruAvatarMoverProcess::tryAttack() {
 		waitFor(avatar->doAnim(Animation::readyWeapon, dir));
 	} else {
 		if (canAttack()) {
+			// Fire event happens from animation
 			waitFor(avatar->doAnim(Animation::attack, dir));
-			// FIXME: put some real values in here.
-			int32 xs, ys, zs;
-			avatar->getFootpadWorld(xs, ys, zs);
-			avatar->fireWeapon(xs / 2, ys / 2, zs / 2, dir, 1, 1);
 		}
 	}
 }
diff --git a/engines/ultima/ultima8/world/item.cpp b/engines/ultima/ultima8/world/item.cpp
index ccda99b3cd..09421a4c87 100644
--- a/engines/ultima/ultima8/world/item.cpp
+++ b/engines/ultima/ultima8/world/item.cpp
@@ -36,6 +36,7 @@
 #include "ultima/ultima8/gumps/game_map_gump.h"
 #include "ultima/ultima8/graphics/main_shape_archive.h"
 #include "ultima/ultima8/graphics/gump_shape_archive.h"
+#include "ultima/ultima8/graphics/anim_dat.h"
 #include "ultima/ultima8/graphics/shape.h"
 #include "ultima/ultima8/graphics/shape_info.h"
 #include "ultima/ultima8/world/item_factory.h"
@@ -67,6 +68,7 @@
 #include "ultima/ultima8/world/actors/main_actor.h"
 #include "ultima/ultima8/world/missile_tracker.h"
 #include "ultima/ultima8/world/crosshair_process.h"
+#include "ultima/ultima8/world/actors/anim_action.h"
 
 namespace Ultima {
 namespace Ultima8 {
@@ -1153,7 +1155,7 @@ int32 Item::collideMove(int32 dx, int32 dy, int32 dz, bool teleport, bool force,
 	return 0;
 }
 
-uint16 Item::fireWeapon(int32 x, int32 y, int32 z, Direction dir, int firetype, char someflag) {
+uint16 Item::fireWeapon(int32 x, int32 y, int32 z, Direction dir, int firetype, char findtarget) {
 	int32 ix, iy, iz;
 	getLocation(ix, iy, iz);
 
@@ -1214,6 +1216,7 @@ uint16 Item::fireWeapon(int32 x, int32 y, int32 z, Direction dir, int firetype,
 
 		// HACK: this should be fixed to use inheritence so the behavior
 		// is clean for both Item and Actor.
+		DirectionMode dirmode = dirmode_8dirs;
 		const Actor *thisactor = dynamic_cast<Actor *>(this);
 		if (thisactor) {
 			// TODO: Get damage for active inventory item, and dirmode of last
@@ -1221,12 +1224,15 @@ uint16 Item::fireWeapon(int32 x, int32 y, int32 z, Direction dir, int firetype,
 		}
 
 		Item *target = nullptr;
-		if (someflag) {
+		if (findtarget) {
 			if (this != getControlledActor()) {
 				target = getControlledActor();
 			} else {
 				// TODO: this should be dirmode of last animation (above)
-				target = currentmap->findBestTargetItem(ix, iy, dir, dirmode_8dirs);
+				// for now, hack for avatar
+				if (thisactor->getShape() == 1 && thisactor->isInCombat())
+					dirmode = dirmode_16dirs;
+				target = currentmap->findBestTargetItem(ix, iy, dir, dirmode);
 			}
 		}
 
@@ -1234,29 +1240,8 @@ uint16 Item::fireWeapon(int32 x, int32 y, int32 z, Direction dir, int firetype,
 		int32 ty = 0;
 		int32 tz = 0;
 		if (target) {
-			int32 tsx, tsy, tsz;
 			target->getCentre(tx, ty, tz);
-			target->getFootpadData(tsx, tsy, tsz);
-
-			tz = target->getZ() + tsz * 8;
-
-			if (tsz < 3) {
-				if (tsz)
-					tz -= 8;
-			} else {
-				int32 targetz = tz;
-				int32 thisz = getZ();
-				tz -= 16;
-				if (thisz - targetz < -0x2f) {
-					tz += 8;
-				} else if (thisz - targetz > 0x2f) {
-					if (tsz == 6) {
-						tz -= 16;
-					} else if (tsz >= 7) {
-						tz -= 24;
-					}
-				}
-			}
+			tz = target->getTargetZRelativeToAttackerZ(getZ());
 		}
 
 		// TODO: check if we need the equivalent of FUN_1130_0299 here..
@@ -1292,7 +1277,7 @@ uint16 Item::fireWeapon(int32 x, int32 y, int32 z, Direction dir, int firetype,
 			uint16 targetid = (target ? target->getObjId() : 0);
 			ssp = new SuperSpriteProcess(BULLET_SPLASH_SHAPE, spriteframe,
 										 ix, iy, iz, ssx, ssy, ssz, firetype,
-										 damage, _objId, targetid, someflag);
+										 damage, _objId, targetid, findtarget);
 			Kernel::get_instance()->addProcess(ssp);
 			spriteprocpid = ssp->getPid();
 		}
@@ -1304,6 +1289,20 @@ uint16 Item::fireDistance(Item *other, Direction dir, int16 xoff, int16 yoff, in
 	if (!other)
 		return 0;
 
+	//
+	// We pick what animation the actor would do to fire, then
+	// pick the frame(s) where they fire in that anim.
+	//
+	// Then, check if the target can be hit using the attackx/attacky/attackz offsets.
+	// The offsets are checked in priority order:
+	// * First fire frame in anim
+	// * Second fire frame
+	// * if there are no fire frames, use the parameter offsets
+	//
+	int16 xoff2 = 0;
+	int16 yoff2 = 0;
+	int16 zoff2 = 0;
+	bool other_offsets = false;
 	Actor *a = dynamic_cast<Actor *>(this);
 	if (a) {
 		Animation::Sequence anim;
@@ -1327,11 +1326,29 @@ uint16 Item::fireDistance(Item *other, Direction dir, int16 xoff, int16 yoff, in
 				anim = Animation::fire2;
 		}
 
-		// TODO: Get midpoint of frames in anim.  For now we ignore it and get the centre below.
+		bool first_offsets = false;
+		const AnimAction *action = GameData::get_instance()->getMainShapes()->getAnim(_shape, static_cast<int32>(anim));
+		for (unsigned int i = 0; i < action->getSize(); i++) {
+			const AnimFrame &frame = action->getFrame(dir, i);
+			if (frame.is_cruattack()) {
+				if (!first_offsets) {
+					xoff = frame.cru_attackx();
+					yoff = frame.cru_attacky();
+					zoff = frame.cru_attackz();
+					first_offsets = true;
+				} else {
+					xoff2 = frame.cru_attackx();
+					yoff2 = frame.cru_attacky();
+					zoff2 = frame.cru_attackz();
+					other_offsets = true;
+					break;
+				}
+			}
+		}
 	}
 
-	int32 cx, cy, cz;
-	getCentre(cx, cy, cz);
+	int32 x, y, z;
+	getLocation(x, y, z);
 
 	int32 ox, oy, oz;
 	other->getLocation(ox, oy, oz);
@@ -1341,37 +1358,70 @@ uint16 Item::fireDistance(Item *other, Direction dir, int16 xoff, int16 yoff, in
 	CurrentMap *cm = World::get_instance()->getCurrentMap();
 	if (!cm)
 		return 0;
-	const Item *blocker = nullptr;
-	bool valid = cm->isValidPosition(cx, cy, cz, BULLET_SPLASH_SHAPE,
-									 getObjId(), nullptr, nullptr, &blocker);
-	if (!valid) {
-		if (blocker->getObjId() == other->getObjId())
-			dist = MAX(abs(_x - ox), abs(_y - oy));
-	} else {
-		int32 ocx, ocy, ocz;
-		other->getCentre(ocx, ocy, ocz);
-		const int32 start[3] = {cx, cy, cz};
-		const int32 end[3] = {ocx, ocy, cz};
-		const int32 dims[3] = { 2, 2, 2 };
-
-		Std::list<CurrentMap::SweepItem> collisions;
-		Std::list<CurrentMap::SweepItem>::iterator it;
-		cm->sweepTest(start, end, dims, ShapeInfo::SI_SOLID,
-					   _objId, false, &collisions);
-		for (it = collisions.begin(); it != collisions.end(); it++) {
-			if (it->_item == getObjId())
-				continue;
-			if (it->_item != other->getObjId())
+
+	for (int i = 0; i < (other_offsets ? 2 : 1) && dist == 0; i++) {
+		int32 cx = x + (i == 0 ? xoff : xoff2);
+		int32 cy = y + (i == 0 ? yoff : yoff2);
+		int32 cz = z + (i == 0 ? zoff : zoff2);
+		const Item *blocker = nullptr;
+		bool valid = cm->isValidPosition(cx, cy, cz, BULLET_SPLASH_SHAPE,
+										 getObjId(), nullptr, nullptr, &blocker);
+		if (!valid) {
+			if (blocker->getObjId() == other->getObjId())
+				dist = MAX(abs(_x - ox), abs(_y - oy));
+		} else {
+			int32 ocx, ocy, ocz;
+			other->getCentre(ocx, ocy, ocz);
+			ocz = other->getTargetZRelativeToAttackerZ(getZ());
+			const int32 start[3] = {cx, cy, cz};
+			const int32 end[3] = {ocx, ocy, ocz};
+			const int32 dims[3] = {2, 2, 2};
+
+			Std::list<CurrentMap::SweepItem> collisions;
+			Std::list<CurrentMap::SweepItem>::iterator it;
+			cm->sweepTest(start, end, dims, ShapeInfo::SI_SOLID,
+						   _objId, false, &collisions);
+			for (it = collisions.begin(); it != collisions.end(); it++) {
+				if (it->_item == getObjId())
+					continue;
+				if (it->_item != other->getObjId())
+					break;
+				int32 out[3];
+				it->GetInterpolatedCoords(out, start, end);
+				dist = MAX(abs(_x - out[0]), abs(_y - out[1]));
 				break;
-			int32 out[3];
-			it->GetInterpolatedCoords(out, start, end);
-			dist = MAX(abs(_x - out[0]), abs(_y - out[1]));
-			break;
+			}
 		}
 	}
 	return dist / 32;
 }
 
+int32 Item::getTargetZRelativeToAttackerZ(int32 otherz) {
+	int32 tsx, tsy, tsz;
+	getFootpadData(tsx, tsy, tsz);
+
+	int32 tz = getZ() + tsz * 8;
+
+	if (tsz < 3) {
+		if (tsz)
+			tz -= 8;
+	} else {
+		int32 targetz = tz;
+		tz -= 16;
+		if (otherz - targetz < -0x2f) {
+			tz += 8;
+		} else if (otherz - targetz > 0x2f) {
+			if (tsz == 6) {
+				tz -= 16;
+			} else if (tsz >= 7) {
+				tz -= 24;
+			}
+		}
+	}
+	return tz;
+}
+
+
 unsigned int Item::countNearby(uint32 shape, uint16 range) {
 	CurrentMap *currentmap = World::get_instance()->getCurrentMap();
 	UCList itemlist(2);
diff --git a/engines/ultima/ultima8/world/item.h b/engines/ultima/ultima8/world/item.h
index eeba0b5314..2b7c74a4b8 100644
--- a/engines/ultima/ultima8/world/item.h
+++ b/engines/ultima/ultima8/world/item.h
@@ -388,9 +388,10 @@ public:
 	virtual void receiveHit(ObjId other, Direction dir, int damage, uint16 type);
 
 	//! fire the given weapon type in the given direction from location x, y, z.
-	uint16 fireWeapon(int32 x, int32 y, int32 z, Direction dir, int firetype, char someflag);
+	uint16 fireWeapon(int32 x, int32 y, int32 z, Direction dir, int firetype, char findtarget);
 
-	//! get the distance (in map tiles) if we were to fire in this direction
+	//! get the distance (in map tiles) if we were to fire in this direction to "other"
+	//! and could hit, otherwise return 0.
 	uint16 fireDistance(Item *other, Direction dir, int16 xoff, int16 yoff, int16 zoff);
 
 	//! get damage points, used in Crusader for item damage.
@@ -656,6 +657,10 @@ private:
 	//! The Crusader version of receiveHit
 	void receiveHitCru(ObjId other, Direction dir, int damage, uint16 type);
 
+	//! Get the right Z which an attacker should aim for, given the attacker's z.
+	//! (Crusader only)
+	int32 getTargetZRelativeToAttackerZ(int32 attackerz);
+
 public:
 	enum statusflags {
 		FLG_DISPOSABLE   = 0x0002,  //!< Item is discarded on map change
diff --git a/engines/ultima/ultima8/world/super_sprite_process.cpp b/engines/ultima/ultima8/world/super_sprite_process.cpp
index cfeeefb7ce..91dccd334c 100644
--- a/engines/ultima/ultima8/world/super_sprite_process.cpp
+++ b/engines/ultima/ultima8/world/super_sprite_process.cpp
@@ -57,7 +57,7 @@ SuperSpriteProcess::SuperSpriteProcess() : Process(),
 SuperSpriteProcess::SuperSpriteProcess(int shape, int frame, int sx, int sy, int sz,
 									   int dx, int dy, int dz,
 									   uint16 firetype, uint16 damage, uint16 source,
-									   uint16 target, uint8 spread) :
+									   uint16 target, bool inexact) :
 		_shape(shape), _frame(frame), _startpt(sx, sy, sz), _destpt(dx, dy, dz),
 		_nextpt(sx, sy, sz), _fireType(firetype), _damage(damage), _source(source),
 		_target(target), _counter(1), _item0x77(0), _spriteNo(0),
@@ -67,9 +67,9 @@ SuperSpriteProcess::SuperSpriteProcess(int shape, int frame, int sx, int sy, int
 	const FireType *firetypedat = GameData::get_instance()->getFireType(firetype);
 	assert(firetypedat);
 	if (firetypedat->getAccurate()) {
-		spread = 0;
+		inexact = false;
 	}
-	if (spread) {
+	if (inexact) {
 		int rng = _startpt.maxDistXYZ(_destpt);
 		Item *srcitem = getItem(source);
 		if (srcitem == getControlledActor()) {
diff --git a/engines/ultima/ultima8/world/super_sprite_process.h b/engines/ultima/ultima8/world/super_sprite_process.h
index a59455e2f4..162284b011 100644
--- a/engines/ultima/ultima8/world/super_sprite_process.h
+++ b/engines/ultima/ultima8/world/super_sprite_process.h
@@ -73,10 +73,11 @@ public:
 	//! \param dx Dest X coord of the sprite in the world
 	//! \param dy Dest Y coord of the sprite in the world
 	//! \param dz Dest Z coord of the sprite in the world
+	//! \param inexact true if the destination is not exactly chosen
 	//!
 	SuperSpriteProcess(int shape, int frame, int sx, int sy, int sz,
 					   int dx, int dy, int dz, uint16 firetype,
-					   uint16 damage, uint16 source, uint16 target, uint8 flag);
+					   uint16 damage, uint16 source, uint16 target, bool inexact);
 
 	//! The SuperSpriteProcess destructor
 	~SuperSpriteProcess(void) override;


Commit: bf4f61b35615f628d90e23b6cf558e683d03c549
    https://github.com/scummvm/scummvm/commit/bf4f61b35615f628d90e23b6cf558e683d03c549
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2020-12-24T15:08:51+09:00

Commit Message:
ULTIMA8: Crusader: Fix robot death and make absolute anims a bit cleaner.

Changed paths:
    engines/ultima/ultima8/graphics/anim_dat.cpp
    engines/ultima/ultima8/world/actors/actor.cpp
    engines/ultima/ultima8/world/actors/animation.h
    engines/ultima/ultima8/world/item.cpp


diff --git a/engines/ultima/ultima8/graphics/anim_dat.cpp b/engines/ultima/ultima8/graphics/anim_dat.cpp
index cd8f8730c8..4ced82e13b 100644
--- a/engines/ultima/ultima8/graphics/anim_dat.cpp
+++ b/engines/ultima/ultima8/graphics/anim_dat.cpp
@@ -83,12 +83,12 @@ uint32 AnimDat::getActionNumberForSequence(Animation::Sequence action, const Act
 		//
 		// We also translate based on weapon.  See the function at 1128:2104
 		//
-		// First, if the animation is >= 0x1000 then it's from the usecode -
-		// use directly and don't translate.
+		// First, if the animation includes the Animation::crusaderAbsoluteAnimFlag
+		// bitmask then it's from the usecode - use directly and don't translate.
 		//
 		const uint32 action_int = static_cast<uint32>(action);
-		if (action_int >= 0x1000)
-			return action_int - 0x1000;
+		if (action_int & Animation::crusaderAbsoluteAnimFlag)
+			return action_int - Animation::crusaderAbsoluteAnimFlag;
 
 		switch (action) {
 		case Animation::stand:
@@ -119,7 +119,7 @@ uint32 AnimDat::getActionNumberForSequence(Animation::Sequence action, const Act
 		case Animation::fallBackwards:
 			return 18;
 		case Animation::die:
-			return 20; // maybe? falls over forwards
+			return 18; // by default fall over backwards. TODO: randomly use 20 for some deaths - fall forwards.
 		case Animation::advance:
 			return (smallwpn ? 36 : 44);
 		case Animation::startKneeling:
diff --git a/engines/ultima/ultima8/world/actors/actor.cpp b/engines/ultima/ultima8/world/actors/actor.cpp
index 8e9285503c..270640b7ae 100644
--- a/engines/ultima/ultima8/world/actors/actor.cpp
+++ b/engines/ultima/ultima8/world/actors/actor.cpp
@@ -1128,20 +1128,22 @@ ProcId Actor::die(uint16 damageType) {
 			MusicProcess::get_instance()->queueMusic(98);
 		}
 	} else if (GAME_IS_CRUSADER) {
-		uint16 sfxno;
-		static const uint16 FADING_SCREAM_SFX[] = { 0xD9, 0xDA };
-		static const uint16 MALE_DEATH_SFX[] = { 0x88, 0x8C, 0x8F };
-		static const uint16 FEMALE_DEATH_SFX[] = { 0xD8, 0x10 };
-		if (damageType == 0xf) {
-			sfxno = FADING_SCREAM_SFX[getRandom() % 2];
-		} else {
-			if (hasExtFlags(EXT_FEMALE)) {
-				sfxno = FEMALE_DEATH_SFX[getRandom() % 2];
+		if (!isRobotCru()) {
+			uint16 sfxno;
+			static const uint16 FADING_SCREAM_SFX[] = { 0xD9, 0xDA };
+			static const uint16 MALE_DEATH_SFX[] = { 0x88, 0x8C, 0x8F };
+			static const uint16 FEMALE_DEATH_SFX[] = { 0xD8, 0x10 };
+			if (damageType == 0xf) {
+				sfxno = FADING_SCREAM_SFX[getRandom() % 2];
 			} else {
-				sfxno = MALE_DEATH_SFX[getRandom() % 3];
+				if (hasExtFlags(EXT_FEMALE)) {
+					sfxno = FEMALE_DEATH_SFX[getRandom() % 2];
+				} else {
+					sfxno = MALE_DEATH_SFX[getRandom() % 3];
+				}
 			}
+			AudioProcess::get_instance()->playSFX(sfxno, 0x10, _objId, 0, true);
 		}
-		AudioProcess::get_instance()->playSFX(sfxno, 0x10, _objId, 0, true);
 	}
 
 	destroyContents();
@@ -1680,11 +1682,11 @@ uint32 Actor::I_doAnim(const uint8 *args, unsigned int /*argsize*/) {
 
 	//
 	// HACK: In Crusader, we do translation on the animations so we want to remap
-	// most of them, but for direct commands from the usecode we add 0x1000 for
+	// most of them, but for direct commands from the usecode we add a bitflag for
 	// no remapping
 	//
 	if (GAME_IS_CRUSADER) {
-		anim += 0x1000;
+		anim |= Animation::crusaderAbsoluteAnimFlag;
 	}
 
 	return actor->doAnim(static_cast<Animation::Sequence>(anim), Direction_FromUsecodeDir(dir));
diff --git a/engines/ultima/ultima8/world/actors/animation.h b/engines/ultima/ultima8/world/actors/animation.h
index 739cc36a7b..3469287b73 100644
--- a/engines/ultima/ultima8/world/actors/animation.h
+++ b/engines/ultima/ultima8/world/actors/animation.h
@@ -136,8 +136,10 @@ enum Sequence {
 	slowCombatRollLeft = 61,
 	slowCombatRollRight = 62,
 	finishFiring = 63,
-	teleportInReplacement = 0x1020,	//!< See notes in Actor::receiveHitCru
-	teleportOutReplacement = 0x1021	//!< See notes in Actor::receiveHitCru
+
+	crusaderAbsoluteAnimFlag = 0x1000, //!< Bit mask magic to say we want an exact number, don't do mapping from U8 animation numbers
+	teleportInReplacement = crusaderAbsoluteAnimFlag | teleportIn,	//!< See notes in Actor::receiveHitCru
+	teleportOutReplacement = crusaderAbsoluteAnimFlag | teleportOut	//!< See notes in Actor::receiveHitCru
 };
 
 enum Result {
diff --git a/engines/ultima/ultima8/world/item.cpp b/engines/ultima/ultima8/world/item.cpp
index 09421a4c87..37cdad8185 100644
--- a/engines/ultima/ultima8/world/item.cpp
+++ b/engines/ultima/ultima8/world/item.cpp
@@ -1219,8 +1219,8 @@ uint16 Item::fireWeapon(int32 x, int32 y, int32 z, Direction dir, int firetype,
 		DirectionMode dirmode = dirmode_8dirs;
 		const Actor *thisactor = dynamic_cast<Actor *>(this);
 		if (thisactor) {
-			// TODO: Get damage for active inventory item, and dirmode of last
-			// animation here (lines 185~208 of disasm)
+			// TODO: Get damage for active inventory item
+			dirmode = thisactor->animDirMode(thisactor->getLastAnim());
 		}
 
 		Item *target = nullptr;
@@ -1228,10 +1228,6 @@ uint16 Item::fireWeapon(int32 x, int32 y, int32 z, Direction dir, int firetype,
 			if (this != getControlledActor()) {
 				target = getControlledActor();
 			} else {
-				// TODO: this should be dirmode of last animation (above)
-				// for now, hack for avatar
-				if (thisactor->getShape() == 1 && thisactor->isInCombat())
-					dirmode = dirmode_16dirs;
 				target = currentmap->findBestTargetItem(ix, iy, dir, dirmode);
 			}
 		}


Commit: 51c8f66d97cfeaeb6e28c44f027b6d97dc58cd75
    https://github.com/scummvm/scummvm/commit/51c8f66d97cfeaeb6e28c44f027b6d97dc58cd75
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2020-12-24T19:02:25+09:00

Commit Message:
ULTIMA8: Provide quick helper functions for mouse

Changed paths:
    engines/ultima/ultima8/kernel/mouse.cpp
    engines/ultima/ultima8/kernel/mouse.h


diff --git a/engines/ultima/ultima8/kernel/mouse.cpp b/engines/ultima/ultima8/kernel/mouse.cpp
index cd0ff7df30..eb9997592b 100644
--- a/engines/ultima/ultima8/kernel/mouse.cpp
+++ b/engines/ultima/ultima8/kernel/mouse.cpp
@@ -139,7 +139,7 @@ bool Mouse::isMouseDownEvent(Shared::MouseButton button) const {
 	return _mouseButton[button].isState(MBS_DOWN);
 }
 
-int Mouse::getMouseLength(int mx, int my) {
+int Mouse::getMouseLength(int mx, int my) const {
 	Rect dims;
 	RenderSurface *screen = Ultima8Engine::get_instance()->getRenderScreen();
 	screen->GetSurfaceDims(dims);
@@ -171,7 +171,7 @@ int Mouse::getMouseLength(int mx, int my) {
 	}
 }
 
-Direction Mouse::getMouseDirectionWorld(int mx, int my) {
+Direction Mouse::getMouseDirectionWorld(int mx, int my) const {
 	Rect dims;
 	RenderSurface *screen = Ultima8Engine::get_instance()->getRenderScreen();
 	screen->GetSurfaceDims(dims);
@@ -183,7 +183,7 @@ Direction Mouse::getMouseDirectionWorld(int mx, int my) {
 	return Direction_Get(dy * 2, dx, dirmode_8dirs);
 }
 
-Direction Mouse::getMouseDirectionScreen(int mx, int my) {
+Direction Mouse::getMouseDirectionScreen(int mx, int my) const {
 	return Direction_OneRight(getMouseDirectionWorld(mx, my), dirmode_8dirs);
 }
 
@@ -222,7 +222,7 @@ int Mouse::getMouseFrame() {
 		}
 
 		// Calculate frame based on direction
-		Direction mousedir = getMouseDirectionScreen(_mousePos.x, _mousePos.y);
+		Direction mousedir = getMouseDirectionScreen();
 		int frame = mouseFrameForDir(mousedir);
 
 		/** length --- frame offset
@@ -231,7 +231,7 @@ int Mouse::getMouseFrame() {
 		 *    2             16
 		 *  combat          25
 		 **/
-		int offset = getMouseLength(_mousePos.x, _mousePos.y) * 8;
+		int offset = getMouseLength() * 8;
 		if (combat && offset != 16) //combat mouse is off if running
 			offset = 25;
 		return frame + offset;
diff --git a/engines/ultima/ultima8/kernel/mouse.h b/engines/ultima/ultima8/kernel/mouse.h
index d86946e731..28d8427a9c 100644
--- a/engines/ultima/ultima8/kernel/mouse.h
+++ b/engines/ultima/ultima8/kernel/mouse.h
@@ -150,13 +150,28 @@ public:
 	bool buttonUp(Shared::MouseButton button);
 
 	//! get mouse cursor length. 0 = short, 1 = medium, 2 = long
-	int getMouseLength(int mx, int my);
+	int getMouseLength(int mx, int my) const;
+
+	//! get mouse cursor length for the current coordinates
+	int getMouseLength() const {
+		return getMouseLength(_mousePos.x, _mousePos.y);
+	}
 
 	//! get mouse cursor direction on the screen. 0 = up, 1 = up-right, 2 = right, etc...
-	Direction getMouseDirectionScreen(int mx, int my);
+	Direction getMouseDirectionScreen(int mx, int my) const;
+
+	//! get mouse cursor direction on the screen using the current coordinates.
+	Direction getMouseDirectionScreen() const {
+		return getMouseDirectionScreen(_mousePos.x, _mousePos.y);
+	}
 
 	//! get mouse cursor direction in the world. 0 = up, 1 = up-right, 2 = right, etc...
-	Direction getMouseDirectionWorld(int mx, int my);
+	Direction getMouseDirectionWorld(int mx, int my) const;
+
+	//! get mouse cursor direction in the world using the current coordinates.
+	Direction getMouseDirectionWorld() const {
+		return getMouseDirectionWorld(_mousePos.x, _mousePos.y);
+	}
 
 	//! get current mouse cursor location
 	void getMouseCoords(int32 &mx, int32 &my) const {


Commit: 404371365a81262f9af8796b80301f6ed8b3c5ab
    https://github.com/scummvm/scummvm/commit/404371365a81262f9af8796b80301f6ed8b3c5ab
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2020-12-24T19:15:58+09:00

Commit Message:
ULTIMA8: Reduce code duplication in mover procs

Changed paths:
    engines/ultima/ultima8/world/actors/avatar_mover_process.cpp
    engines/ultima/ultima8/world/actors/avatar_mover_process.h
    engines/ultima/ultima8/world/actors/cru_avatar_mover_process.cpp
    engines/ultima/ultima8/world/actors/u8_avatar_mover_process.cpp


diff --git a/engines/ultima/ultima8/world/actors/avatar_mover_process.cpp b/engines/ultima/ultima8/world/actors/avatar_mover_process.cpp
index 195fa8d048..a49ee53ff8 100644
--- a/engines/ultima/ultima8/world/actors/avatar_mover_process.cpp
+++ b/engines/ultima/ultima8/world/actors/avatar_mover_process.cpp
@@ -86,25 +86,6 @@ void AvatarMoverProcess::run() {
 }
 
 
-void AvatarMoverProcess::tryAttack() {
-	MainActor *avatar = getMainActor();
-	Direction dir = avatar->getDir();
-	if (!avatar->isInCombat()) {
-		avatar->setInCombat(0);
-		waitFor(avatar->doAnim(Animation::readyWeapon, dir));
-	} else {
-		if (canAttack()) {
-			waitFor(avatar->doAnim(Animation::attack, dir));
-			if (GAME_IS_CRUSADER) {
-				// FIXME: put some real values in here.
-				int32 xs, ys, zs;
-				avatar->getFootpadWorld(xs, ys, zs);
-				avatar->fireWeapon(xs / 2, ys / 2, zs / 2, dir, 1, 1);
-			}
-		}
-	}
-}
-
 bool AvatarMoverProcess::checkTurn(Direction direction, bool moving) {
 	MainActor *avatar = getMainActor();
 	Direction curdir = avatar->getDir();
@@ -143,6 +124,66 @@ void AvatarMoverProcess::turnToDirection(Direction direction) {
 		waitFor(turnpid);
 }
 
+void AvatarMoverProcess::slowFromRun(Direction direction) {
+	MainActor *avatar = getMainActor();
+	ProcId walkpid = avatar->doAnim(Animation::walk, direction);
+	ProcId standpid = avatar->doAnim(Animation::stand, direction);
+	Process *standproc = Kernel::get_instance()->getProcess(standpid);
+	standproc->waitFor(walkpid);
+	waitFor(standpid);
+}
+
+void AvatarMoverProcess::putAwayWeapon(Direction direction) {
+	MainActor *avatar = getMainActor();
+	ProcId anim1 = avatar->doAnim(Animation::unreadyWeapon, direction);
+	ProcId anim2 = avatar->doAnim(Animation::stand, direction);
+	Process *anim2p = Kernel::get_instance()->getProcess(anim2);
+	anim2p->waitFor(anim1);
+	waitFor(anim2);
+}
+
+bool AvatarMoverProcess::standUpIfNeeded(Direction direction) {
+	MainActor *avatar = getMainActor();
+	Animation::Sequence lastanim = avatar->getLastAnim();
+	bool stasis = Ultima8Engine::get_instance()->isAvatarInStasis();
+
+	if (lastanim == Animation::die || lastanim == Animation::fallBackwards) {
+		if (!stasis) {
+			waitFor(avatar->doAnim(Animation::standUp, direction));
+		}
+		return true;
+	}
+	return false;
+}
+
+void AvatarMoverProcess::getMovementFlagAxes(int &x, int &y) {
+	y = 0;
+	x = 0;
+	if (hasMovementFlags(MOVE_UP)) {
+		y++;
+	}
+	if (hasMovementFlags(MOVE_DOWN)) {
+		y--;
+	}
+	if (hasMovementFlags(MOVE_LEFT)) {
+		x--;
+	}
+	if (hasMovementFlags(MOVE_RIGHT)) {
+		x++;
+	}
+}
+
+Direction AvatarMoverProcess::getTurnDirForTurnFlags(Direction direction, DirectionMode dirmode) {
+	if (hasMovementFlags(MOVE_TURN_LEFT)) {
+		direction = Direction_OneLeft(direction, dirmode);
+	}
+
+	if (hasMovementFlags(MOVE_TURN_RIGHT)) {
+		direction = Direction_OneRight(direction, dirmode);
+	}
+	return direction;
+}
+
 void AvatarMoverProcess::onMouseDown(int button, int32 mx, int32 my) {
 	int bid = 0;
 
diff --git a/engines/ultima/ultima8/world/actors/avatar_mover_process.h b/engines/ultima/ultima8/world/actors/avatar_mover_process.h
index 1d4dd019d2..cb523d30e9 100644
--- a/engines/ultima/ultima8/world/actors/avatar_mover_process.h
+++ b/engines/ultima/ultima8/world/actors/avatar_mover_process.h
@@ -94,6 +94,22 @@ protected:
 	void turnToDirection(Direction direction);
 	bool checkTurn(Direction direction, bool moving);
 
+	// Walk and then stop in the given direction
+	void slowFromRun(Direction direction);
+
+	// Stow weapon and stand
+	void putAwayWeapon(Direction direction);
+
+	// If the last animation was falling or die but we're not dead, stand up!
+	// return true if we are waiting to get up
+	bool standUpIfNeeded(Direction direction);
+
+	// Get directions based on what movement flags are set, eg y=+1 for up, x=-1 for left.
+	void getMovementFlagAxes(int &x, int &y);
+
+	// Adjust the direction based on the current turn flags
+	Direction getTurnDirForTurnFlags(Direction direction, DirectionMode dirmode);
+
 	uint32 _lastFrame;
 
 	// attack speed limiting
diff --git a/engines/ultima/ultima8/world/actors/cru_avatar_mover_process.cpp b/engines/ultima/ultima8/world/actors/cru_avatar_mover_process.cpp
index b96306468c..d76e1fd242 100644
--- a/engines/ultima/ultima8/world/actors/cru_avatar_mover_process.cpp
+++ b/engines/ultima/ultima8/world/actors/cru_avatar_mover_process.cpp
@@ -56,20 +56,16 @@ void CruAvatarMoverProcess::handleHangingMode() {
 
 void CruAvatarMoverProcess::handleCombatMode() {
 	MainActor *avatar = getMainActor();
-	Animation::Sequence lastanim = avatar->getLastAnim();
+	const Animation::Sequence lastanim = avatar->getLastAnim();
 	Direction direction = avatar->getDir();
-	bool stasis = Ultima8Engine::get_instance()->isAvatarInStasis();
+	const bool stasis = Ultima8Engine::get_instance()->isAvatarInStasis();
 
 	// never idle when in combat
 	_idleTime = 0;
 
 	// If Avatar has fallen down, stand up
-	if (lastanim == Animation::die || lastanim == Animation::fallBackwards) {
-		if (!stasis) {
-			waitFor(avatar->doAnim(Animation::standUp, direction));
-		}
+	if (standUpIfNeeded(direction))
 		return;
-	}
 
 	// can't do any new actions if in stasis
 	if (stasis)
@@ -77,17 +73,9 @@ void CruAvatarMoverProcess::handleCombatMode() {
 
 	bool moving = (lastanim == Animation::advance || lastanim == Animation::retreat);
 
-	DirectionMode dirmode = avatar->animDirMode(Animation::combatStand);
-
 	//  if we are trying to move, allow change direction only after move occurs to avoid spinning
 	if (moving || !hasMovementFlags(MOVE_FORWARD | MOVE_BACK)) {
-		if (hasMovementFlags(MOVE_TURN_LEFT)) {
-			direction = Direction_OneLeft(direction, dirmode);
-		}
-
-		if (hasMovementFlags(MOVE_TURN_RIGHT)) {
-			direction = Direction_OneRight(direction, dirmode);
-		}
+		direction = getTurnDirForTurnFlags(direction, avatar->animDirMode(Animation::combatStand));
 	}
 
 	if (hasMovementFlags(MOVE_FORWARD)) {
@@ -117,20 +105,8 @@ void CruAvatarMoverProcess::handleCombatMode() {
 		return;
 	}
 
-	int y = 0;
-	int x = 0;
-	if (hasMovementFlags(MOVE_UP)) {
-		y++;
-	}
-	if (hasMovementFlags(MOVE_DOWN)) {
-		y--;
-	}
-	if (hasMovementFlags(MOVE_LEFT)) {
-		x--;
-	}
-	if (hasMovementFlags(MOVE_RIGHT)) {
-		x++;
-	}
+	int x, y;
+	getMovementFlagAxes(x, y);
 
 	if (x != 0 || y != 0) {
 		Direction nextdir = Direction_Get(y, x, dirmode_8dirs);
@@ -138,7 +114,7 @@ void CruAvatarMoverProcess::handleCombatMode() {
 		if (checkTurn(nextdir, true))
 			return;
 
-		Animation::Sequence nextanim;
+		Animation::Sequence nextanim = Animation::combatStand;
 		if (lastanim == Animation::run) {
 			// want to run while in combat mode?
 			// first sheath weapon
@@ -164,18 +140,16 @@ void CruAvatarMoverProcess::handleCombatMode() {
 
 	// not doing anything in particular? stand
 	if (lastanim != Animation::combatStand) {
-		Animation::Sequence nextanim = Animation::combatStand;
-		nextanim = Animation::checkWeapon(nextanim, lastanim);
+		Animation::Sequence nextanim = Animation::checkWeapon(Animation::combatStand, lastanim);
 		waitFor(avatar->doAnim(nextanim, direction));
 	}
 }
 
 void CruAvatarMoverProcess::handleNormalMode() {
-	Ultima8Engine *guiapp = Ultima8Engine::get_instance();
 	MainActor *avatar = getMainActor();
-	Animation::Sequence lastanim = avatar->getLastAnim();
+	const Animation::Sequence lastanim = avatar->getLastAnim();
 	Direction direction = avatar->getDir();
-	bool stasis = guiapp->isAvatarInStasis();
+	const bool stasis = Ultima8Engine::get_instance()->isAvatarInStasis();
 
 	// Store current idle time. (Also see end of function.)
 	uint32 currentIdleTime = _idleTime;
@@ -187,36 +161,21 @@ void CruAvatarMoverProcess::handleNormalMode() {
 		avatar->toggleInCombat();
 	}
 
-	// If Avatar has fallen down do nothing
-	if (lastanim == Animation::die || lastanim == Animation::fallBackwards) {
-		if (!stasis) {
-			waitFor(avatar->doAnim(Animation::standUp, direction));
-		}
+	// If Avatar has fallen down and not dead, get up!
+	if (standUpIfNeeded(direction))
 		return;
-	}
 
 	// If still in combat stance, sheathe weapon
 	if (!stasis && Animation::isCombatAnim(lastanim)) {
-		ProcId anim1 = avatar->doAnim(Animation::unreadyWeapon, direction);
-		ProcId anim2 = avatar->doAnim(Animation::stand, direction);
-		Process *anim2p = Kernel::get_instance()->getProcess(anim2);
-		anim2p->waitFor(anim1);
-		waitFor(anim2);
-
+		putAwayWeapon(direction);
 		return;
 	}
 
-	if (!hasMovementFlags(MOVE_ANY_DIRECTION)) {
+	if (!hasMovementFlags(MOVE_ANY_DIRECTION) && lastanim == Animation::run) {
 		// if we were running, slow to a walk before stopping
 		// (even in stasis)
-		if (lastanim == Animation::run) {
-			ProcId walkpid = avatar->doAnim(Animation::walk, direction);
-			ProcId standpid = avatar->doAnim(Animation::stand, direction);
-			Process *standproc = Kernel::get_instance()->getProcess(standpid);
-			standproc->waitFor(walkpid);
-			waitFor(standpid);
-			return;
-		}
+		slowFromRun(direction);
+		return;
 	}
 
 	// can't do any new actions if in stasis
@@ -260,13 +219,7 @@ void CruAvatarMoverProcess::handleNormalMode() {
 
 	//  if we are trying to move, allow change direction only after move occurs to avoid spinning
 	if (moving || !hasMovementFlags(MOVE_FORWARD | MOVE_BACK)) {
-		if (hasMovementFlags(MOVE_TURN_LEFT)) {
-			direction = Direction_OneLeft(direction, dirmode);
-		}
-
-		if (hasMovementFlags(MOVE_TURN_RIGHT)) {
-			direction = Direction_OneRight(direction, dirmode);
-		}
+		direction = getTurnDirForTurnFlags(direction, dirmode);
 	}
 
 	Animation::Sequence nextanim = Animation::walk;
@@ -295,20 +248,8 @@ void CruAvatarMoverProcess::handleNormalMode() {
 		return;
 	}
 
-	int y = 0;
-	int x = 0;
-	if (hasMovementFlags(MOVE_UP)) {
-		y++;
-	}
-	if (hasMovementFlags(MOVE_DOWN)) {
-		y--;
-	}
-	if (hasMovementFlags(MOVE_LEFT)) {
-		x--;
-	}
-	if (hasMovementFlags(MOVE_RIGHT)) {
-		x++;
-	}
+	int x, y;
+	getMovementFlagAxes(x, y);
 
 	if (x != 0 || y != 0) {
 		direction = Direction_Get(y, x, dirmode_8dirs);
diff --git a/engines/ultima/ultima8/world/actors/u8_avatar_mover_process.cpp b/engines/ultima/ultima8/world/actors/u8_avatar_mover_process.cpp
index 5e6ffdc7d3..6c330a8a1b 100644
--- a/engines/ultima/ultima8/world/actors/u8_avatar_mover_process.cpp
+++ b/engines/ultima/ultima8/world/actors/u8_avatar_mover_process.cpp
@@ -94,21 +94,15 @@ void U8AvatarMoverProcess::handleCombatMode() {
 	Direction direction = avatar->getDir();
 	bool stasis = Ultima8Engine::get_instance()->isAvatarInStasis();
 
-	int32 mx, my;
-	mouse->getMouseCoords(mx, my);
-	unsigned int mouselength = mouse->getMouseLength(mx, my);
-
-	Direction mousedir = mouse->getMouseDirectionWorld(mx, my);
+	unsigned int mouselength = mouse->getMouseLength();
+	Direction mousedir = mouse->getMouseDirectionWorld();
 
 	// never idle when in combat
 	_idleTime = 0;
 
 	// If Avatar has fallen down, stand up.
-	if (lastanim == Animation::die || lastanim == Animation::fallBackwards) {
-		if (!stasis)
-			waitFor(avatar->doAnim(Animation::standUp, mousedir));
+	if (standUpIfNeeded(direction))
 		return;
-	}
 
 	// if we were blocking, and no longer holding the mouse, stop
 	if (lastanim == Animation::startBlock &&
@@ -182,6 +176,8 @@ void U8AvatarMoverProcess::handleCombatMode() {
 		_mouseButton[1]._lastDown = 0;
 
 		Gump *desktopgump = Ultima8Engine::get_instance()->getDesktopGump();
+		int32 mx, my;
+		mouse->getMouseCoords(mx, my);
 		if (desktopgump->TraceObjId(mx, my) == 1) {
 			// double right click on avatar = toggle combat mode
 			avatar->toggleInCombat();
@@ -218,7 +214,6 @@ void U8AvatarMoverProcess::handleCombatMode() {
 		//!! TODO: check if you can actually take this step
 		Direction nextdir = mousedir;
 		Animation::Sequence nextanim;
-
 		if (lastanim == Animation::run) {
 			// want to run while in combat mode?
 			// first sheath weapon
@@ -250,17 +245,9 @@ void U8AvatarMoverProcess::handleCombatMode() {
 
 	bool moving = (lastanim == Animation::advance || lastanim == Animation::retreat);
 
-	DirectionMode dirmode = avatar->animDirMode(Animation::combatStand);
-
 	//  if we are trying to move, allow change direction only after move occurs to avoid spinning
 	if (moving || !hasMovementFlags(MOVE_FORWARD | MOVE_BACK)) {
-		if (hasMovementFlags(MOVE_TURN_LEFT)) {
-			direction = Direction_OneLeft(direction, dirmode);
-		}
-
-		if (hasMovementFlags(MOVE_TURN_RIGHT)) {
-			direction = Direction_OneRight(direction, dirmode);
-		}
+		direction = getTurnDirForTurnFlags(direction, avatar->animDirMode(Animation::combatStand));
 	}
 
 	if (hasMovementFlags(MOVE_FORWARD)) {
@@ -290,20 +277,8 @@ void U8AvatarMoverProcess::handleCombatMode() {
 		return;
 	}
 
-	int y = 0;
-	int x = 0;
-	if (hasMovementFlags(MOVE_UP)) {
-		y++;
-	}
-	if (hasMovementFlags(MOVE_DOWN)) {
-		y--;
-	}
-	if (hasMovementFlags(MOVE_LEFT)) {
-		x--;
-	}
-	if (hasMovementFlags(MOVE_RIGHT)) {
-		x++;
-	}
+	int x, y;
+	getMovementFlagAxes(x, y);
 
 	if (x != 0 || y != 0) {
 		Direction nextdir = Direction_Get(y, x, dirmode_8dirs);
@@ -342,25 +317,21 @@ void U8AvatarMoverProcess::handleCombatMode() {
 	// not doing anything in particular? stand
 	// TODO: make sure falling works properly.
 	if (lastanim != Animation::combatStand) {
-		Animation::Sequence nextanim = Animation::combatStand;
-		nextanim = Animation::checkWeapon(nextanim, lastanim);
+		Animation::Sequence nextanim = Animation::checkWeapon(Animation::combatStand, lastanim);
 		waitFor(avatar->doAnim(nextanim, direction));
 	}
 }
 
 void U8AvatarMoverProcess::handleNormalMode() {
-	Ultima8Engine *guiapp = Ultima8Engine::get_instance();
-	Mouse *mouse = Mouse::get_instance();
+	const Mouse *mouse = Mouse::get_instance();
 	MainActor *avatar = getMainActor();
 	Animation::Sequence lastanim = avatar->getLastAnim();
 	Direction direction = avatar->getDir();
-	bool stasis = guiapp->isAvatarInStasis();
+	bool stasis = Ultima8Engine::get_instance()->isAvatarInStasis();
 	bool combatRun = avatar->hasActorFlags(Actor::ACT_COMBATRUN);
 
-	int32 mx, my;
-	mouse->getMouseCoords(mx, my);
-	unsigned int mouselength = mouse->getMouseLength(mx, my);
-	Direction mousedir = mouse->getMouseDirectionWorld(mx, my);
+	unsigned int mouselength = mouse->getMouseLength();
+	Direction mousedir = mouse->getMouseDirectionWorld();
 
 	// Store current idle time. (Also see end of function.)
 	uint32 currentIdleTime = _idleTime;
@@ -373,21 +344,12 @@ void U8AvatarMoverProcess::handleNormalMode() {
 	}
 
 	// If Avatar has fallen down, stand up.
-	if (lastanim == Animation::die || lastanim == Animation::fallBackwards) {
-		if (!stasis) {
-			waitFor(avatar->doAnim(Animation::standUp, direction));
-		}
+	if (standUpIfNeeded(direction))
 		return;
-	}
 
 	// If still in combat stance, sheathe weapon
 	if (!stasis && Animation::isCombatAnim(lastanim)) {
-		ProcId anim1 = avatar->doAnim(Animation::unreadyWeapon, direction);
-		ProcId anim2 = avatar->doAnim(Animation::stand, direction);
-		Process *anim2p = Kernel::get_instance()->getProcess(anim2);
-		anim2p->waitFor(anim1);
-		waitFor(anim2);
-
+		putAwayWeapon(direction);
 		return;
 	}
 
@@ -443,11 +405,7 @@ void U8AvatarMoverProcess::handleNormalMode() {
 		// if we were running, slow to a walk before stopping
 		// (even in stasis)
 		if (lastanim == Animation::run) {
-			ProcId walkpid = avatar->doAnim(Animation::walk, direction);
-			ProcId standpid = avatar->doAnim(Animation::stand, direction);
-			Process *standproc = Kernel::get_instance()->getProcess(standpid);
-			standproc->waitFor(walkpid);
-			waitFor(standpid);
+			slowFromRun(direction);
 			return;
 		}
 
@@ -487,6 +445,8 @@ void U8AvatarMoverProcess::handleNormalMode() {
 
 	if (_mouseButton[1].isUnhandledDoubleClick()) {
 		Gump *desktopgump = Ultima8Engine::get_instance()->getDesktopGump();
+		int32 mx, my;
+		mouse->getMouseCoords(mx, my);
 		if (desktopgump->TraceObjId(mx, my) == 1) {
 			// double right click on avatar = toggle combat mode
 			_mouseButton[1].setState(MBS_HANDLED);
@@ -583,17 +543,9 @@ void U8AvatarMoverProcess::handleNormalMode() {
 
 	bool moving = (lastanim == Animation::step || lastanim == Animation::run || lastanim == Animation::walk);
 
-	DirectionMode dirmode = avatar->animDirMode(Animation::step);
-
 	//  if we are trying to move, allow change direction only after move occurs to avoid spinning
 	if (moving || !hasMovementFlags(MOVE_FORWARD | MOVE_BACK)) {
-		if (hasMovementFlags(MOVE_TURN_LEFT)) {
-			direction = Direction_OneLeft(direction, dirmode);
-		}
-
-		if (hasMovementFlags(MOVE_TURN_RIGHT)) {
-			direction = Direction_OneRight(direction, dirmode);
-		}
+		direction = getTurnDirForTurnFlags(direction, avatar->animDirMode(Animation::step));
 	}
 
 	Animation::Sequence nextanim = Animation::walk;
@@ -622,20 +574,8 @@ void U8AvatarMoverProcess::handleNormalMode() {
 		return;
 	}
 
-	int y = 0;
-	int x = 0;
-	if (hasMovementFlags(MOVE_UP)) {
-		y++;
-	}
-	if (hasMovementFlags(MOVE_DOWN)) {
-		y--;
-	}
-	if (hasMovementFlags(MOVE_LEFT)) {
-		x--;
-	}
-	if (hasMovementFlags(MOVE_RIGHT)) {
-		x++;
-	}
+	int x, y;
+	getMovementFlagAxes(x, y);
 
 	if (x != 0 || y != 0) {
 		direction = Direction_Get(y, x, dirmode_8dirs);
@@ -695,9 +635,7 @@ void U8AvatarMoverProcess::step(Animation::Sequence action, Direction direction,
 
 	MainActor *avatar = getMainActor();
 	Animation::Sequence lastanim = avatar->getLastAnim();
-
 	Animation::Result res = avatar->tryAnim(action, direction);
-
 	Direction stepdir = direction;
 
 	if (res == Animation::FAILURE ||
@@ -753,7 +691,7 @@ void U8AvatarMoverProcess::step(Animation::Sequence action, Direction direction,
 	if (checkTurn(stepdir, moving))
 		return;
 
-	debug(6, "Step: step ok: action %d dir %d", action, stepdir);
+	//debug(6, "Step: step ok: action %d dir %d", action, stepdir);
 	action = Animation::checkWeapon(action, lastanim);
 	waitFor(avatar->doAnim(action, stepdir));
 }




More information about the Scummvm-git-logs mailing list