[Scummvm-git-logs] scummvm master -> 27dab7b18786ee9b67ad2a3a68dae489acc3c8d8

mduggan mgithub at guarana.org
Fri Jul 3 10:42:56 UTC 2020


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

Summary:
4471a55cd0 ULTIMA8: Increase usage range in crusader
31bc37affa ULTIMA8: Add simpler container search functions
f79b300cdd ULTIMA8: Reduce reticle debug spam a bit
d0ec49a305 ULTIMA8: Add x/y offset as some crusader gumps use them
4abc2b6fd5 ULTIMA8: Add support for Crusader inventory items
f858c03755 ULTIMA8: Give Crusader avatar correct items on startup
07a43d975a ULTIMA8: Fix null pointer usage in reticle process
9d396d6a1b ULTIMA8: Make crusader inventory gumps work
535867a4e8 ULTIMA8: Improve debug messages
27dab7b187 ULTIMA8: Add ability to cycle items for Crusader


Commit: 4471a55cd0c93cab92958e53265f0c3a6481936e
    https://github.com/scummvm/scummvm/commit/4471a55cd0c93cab92958e53265f0c3a6481936e
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2020-07-03T16:21:54+09:00

Commit Message:
ULTIMA8: Increase usage range in crusader

Changed paths:
    engines/ultima/ultima8/gumps/game_map_gump.cpp


diff --git a/engines/ultima/ultima8/gumps/game_map_gump.cpp b/engines/ultima/ultima8/gumps/game_map_gump.cpp
index 7e0ba6ffeb..82423055b4 100644
--- a/engines/ultima/ultima8/gumps/game_map_gump.cpp
+++ b/engines/ultima/ultima8/gumps/game_map_gump.cpp
@@ -394,8 +394,13 @@ void GameMapGump::onMouseDouble(int button, int32 mx, int32 my) {
 			item->getLocation(xv, yv, zv);
 			item->dumpInfo();
 
+			int range = 128; // CONSTANT!
+			if (GAME_IS_CRUSADER) {
+				range = 512;
+			}
+
 			if (dynamic_cast<Actor *>(item) ||
-			        avatar->canReach(item, 128)) { // CONSTANT!
+			        avatar->canReach(item, range)) {
 				// call the 'use' event
 				item->use();
 			} else {


Commit: 31bc37affa5175a3c2c9c4f7537993cd6b8c88f7
    https://github.com/scummvm/scummvm/commit/31bc37affa5175a3c2c9c4f7537993cd6b8c88f7
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2020-07-03T16:21:54+09:00

Commit Message:
ULTIMA8: Add simpler container search functions

Changed paths:
    engines/ultima/ultima8/world/container.cpp
    engines/ultima/ultima8/world/container.h


diff --git a/engines/ultima/ultima8/world/container.cpp b/engines/ultima/ultima8/world/container.cpp
index dc14ec6552..f8390f8919 100644
--- a/engines/ultima/ultima8/world/container.cpp
+++ b/engines/ultima/ultima8/world/container.cpp
@@ -296,6 +296,43 @@ void Container::containerSearch(UCList *itemlist, const uint8 *loopscript,
 	}
 }
 
+Item *Container::getFirstItemWithShape(uint16 shapeno, bool recurse) {
+	Std::list<Item *>::iterator iter;
+	for (iter = _contents.begin(); iter != _contents.end(); ++iter) {
+		if ((*iter)->getShape() == shapeno)
+			return *iter;
+
+		if (recurse) {
+			// recurse into child-containers
+			Container *container = dynamic_cast<Container *>(*iter);
+			if (container) {
+				Item *result = container->getFirstItemWithShape(shapeno, recurse);
+				if (result)
+					return result;
+			}
+		}
+	}
+
+	return nullptr;
+}
+
+void Container::getItemsWithShapeFamily(Std::vector<Item *> &itemlist, uint16 family, bool recurse) {
+	Std::list<Item *>::iterator iter;
+	for (iter = _contents.begin(); iter != _contents.end(); ++iter) {
+		if ((*iter)->getShapeInfo()->_family == family)
+			itemlist.push_back(*iter);
+
+		if (recurse) {
+			// recurse into child-containers
+			Container *container = dynamic_cast<Container *>(*iter);
+			if (container) {
+				container->getItemsWithShapeFamily(itemlist, family, recurse);
+			}
+		}
+	}
+
+}
+
 void Container::dumpInfo() const {
 	Item::dumpInfo();
 
diff --git a/engines/ultima/ultima8/world/container.h b/engines/ultima/ultima8/world/container.h
index 4255181c04..3f26dae10d 100644
--- a/engines/ultima/ultima8/world/container.h
+++ b/engines/ultima/ultima8/world/container.h
@@ -86,6 +86,16 @@ public:
 	void containerSearch(UCList *itemlist, const uint8 *loopscript,
 	                     uint32 scriptsize, bool recurse) const;
 
+	//! A simpler search of the container which just gets the
+	//! first item with a given shape number, optionally recursively.
+	//! \return The first item with that shape, or nullptr if nothing found.
+	Item *getFirstItemWithShape(uint16 shapeno, bool recurse);
+
+	//! A simpler search of the container which just gets the
+	//! items with a given shape family, optionally recursively.
+	//! \return The first item with that shape, or nullptr if nothing found.
+	void getItemsWithShapeFamily(Std::vector<Item *> &itemlist, uint16 family, bool recurse);
+
 	//! Get the weight of the container and its contents
 	//! \return weight
 	uint32 getTotalWeight() const override;


Commit: f79b300cdd86dfbe994979f1e2e9c5ac8217209e
    https://github.com/scummvm/scummvm/commit/f79b300cdd86dfbe994979f1e2e9c5ac8217209e
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2020-07-03T16:21:54+09:00

Commit Message:
ULTIMA8: Reduce reticle debug spam a bit

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


diff --git a/engines/ultima/ultima8/world/target_reticle_process.cpp b/engines/ultima/ultima8/world/target_reticle_process.cpp
index 09f3b0d199..a3696acf07 100644
--- a/engines/ultima/ultima8/world/target_reticle_process.cpp
+++ b/engines/ultima/ultima8/world/target_reticle_process.cpp
@@ -95,8 +95,8 @@ bool TargetReticleProcess::findTargetItem() {
 		_lastTargetDir = dir;
 		changed = true;
 	} else if (!item) {
-		debug("New reticle target: NONE");
 		if (_lastTargetItem) {
+			debug("New reticle target: NONE");
 			Item *lastItem = getItem(_lastTargetItem);
 			if (lastItem)
 				lastItem->clearExtFlag(Item::EXT_TARGET);
@@ -165,6 +165,12 @@ void TargetReticleProcess::itemMoved(Item *item) {
 
 void TargetReticleProcess::clearSprite() {
 	_reticleSpriteProcess = 0;
+	if (_lastTargetItem) {
+		Item *item = getItem(_lastTargetItem);
+		if (item) {
+			item->clearExtFlag(Item::EXT_TARGET);
+		}
+	}
 	_lastTargetItem = 0;
 	_lastTargetDir = 0x10;
 }


Commit: d0ec49a305dd76698f97bdb291ce2f88ba44080a
    https://github.com/scummvm/scummvm/commit/d0ec49a305dd76698f97bdb291ce2f88ba44080a
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2020-07-03T16:21:54+09:00

Commit Message:
ULTIMA8: Add x/y offset as some crusader gumps use them

Changed paths:
    engines/ultima/ultima8/gumps/gump.cpp


diff --git a/engines/ultima/ultima8/gumps/gump.cpp b/engines/ultima/ultima8/gumps/gump.cpp
index 0ee9dd5d53..7db6e3c310 100644
--- a/engines/ultima/ultima8/gumps/gump.cpp
+++ b/engines/ultima/ultima8/gumps/gump.cpp
@@ -92,6 +92,8 @@ void Gump::UpdateDimsFromShape() {
 	assert(sf);
 	_dims.w = sf->_width;
 	_dims.h = sf->_height;
+	_dims.x = -sf->_xoff;
+	_dims.y = -sf->_yoff;
 }
 
 void Gump::CreateNotifier() {


Commit: 4abc2b6fd5f93d87b11e7a81ec29e2398e3e7880
    https://github.com/scummvm/scummvm/commit/4abc2b6fd5f93d87b11e7a81ec29e2398e3e7880
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2020-07-03T16:21:54+09:00

Commit Message:
ULTIMA8: Add support for Crusader inventory items

Changed paths:
    engines/ultima/ultima8/convert/crusader/convert_usecode_crusader.h
    engines/ultima/ultima8/usecode/remorse_intrinsics.h
    engines/ultima/ultima8/world/actors/main_actor.cpp
    engines/ultima/ultima8/world/actors/main_actor.h


diff --git a/engines/ultima/ultima8/convert/crusader/convert_usecode_crusader.h b/engines/ultima/ultima8/convert/crusader/convert_usecode_crusader.h
index 53f251527c..7efddb0849 100644
--- a/engines/ultima/ultima8/convert/crusader/convert_usecode_crusader.h
+++ b/engines/ultima/ultima8/convert/crusader/convert_usecode_crusader.h
@@ -125,7 +125,7 @@ const char* const ConvertUsecodeCrusader::_intrinsics[] = {
 	"int16 Item::I_getDirFromTo16(x1, y1, x2, y2)",
 	"byte Actor::I_getSomeFlagProbablyCrouch(Item *)",
 	"int16 Actor::I_doAnim(12 bytes)", // v. similar code to U8
-	"byte I_probablyPickUpItem_037(4 bytes)", // same coff as 0B8
+	"byte MainActor::I_addItemCru(4 bytes)", // same coff as 0B8
 	"void AudioProcess::I_stopSFXCru(Item *, int16 sndno)",
 	"byte Actor::I_isDead(Item *)", // same coff as 122, 12E
 	"byte AudioProcess::I_isSFXPlayingForObject(Item *, int16 unk)",
@@ -262,7 +262,7 @@ const char* const ConvertUsecodeCrusader::_intrinsics[] = {
 	"int16 Item::I_equip(6 bytes)",
 	"void Ultima8Engine::I_clrAlertActive(void)",
 	"int16 Ultima8Engine::I_getAvatarInStasis(void)",
-	"byte I_probablyPickUpItem_0B8(4 bytes)", // same coff as 037
+	"byte MainActor::I_addItemCru(4 bytes)", // same coff as 037
 	"int16 Actor::I_getLastAnimSet(4 bytes)", // part of same coff set 01D, 05A, 0B9, 0D7, 0E4, 124
 	"void Item::I_setQuality(Item *, int)", // same coff as 07F, 125
 	"byte Intrinsic0BB(8 bytes)", // TODO: check usecode.. code is weird, something with an imaginary chequered wall shape? (0x31A)
diff --git a/engines/ultima/ultima8/usecode/remorse_intrinsics.h b/engines/ultima/ultima8/usecode/remorse_intrinsics.h
index 64321df270..ec935dd093 100644
--- a/engines/ultima/ultima8/usecode/remorse_intrinsics.h
+++ b/engines/ultima/ultima8/usecode/remorse_intrinsics.h
@@ -93,7 +93,7 @@ Intrinsic RemorseIntrinsics[] = {
 	Item::I_getDirFromTo16,
 	0, // TODO: Actor::I_getSomeFlagProbablyCrouch(Actor *)
 	Actor::I_doAnim, // void Intrinsic036(12 bytes)
-	0, // int Intrinsic037(4 bytes) (probably pick up)
+	MainActor::I_addItemCru, // int Intrinsic037(4 bytes)
 	AudioProcess::I_stopSFXCru, // takes Item *, sndno (from disasm)
 	Actor::I_isDead, // int Intrinsic039(4 bytes)
 	AudioProcess::I_isSFXPlayingForObject,
@@ -230,7 +230,7 @@ Intrinsic RemorseIntrinsics[] = {
 	Item::I_equip, // void Intrinsic0B5(6 bytes)
 	World::I_clrAlertActive, // void Intrinsic0B6(void)
 	Ultima8Engine::I_getAvatarInStasis, // void Intrinsic0B7(void)
-	0, // int Intrinsic0B8(4 bytes)
+	MainActor::I_addItemCru, // int Intrinsic0B8(4 bytes)
 	Actor::I_getLastAnimSet, // void Intrinsic0B9(4 bytes)
 	Item::I_setQuality,
 	0, // int Intrinsic0BB(8 bytes)
diff --git a/engines/ultima/ultima8/world/actors/main_actor.cpp b/engines/ultima/ultima8/world/actors/main_actor.cpp
index 13e6b73d79..badeef6ad0 100644
--- a/engines/ultima/ultima8/world/actors/main_actor.cpp
+++ b/engines/ultima/ultima8/world/actors/main_actor.cpp
@@ -53,7 +53,8 @@ namespace Ultima8 {
 DEFINE_RUNTIME_CLASSTYPE_CODE(MainActor)
 
 MainActor::MainActor() : _justTeleported(false), _accumStr(0), _accumDex(0),
-	_accumInt(0), _cruBatteryType(ChemicalBattery), _keycards(0) {
+	_accumInt(0), _cruBatteryType(ChemicalBattery), _keycards(0),
+	_activeWeapon(0), _activeInvItem(0) {
 }
 
 MainActor::~MainActor() {
@@ -74,7 +75,6 @@ GravityProcess *MainActor::ensureGravityProcess() {
 }
 
 bool MainActor::CanAddItem(Item *item, bool checkwghtvol) {
-	const unsigned int backpack_shape = 529; //!! *cough* constant
 
 	if (!Actor::CanAddItem(item, checkwghtvol)) return false;
 	if (item->getParent() == _objId) return true; // already in here
@@ -82,19 +82,26 @@ bool MainActor::CanAddItem(Item *item, bool checkwghtvol) {
 	// now check 'equipment slots'
 	// we can have one item of each equipment type, plus one backpack
 
-	uint32 equiptype = item->getShapeInfo()->_equipType;
-	bool backpack = (item->getShape() == backpack_shape);
+	if (GAME_IS_U8) {
+		const unsigned int backpack_shape = 529; //!! *cough* constant
+		uint32 equiptype = item->getShapeInfo()->_equipType;
+		bool backpack = (item->getShape() == backpack_shape);
 
-	// valid item type?
-	if (equiptype == ShapeInfo::SE_NONE && !backpack) return false;
+		// valid item type?
+		if (equiptype == ShapeInfo::SE_NONE && !backpack) return false;
 
-	Std::list<Item *>::iterator iter;
-	for (iter = _contents.begin(); iter != _contents.end(); ++iter) {
-		uint32 cet = (*iter)->getShapeInfo()->_equipType;
-		bool cbackpack = ((*iter)->getShape() == backpack_shape);
+		Std::list<Item *>::iterator iter;
+		for (iter = _contents.begin(); iter != _contents.end(); ++iter) {
+			uint32 cet = (*iter)->getShapeInfo()->_equipType;
+			bool cbackpack = ((*iter)->getShape() == backpack_shape);
 
-		// already have an item with the same equiptype
-		if (cet == equiptype || (cbackpack && backpack)) return false;
+			// already have an item with the same equiptype
+			if (cet == equiptype || (cbackpack && backpack)) return false;
+		}
+	} else if (GAME_IS_CRUSADER) {
+		// TODO: Enforce the number of slots by family here (weapon / ammo / other)
+		// For now just enforce no limit.
+		return true;
 	}
 
 	return true;
@@ -111,6 +118,192 @@ bool MainActor::addItem(Item *item, bool checkwghtvol) {
 	return true;
 }
 
+int16 MainActor::addItemCru(Item *item, bool showtoast) {
+	// This code is a little ugly, it's a somewhat close
+	// re-implementation of the original and could do
+	// with some cleanup.
+
+	if (!item || !item->getShape())
+		return 0;
+
+	int shapeno = item->getShape();
+	int x, y, z;
+	getLocation(x, y, z);
+
+	if (shapeno == 0x4ed) {
+		Item *credits = getFirstItemWithShape(shapeno, true);
+		if (credits) {
+			uint16 q = item->getQuality();
+			uint32 newq = credits->getQuality() + q;
+			if (newq > 64000)
+				newq = 64000;
+			credits->setQuality(newq);
+			credits->callUsecodeEvent_combine();
+			if (showtoast) {
+				warning("TODO: show toast for added credits %d", q);
+			}
+			item->destroy();
+		} else {
+			item->setFrame(0);
+			item->moveToContainer(this);
+			if (!_activeInvItem)
+				_activeInvItem = item->getObjId();
+			if (showtoast) {
+				warning("TODO: show toast for new credits %d", item->getQuality());
+			}
+		}
+		return 1;
+	}
+
+	switch (static_cast<ShapeInfo::SFamily>(item->getShapeInfo()->_family)) {
+	case ShapeInfo::SF_CRUWEAPON: {	// 0xa
+		Item *weapon = getFirstItemWithShape(shapeno, true);
+		if (!weapon) {
+			// New weapon. Add it.
+			const WeaponInfo *winfo = item->getShapeInfo()->_weaponInfo;
+			assert(winfo);
+			if (winfo->_ammoType == 0) {
+				item->setQuality(0);
+				item->callUsecodeEvent_combine();
+			} else {
+				warning("TODO: Get default count for ammo type %d", winfo->_ammoType);
+				item->setQuality(100);
+			}
+			item->setLocation(x, y, z);
+			item->moveToContainer(this);
+			if (!_activeWeapon)
+				_activeWeapon = item->getObjId();
+			warning("TODO: Set new weapon as active weapon if there is none");
+			if (showtoast)
+				warning("TODO: Show toast for new weapon %d", shapeno);
+		}
+		break;
+	}
+	case ShapeInfo::SF_CRUAMMO:	{	// 0xb
+		Item *ammo = getFirstItemWithShape(shapeno, true);
+		if (!ammo) {
+			// don't have this ammo yet, add it
+			item->setQuality(1);
+			item->callUsecodeEvent_combine();
+			item->moveToContainer(this);
+			if (showtoast)
+				warning("TODO: Show toast for new ammo %d", shapeno);
+			return 1;
+		} else {
+			// already have this, add some ammo.
+			uint16 q = ammo->getQuality();
+			if (q < 0x14) {
+				ammo->setQuality(q + 1);
+				ammo->callUsecodeEvent_combine();
+				if (showtoast)
+					warning("TODO: Show toast for combined ammo %d (%d)", shapeno, q + 1);
+				item->destroy();
+				return 1;
+			}
+		}
+		break;
+	}
+	case ShapeInfo::SF_CRUBOMB:		// 0xc
+	case ShapeInfo::SF_CRUINVITEM:	// 0xd
+		if (shapeno == 0x111) {
+			addKeycard(item->getQuality() & 0xff);
+			if (showtoast) {
+				warning("TODO: show toast for added keycard %d", item->getQuality() & 0xff);
+			}
+			item->destroy();
+			return 1;
+		} else if ((shapeno == 0x3a2) || (shapeno == 0x3a3) || (shapeno == 0x3a4)) {
+			// Batteries
+			if (showtoast) {
+				warning("TODO: show toast for added battery %d", shapeno);
+			}
+			item->destroy();
+			int plusenergy = 0;
+			CruBatteryType oldbattery = _cruBatteryType;
+			if (shapeno == 0x3a2) {
+				if (oldbattery == NoBattery) {
+					setBatteryType(ChemicalBattery);
+				} else {
+					plusenergy = 0x9c4;
+				}
+			} else if (shapeno == 0x3a4) {
+				if (oldbattery < FusionBattery) {
+					setBatteryType(FusionBattery);
+				} else {
+					plusenergy = 5000;
+				}
+			} else if (shapeno == 0x3a3) {
+				if (oldbattery < FissionBattery) {
+					setBatteryType(FissionBattery);
+				} else {
+					plusenergy = 10000;
+				}
+			}
+			if (plusenergy) {
+				int newenergy = getMana() + plusenergy;
+				if (newenergy > getMaxEnergy())
+					newenergy = getMaxEnergy();
+				setMana(newenergy);
+			}
+			return 1;
+		} else {
+			Item *existing = getFirstItemWithShape(shapeno, true);
+			if (!existing) {
+				if ((shapeno == 0x52e) || (shapeno == 0x52f) || (shapeno == 0x530)) {
+					warning("TODO: Properly handle giving avatar a shield 0x%x", shapeno);
+					return 0;
+				} else {
+					item->setFrame(0);
+					item->setQuality(1);
+					item->callUsecodeEvent_combine();
+					bool added = item->moveToContainer(this);
+					if (showtoast) {
+						warning("TODO: show toast new item %d (%s)",
+								shapeno, added ? "added" : "failed");
+					}
+					if (!_activeInvItem)
+						_activeInvItem = item->getObjId();
+					return 1;
+				}
+			} else {
+				// Already have this item..
+				if ((shapeno == 0x52e) || (shapeno == 0x52f) || (shapeno == 0x530)) {
+					// shields, already have one, destroy the new one.
+					item->destroy();
+					return 1;
+				} else if (shapeno == 0x560) {
+					uint16 q = existing->getQuality();
+					if (q < 0x14) {
+						existing->setQuality(q + 1);
+						existing->callUsecodeEvent_combine();
+						if (showtoast) {
+							warning("TODO: show toast for combined cru spider q=%d", q + 1);
+						}
+						item->destroy();
+						return 1;
+					}
+				} else {
+					uint16 q = existing->getQuality();
+					if (q < 10) {
+						existing->setQuality(q + 1);
+						existing->callUsecodeEvent_combine();
+						if (showtoast) {
+							warning("TODO: show toast for combined other item %d q=%d", shapeno, q + 1);
+						}
+						item->destroy();
+						return 1;
+					}
+				}
+			}
+		}
+		break;
+	default:
+		break;
+	}
+
+	return 0;
+}
+
 void MainActor::teleport(int mapNum_, int32 x_, int32 y_, int32 z_) {
 	World *world = World::get_instance();
 
@@ -405,13 +598,19 @@ int16 MainActor::getMaxEnergy() {
 	}
 }
 
-bool MainActor::hasKeycard(int num) {
+bool MainActor::hasKeycard(int num) const {
 	if (num > 31)
 		return 0;
 
 	return _keycards & (1 << num);
 }
 
+void MainActor::addKeycard(int bitno) {
+	if (bitno > 31 || bitno < 0)
+		return;
+	_keycards |= (1 << bitno);
+}
+
 void MainActor::saveData(Common::WriteStream *ws) {
 	Actor::saveData(ws);
 	uint8 jt = _justTeleported ? 1 : 0;
@@ -423,6 +622,8 @@ void MainActor::saveData(Common::WriteStream *ws) {
 	if (GAME_IS_CRUSADER) {
 		ws->writeByte(static_cast<byte>(_cruBatteryType));
 		ws->writeUint32LE(_keycards);
+		ws->writeUint16LE(_activeWeapon);
+		ws->writeUint16LE(_activeInvItem);
 	}
 
 	uint8 namelength = static_cast<uint8>(_name.size());
@@ -443,6 +644,8 @@ bool MainActor::loadData(Common::ReadStream *rs, uint32 version) {
 	if (GAME_IS_CRUSADER) {
 		_cruBatteryType = static_cast<CruBatteryType>(rs->readByte());
 		_keycards = rs->readUint32LE();
+		_activeWeapon = rs->readUint16LE();
+		_activeInvItem = rs->readUint16LE();
 	}
 
 	uint8 namelength = rs->readByte();
@@ -529,7 +732,7 @@ uint32 MainActor::I_getMaxEnergy(const uint8 *args,
 								 unsigned int /*argsize*/) {
 	ARG_ACTOR_FROM_PTR(actor);
 	MainActor *av = getMainActor();
-	if (actor != av) {
+	if (!av || actor != av) {
 		return 0;
 	}
 	return av->getMaxEnergy();
@@ -539,31 +742,43 @@ uint32 MainActor::I_hasKeycard(const uint8 *args,
 								 unsigned int /*argsize*/) {
 	ARG_UINT16(num);
 	MainActor *av = getMainActor();
+	if (!av)
+		return 0;
 	return av->hasKeycard(num);
 }
 
 uint32 MainActor::I_clrKeycards(const uint8 *args,
 								 unsigned int /*argsize*/) {
 	MainActor *av = getMainActor();
+	if (!av)
+		return 0;
 	av->clrKeycards();
 	return 0;
 }
 
+uint32 MainActor::I_addItemCru(const uint8 *args,
+								 unsigned int /*argsize*/) {
+	MainActor *av = getMainActor();
+	ARG_ITEM_FROM_ID(item);
+	ARG_UINT16(showtoast);
+
+	if (!av || !item)
+		return 0;
+
+	if (av->addItemCru(item, showtoast != 0))
+		return 1;
+
+	return 0;
+}
+
 void MainActor::useInventoryItem(uint32 shapenum) {
 	if (Ultima8Engine::get_instance()->isAvatarInStasis()) {
 		pout << "Can't use item: avatarInStasis" << Std::endl;
 		return;
 	}
-	LOOPSCRIPT(script, LS_SHAPE_EQUAL(shapenum));
-	UCList uclist(2);
-	this->containerSearch(&uclist, script, sizeof(script), true);
-	if (uclist.getSize() < 1)
-		return;
-
-	uint16 oId = uclist.getuint16(0);
-	Item *item = getItem(oId);
-	item->callUsecodeEvent_use();
-
+	Item *item = this->getFirstItemWithShape(shapenum, true);
+	if (item)
+		item->callUsecodeEvent_use();
 }
 
 } // End of namespace Ultima8
diff --git a/engines/ultima/ultima8/world/actors/main_actor.h b/engines/ultima/ultima8/world/actors/main_actor.h
index 057588650a..eb00ccde64 100644
--- a/engines/ultima/ultima8/world/actors/main_actor.h
+++ b/engines/ultima/ultima8/world/actors/main_actor.h
@@ -47,6 +47,10 @@ public:
 	bool CanAddItem(Item *item, bool checkwghtvol = false) override;
 	bool addItem(Item *item, bool checkwghtvol = false) override;
 
+	//! Add item to avatar's inventory, but with some extra logic to do things like combine
+	//! ammo and credits, use batteries, etc.
+	int16 addItemCru(Item *item, bool showtoast);
+
 	//! teleport to the given location on the given map
 	void teleport(int mapNum_, int32 x_, int32 y_, int32 z_) override;
 
@@ -99,12 +103,29 @@ public:
 
 	int16 getMaxEnergy();
 
-	bool hasKeycard(int num);
+	CruBatteryType getBatteryType() const {
+		return _cruBatteryType;
+	}
+	void setBatteryType(CruBatteryType newbattery) {
+		_cruBatteryType = newbattery;
+		setMana(getMaxEnergy());
+	}
+
+	bool hasKeycard(int num) const;
+	void addKeycard(int bitno);
 
 	void clrKeycards() {
 		_keycards = 0;
 	}
 
+	uint16 getActiveWeapon() const {
+		return _activeWeapon;
+	}
+
+	uint16 getActiveInvItem() const {
+		return _activeInvItem;
+	}
+
 	bool loadData(Common::ReadStream *rs, uint32 version);
 	void saveData(Common::WriteStream *ws) override;
 
@@ -120,6 +141,7 @@ public:
 	INTRINSIC(I_getMaxEnergy);
 	INTRINSIC(I_hasKeycard);
 	INTRINSIC(I_clrKeycards);
+	INTRINSIC(I_addItemCru);
 
 	void getWeaponOverlay(const WeaponOverlayFrame *&frame_, uint32 &shape_);
 
@@ -135,8 +157,11 @@ protected:
 
 	uint32 _keycards;
 	CruBatteryType _cruBatteryType;
+	uint16 _activeWeapon;
+	uint16 _activeInvItem;
 
 	Std::string _name;
+
 };
 
 } // End of namespace Ultima8


Commit: f858c03755557f1e4b539f62abff6908c265915c
    https://github.com/scummvm/scummvm/commit/f858c03755557f1e4b539f62abff6908c265915c
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2020-07-03T16:21:54+09:00

Commit Message:
ULTIMA8: Give Crusader avatar correct items on startup

Changed paths:
    engines/ultima/ultima8/games/start_crusader_process.cpp


diff --git a/engines/ultima/ultima8/games/start_crusader_process.cpp b/engines/ultima/ultima8/games/start_crusader_process.cpp
index 3cdf424deb..bcc943e1d8 100644
--- a/engines/ultima/ultima8/games/start_crusader_process.cpp
+++ b/engines/ultima/ultima8/games/start_crusader_process.cpp
@@ -30,6 +30,7 @@
 #include "ultima/ultima8/world/current_map.h"
 #include "ultima/ultima8/world/egg.h"
 #include "ultima/ultima8/world/camera_process.h"
+#include "ultima/ultima8/world/actors/main_actor.h"
 #include "ultima/ultima8/world/world.h"
 #include "ultima/ultima8/ultima8.h"
 #include "ultima/ultima8/kernel/kernel.h"
@@ -88,13 +89,15 @@ void StartCrusaderProcess::run() {
 	//UCList uclist(2);
 
 	if (!_skipStart) {
+		// TODO:
+		// * Give avatar item 0x4d4 and item 0x598
 		// TODO: Find the first MISS1EGG egg like in U8 - should teleport in
-		// Game starts a teleport process to Egg 0x1e:
-		// Item 9115 (class TeleportEgg, shape 404, 1, (60656,59312,16) q:99, m:0, n:0, f: 0x2000, ef:0x3 shapeinfo f:1009, fam:8, et:0)
+		//Kernel::get_instance()->addProcess(new TeleportToEggProcess(1, 0x1e));
+
 		/*
-		LOOPSCRIPT(script, LS_AND(LS_SHAPE_EQUAL1(73), LS_Q_EQUAL(36)));
+		LOOPSCRIPT(script, LS_AND(LS_SHAPE_EQUAL1(0x90D), LS_Q_EQUAL(36)));
 		currentmap->areaSearch(&uclist, script, sizeof(script),
-		                       0, 256, false, 16188, 7500);
+							   0, 256, false, 16188, 7500);
 		if (uclist.getSize() < 1) {
 			perr << "Unable to find FIRST egg!" << Std::endl;
 			return;
@@ -109,9 +112,17 @@ void StartCrusaderProcess::run() {
 		egg->hatch();
 		*/
 
+		MainActor *avatar = getMainActor();
+		int mapnum = avatar->getMapNum();
+		Item *datalink = ItemFactory::createItem(0x4d4, 0, 0, 0, 0, mapnum, 0, true);
+		avatar->addItemCru(datalink, false);
+		Item *smiley = ItemFactory::createItem(0x598, 0, 0, 0, 0, mapnum, 0, true);
+		smiley->moveToContainer(avatar);
+
 		// TODO: How is this created in the game??
 		Egg *miss1egg = new Egg();
 		miss1egg->setShape(2317);
+		miss1egg->setMapNum(mapnum);
 		miss1egg->assignObjId();
 		miss1egg->callUsecodeEvent_hatch();
 	}


Commit: 07a43d975a4443869cbf9d377f9840d638c259af
    https://github.com/scummvm/scummvm/commit/07a43d975a4443869cbf9d377f9840d638c259af
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2020-07-03T16:21:54+09:00

Commit Message:
ULTIMA8: Fix null pointer usage in reticle process

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


diff --git a/engines/ultima/ultima8/world/target_reticle_process.cpp b/engines/ultima/ultima8/world/target_reticle_process.cpp
index a3696acf07..4103e82760 100644
--- a/engines/ultima/ultima8/world/target_reticle_process.cpp
+++ b/engines/ultima/ultima8/world/target_reticle_process.cpp
@@ -150,16 +150,13 @@ void TargetReticleProcess::itemMoved(Item *item) {
 
 	SpriteProcess *spriteproc = dynamic_cast<SpriteProcess *>(Kernel::get_instance()->getProcess(_reticleSpriteProcess));
 
-	// TODO: If the item moved outside the direction we're targeting,
-	// the process should be terminated.
-
 	if (spriteproc) {
 		if (actordir != _lastTargetDir || dirtoitem != _lastTargetDir) {
 			spriteproc->terminate();
 			clearSprite();
+		} else {
+			spriteproc->move(x, y, z);
 		}
-	} else {
-		spriteproc->move(x, y, z);
 	}
 }
 


Commit: 9d396d6a1b61203ac2963ee9d70cab0a14286a6b
    https://github.com/scummvm/scummvm/commit/9d396d6a1b61203ac2963ee9d70cab0a14286a6b
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2020-07-03T16:21:54+09:00

Commit Message:
ULTIMA8: Make crusader inventory gumps work

Changed paths:
    engines/ultima/ultima8/graphics/type_flags.cpp
    engines/ultima/ultima8/gumps/cru_inventory_gump.cpp
    engines/ultima/ultima8/gumps/cru_inventory_gump.h
    engines/ultima/ultima8/gumps/cru_weapon_gump.cpp
    engines/ultima/ultima8/gumps/cru_weapon_gump.h
    engines/ultima/ultima8/world/weapon_info.h


diff --git a/engines/ultima/ultima8/graphics/type_flags.cpp b/engines/ultima/ultima8/graphics/type_flags.cpp
index d550ac0232..e485e4f961 100644
--- a/engines/ultima/ultima8/graphics/type_flags.cpp
+++ b/engines/ultima/ultima8/graphics/type_flags.cpp
@@ -241,15 +241,20 @@ void TypeFlags::loadWeaponInfo() {
 		// Crusader-specific fields:
 
 		if (config->get(k + "/ammo_type", val))
-			wi->_ammoType = static_cast<uint8>(val);
+			wi->_ammoType = static_cast<uint16>(val);
 		else
 			wi->_ammoType = 0;
 
 		if (config->get(k + "/sound", val))
-			wi->_sound = static_cast<uint8>(val);
+			wi->_sound = static_cast<uint16>(val);
 		else
 			wi->_sound = 0;
 
+		if (config->get(k + "/display_frame", val))
+			wi->_displayFrame = static_cast<uint16>(val);
+		else
+			wi->_displayFrame = 0;
+
 		assert(wi->_shape < _shapeInfo.size());
 		_shapeInfo[wi->_shape]._weaponInfo = wi;
 	}
diff --git a/engines/ultima/ultima8/gumps/cru_inventory_gump.cpp b/engines/ultima/ultima8/gumps/cru_inventory_gump.cpp
index f5ebe5577a..2c4e146915 100644
--- a/engines/ultima/ultima8/gumps/cru_inventory_gump.cpp
+++ b/engines/ultima/ultima8/gumps/cru_inventory_gump.cpp
@@ -36,15 +36,16 @@
 namespace Ultima {
 namespace Ultima8 {
 
-static const int INVENTORY_GUMP_SHAPE = 3;
+static const int INVENTORY_GUMP_SHAPE = 5;
 
 DEFINE_RUNTIME_CLASSTYPE_CODE(CruInventoryGump)
-CruInventoryGump::CruInventoryGump() : CruStatGump(), _inventoryShape(nullptr) {
+CruInventoryGump::CruInventoryGump() : CruStatGump(), _inventoryShape(nullptr),
+	_inventoryItemGump(nullptr) {
 
 }
 
 CruInventoryGump::CruInventoryGump(Shape *shape, int x)
-	: CruStatGump(shape, x), _inventoryShape(nullptr) {
+	: CruStatGump(shape, x), _inventoryShape(nullptr), _inventoryItemGump(nullptr) {
 	_frameNum = 0;
 }
 
@@ -65,20 +66,68 @@ void CruInventoryGump::InitGump(Gump *newparent, bool take_focus) {
 		warning("failed to init stat gump: no inventory shape");
 		return;
 	}
+	_inventoryItemGump = new Gump();
+	_inventoryItemGump->InitGump(this, false);
+	// we'll set the shape for this gump later.
 }
 
-void CruInventoryGump::PaintThis(RenderSurface *surf, int32 lerp_factor, bool scaled) {
-	CruStatGump::PaintThis(surf, lerp_factor, scaled);
+// TODO: This is a bit of a hack.. should be configured
+// in the weapon ini file.
+static uint16 getDisplayFrameForShape(uint16 shapeno) {
+	switch (shapeno) {
+	case 0x351:
+		return 0x0;
+	case 0x4D4:
+		return 0x1;
+	case 0x52D:
+		return 0x2;
+	case 0x52E:
+		return 0x3;
+	case 0x582:
+		return 0x19;
+	case 0x52F:
+		return 0x5;
+	case 0x55F:
+		return 0x18;
+	case 0x530:
+		return 0x7;
+	case 0x3A2:
+		return 0x16;
+	case 0x3A3:
+		return 0x15;
+	case 0x3A4:
+		return 0x17;
+	default:
+		warning("No inventory gump frame for shape %d", shapeno);
+		return 0;
+	}
+}
 
+void CruInventoryGump::PaintThis(RenderSurface *surf, int32 lerp_factor, bool scaled) {
 	const MainActor *a = getMainActor();
 	if (!a) {
 		// avatar gone??
 		return;
 	}
 
-	uint16 weapon = a->getDamageType(); // ?? TODO: Where do we store item weapon?
-	/*const ShapeFrame *frame = */_inventoryShape->getFrame(weapon);
-	// TODO: Paint the current selected item
+	uint16 activeitem = a->getActiveInvItem();
+	if (!activeitem) {
+		_inventoryItemGump->SetShape(0, 0);
+	} else {
+		Item *item = getItem(activeitem);
+		if (!item) {
+			_inventoryItemGump->SetShape(0, 0);
+		} else {
+			uint16 frame = getDisplayFrameForShape(item->getShape());
+			_inventoryItemGump->SetShape(_inventoryShape, frame);
+			_inventoryItemGump->UpdateDimsFromShape();
+			_inventoryItemGump->setRelativePosition(CENTER);
+			// TODO: Add text for count (from q)
+		}
+	}
+
+	// Now that the shape is configured, we can paint.
+	CruStatGump::PaintThis(surf, lerp_factor, scaled);
 }
 
 void CruInventoryGump::saveData(Common::WriteStream *ws) {
diff --git a/engines/ultima/ultima8/gumps/cru_inventory_gump.h b/engines/ultima/ultima8/gumps/cru_inventory_gump.h
index 674f464c08..9e64574dc5 100644
--- a/engines/ultima/ultima8/gumps/cru_inventory_gump.h
+++ b/engines/ultima/ultima8/gumps/cru_inventory_gump.h
@@ -50,7 +50,8 @@ public:
 	void saveData(Common::WriteStream *ws) override;
 
 private:
-	const Shape *_inventoryShape;
+	Shape *_inventoryShape;
+	Gump *_inventoryItemGump;
 };
 
 } // End of namespace Ultima8
diff --git a/engines/ultima/ultima8/gumps/cru_weapon_gump.cpp b/engines/ultima/ultima8/gumps/cru_weapon_gump.cpp
index bb15a66c6f..a38e339309 100644
--- a/engines/ultima/ultima8/gumps/cru_weapon_gump.cpp
+++ b/engines/ultima/ultima8/gumps/cru_weapon_gump.cpp
@@ -40,12 +40,13 @@ static const int WEAPON_GUMP_SHAPE = 3;
 
 DEFINE_RUNTIME_CLASSTYPE_CODE(CruWeaponGump)
 
-CruWeaponGump::CruWeaponGump() : CruStatGump(), _weaponShape(nullptr) {
+CruWeaponGump::CruWeaponGump() : CruStatGump(), _weaponShape(nullptr),
+	_weaponGump(nullptr) {
 
 }
 
 CruWeaponGump::CruWeaponGump(Shape *shape, int x)
-	: CruStatGump(shape, x), _weaponShape(nullptr) {
+	: CruStatGump(shape, x), _weaponShape(nullptr), _weaponGump(nullptr) {
 	_frameNum = 0;
 }
 
@@ -66,20 +67,40 @@ void CruWeaponGump::InitGump(Gump *newparent, bool take_focus) {
 		warning("failed to init stat gump: no weapon shape");
 		return;
 	}
+
+	_weaponGump = new Gump();
+	// We will fill out the shape to paint for this later.
+	_weaponGump->InitGump(this, false);
+
 }
 
 void CruWeaponGump::PaintThis(RenderSurface *surf, int32 lerp_factor, bool scaled) {
-	CruStatGump::PaintThis(surf, lerp_factor, scaled);
-
 	const MainActor *a = getMainActor();
 	if (!a) {
 		// avatar gone??
 		return;
 	}
 
-	uint16 weapon = a->getDamageType(); // ?? TODO: Where do we store current weapon?
-	/*const ShapeFrame *frame = */_weaponShape->getFrame(weapon);
-	// TODO: Paint the current weapon
+	uint16 active = a->getActiveWeapon();
+	if (!active) {
+		_weaponGump->SetShape(0, 0);
+	} else {
+		Item *item = getItem(active);
+		if (!item) {
+			_weaponGump->SetShape(0, 0);
+		} else {
+			WeaponInfo *weaponinfo = item->getShapeInfo()->_weaponInfo;
+			uint16 frameno = 0;
+			if (weaponinfo) {
+				frameno = weaponinfo->_displayFrame;
+			}
+			_weaponGump->SetShape(_weaponShape, frameno);
+			_weaponGump->UpdateDimsFromShape();
+			_weaponGump->setRelativePosition(CENTER);
+		}
+	}
+
+	CruStatGump::PaintThis(surf, lerp_factor, scaled);
 }
 
 void CruWeaponGump::saveData(Common::WriteStream *ws) {
diff --git a/engines/ultima/ultima8/gumps/cru_weapon_gump.h b/engines/ultima/ultima8/gumps/cru_weapon_gump.h
index 69b8833d8e..eeb1db6090 100644
--- a/engines/ultima/ultima8/gumps/cru_weapon_gump.h
+++ b/engines/ultima/ultima8/gumps/cru_weapon_gump.h
@@ -50,7 +50,8 @@ public:
 	void saveData(Common::WriteStream *ws) override;
 
 private:
-	const Shape *_weaponShape;
+	Shape *_weaponShape;
+	Gump *_weaponGump;
 };
 
 } // End of namespace Ultima8
diff --git a/engines/ultima/ultima8/world/weapon_info.h b/engines/ultima/ultima8/world/weapon_info.h
index ced62755c9..b4c69aed42 100644
--- a/engines/ultima/ultima8/world/weapon_info.h
+++ b/engines/ultima/ultima8/world/weapon_info.h
@@ -42,8 +42,9 @@ struct WeaponInfo {
 	int _treasureChance;
 
 	// Crusader-specific fields:
-	uint16 _sound;
-	uint16 _ammoType;
+	uint16 _sound;		//!< The sound this weapon makes when fired
+	uint16 _ammoType;	//!< The type of ammo it uses
+	uint16 _displayFrame;	//!< The frame to use in the inventory gump
 
 	enum DmgType {
 		DMG_NORMAL = 0x0001,


Commit: 535867a4e8a86ccefd82732dfbd2410ce831c769
    https://github.com/scummvm/scummvm/commit/535867a4e8a86ccefd82732dfbd2410ce831c769
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2020-07-03T16:21:54+09:00

Commit Message:
ULTIMA8: Improve debug messages

Changed paths:
    engines/ultima/ultima8/usecode/uc_machine.cpp


diff --git a/engines/ultima/ultima8/usecode/uc_machine.cpp b/engines/ultima/ultima8/usecode/uc_machine.cpp
index 0363c9c21b..44f205384e 100644
--- a/engines/ultima/ultima8/usecode/uc_machine.cpp
+++ b/engines/ultima/ultima8/usecode/uc_machine.cpp
@@ -1743,7 +1743,7 @@ void UCMachine::execProcess(UCProcess *p) {
 					                                   ui16b, recurse);
 				} else {
 					// return error or return empty list?
-					perr << "Warning: invalid item passed to area search"
+					perr << "Warning: invalid item " << ui16a << " passed to area search"
 					     << Std::endl;
 				}
 				break;
@@ -1771,7 +1771,7 @@ void UCMachine::execProcess(UCProcess *p) {
 					                           scriptsize, recurse);
 				} else {
 					// return error or return empty list?
-					perr << "Warning: invalid container passed to "
+					perr << "Warning: invalid container "<< ui16b << " passed to "
 					     << "container search" << Std::endl;
 				}
 				break;


Commit: 27dab7b18786ee9b67ad2a3a68dae489acc3c8d8
    https://github.com/scummvm/scummvm/commit/27dab7b18786ee9b67ad2a3a68dae489acc3c8d8
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2020-07-03T19:40:39+09:00

Commit Message:
ULTIMA8: Add ability to cycle items for Crusader

Changed paths:
    engines/ultima/ultima8/meta_engine.cpp
    engines/ultima/ultima8/meta_engine.h
    engines/ultima/ultima8/misc/debugger.cpp
    engines/ultima/ultima8/misc/debugger.h
    engines/ultima/ultima8/ultima8.cpp
    engines/ultima/ultima8/world/actors/main_actor.cpp
    engines/ultima/ultima8/world/actors/main_actor.h


diff --git a/engines/ultima/ultima8/meta_engine.cpp b/engines/ultima/ultima8/meta_engine.cpp
index 45ddf50e64..9b13ccd01c 100644
--- a/engines/ultima/ultima8/meta_engine.cpp
+++ b/engines/ultima/ultima8/meta_engine.cpp
@@ -50,6 +50,7 @@ static const KeybindingRecord KEYS[] = {
 	{ ACTION_MINIMAP, "MINIMAP", "Toggle Minimap", "MiniMapGump::toggle", nullptr, "m", "JOY_LEFT_TRIGGER" },
 	{ ACTION_RECALL, "RECALL", "Use Recall", "MainActor::useRecall", nullptr, "r", nullptr },
 	{ ACTION_INVENTORY, "INVENTORY", "Inventory", "MainActor::useInventory", nullptr, "z", "JOY_LEFT_SHOULDER" },
+	{ ACTION_NEXT_WEAPON, "NEXT_WEAPON", "Next Crusader Weapon", "MainActor::nextWeapon", nullptr, "w", nullptr },
 	{ ACTION_MENU, "MENU", "Game Menu", "MenuGump::showMenu", nullptr, "ESCAPE", "JOY_Y" },
 	{ ACTION_CLOSE_GUMPS, "CLOSE_GUMPS", "Close Gumps", "GUIApp::closeItemGumps", nullptr, "BACKSPACE", nullptr },
 	{ ACTION_HIGHLIGHT_ITEMS, "HIGHLIGHT_ITEMS", "Show Highlight Items", "GameMapGump::toggleHighlightItems",
diff --git a/engines/ultima/ultima8/meta_engine.h b/engines/ultima/ultima8/meta_engine.h
index 21d21f6fce..ca00fac08c 100644
--- a/engines/ultima/ultima8/meta_engine.h
+++ b/engines/ultima/ultima8/meta_engine.h
@@ -30,9 +30,9 @@ namespace Ultima8 {
 
 enum KeybindingAction {
 	ACTION_QUICKSAVE, ACTION_SAVE, ACTION_LOAD, ACTION_BEDROLL, ACTION_COMBAT, ACTION_BACKPACK,
-	ACTION_KEYRING, ACTION_MINIMAP, ACTION_RECALL, ACTION_INVENTORY, ACTION_MENU,
-	ACTION_CLOSE_GUMPS, ACTION_HIGHLIGHT_ITEMS, ACTION_TOGGLE_TOUCHING, ACTION_JUMP,
-	ACTION_TURN_LEFT, ACTION_TURN_RIGHT, ACTION_MOVE_FORWARD, ACTION_MOVE_BACK,
+	ACTION_KEYRING, ACTION_MINIMAP, ACTION_RECALL, ACTION_INVENTORY, ACTION_NEXT_WEAPON,
+	ACTION_MENU, ACTION_CLOSE_GUMPS, ACTION_HIGHLIGHT_ITEMS, ACTION_TOGGLE_TOUCHING,
+	ACTION_JUMP, ACTION_TURN_LEFT, ACTION_TURN_RIGHT, ACTION_MOVE_FORWARD, ACTION_MOVE_BACK,
 	ACTION_MOVE_UP, ACTION_MOVE_DOWN, ACTION_MOVE_LEFT, ACTION_MOVE_RIGHT,
 	ACTION_MOVE_RUN, ACTION_MOVE_STEP,
 
diff --git a/engines/ultima/ultima8/misc/debugger.cpp b/engines/ultima/ultima8/misc/debugger.cpp
index 82100d1c2d..7665ed7973 100644
--- a/engines/ultima/ultima8/misc/debugger.cpp
+++ b/engines/ultima/ultima8/misc/debugger.cpp
@@ -36,6 +36,7 @@
 #include "ultima/ultima8/gumps/shape_viewer_gump.h"
 #include "ultima/ultima8/gumps/menu_gump.h"
 #include "ultima/ultima8/kernel/kernel.h"
+#include "ultima/ultima8/kernel/core_app.h"
 #include "ultima/ultima8/kernel/object_manager.h"
 #include "ultima/ultima8/misc/id_man.h"
 #include "ultima/ultima8/misc/util.h"
@@ -139,6 +140,7 @@ Debugger::Debugger() : Shared::Debugger() {
 	registerCmd("MainActor::useRecall", WRAP_METHOD(Debugger, cmdUseRecall));
 	registerCmd("MainActor::useBedroll", WRAP_METHOD(Debugger, cmdUseBedroll));
 	registerCmd("MainActor::useKeyring", WRAP_METHOD(Debugger, cmdUseKeyring));
+	registerCmd("MainActor::nextWeapon", WRAP_METHOD(Debugger, cmdNextWeapon));
 	registerCmd("MainActor::toggleCombat", WRAP_METHOD(Debugger, cmdToggleCombat));
 
 	registerCmd("ObjectManager::objectTypes", WRAP_METHOD(Debugger, cmdObjectTypes));
@@ -1083,9 +1085,23 @@ bool Debugger::cmdUseBackpack(int argc, const char **argv) {
 		return false;
 	}
 	MainActor *av = getMainActor();
-	Item *backpack = getItem(av->getEquip(7));
-	if (backpack)
-		backpack->callUsecodeEvent_use();
+	if (GAME_IS_U8) {
+		Item *backpack = getItem(av->getEquip(7));
+		if (backpack)
+			backpack->callUsecodeEvent_use();
+	} else {
+		av->nextInvItem();
+	}
+	return false;
+}
+
+bool Debugger::cmdNextWeapon(int argc, const char **argv) {
+	if (Ultima8Engine::get_instance()->isAvatarInStasis()) {
+		debugPrintf("Can't change weapon: avatarInStasis\n");
+		return false;
+	}
+	MainActor *av = getMainActor();
+	av->nextWeapon();
 	return false;
 }
 
diff --git a/engines/ultima/ultima8/misc/debugger.h b/engines/ultima/ultima8/misc/debugger.h
index d8ca15a7e9..d202bbab5d 100644
--- a/engines/ultima/ultima8/misc/debugger.h
+++ b/engines/ultima/ultima8/misc/debugger.h
@@ -214,6 +214,7 @@ private:
 	bool cmdUseRecall(int argc, const char **argv);
 	bool cmdUseBedroll(int argc, const char **argv);
 	bool cmdUseKeyring(int argc, const char **argv);
+	bool cmdNextWeapon(int argc, const char **argv);
 	bool cmdToggleCombat(int argc, const char **argv);
 
 	// Object Manager
diff --git a/engines/ultima/ultima8/ultima8.cpp b/engines/ultima/ultima8/ultima8.cpp
index 5f85340efa..364781c734 100644
--- a/engines/ultima/ultima8/ultima8.cpp
+++ b/engines/ultima/ultima8/ultima8.cpp
@@ -142,7 +142,7 @@ Ultima8Engine::Ultima8Engine(OSystem *syst, const Ultima::UltimaGameDescription
 		_kernel(nullptr), _objectManager(nullptr), _mouse(nullptr), _ucMachine(nullptr),
 		_screen(nullptr), _fontManager(nullptr), _paletteManager(nullptr), _gameData(nullptr),
 		_world(nullptr), _desktopGump(nullptr), _gameMapGump(nullptr), _avatarMoverProcess(nullptr),
-		_frameSkip(false), _frameLimit(true), _interpolate(true), _animationRate(100),
+		_frameSkip(false), _frameLimit(true), _interpolate(true), _animationRate(10),
 		_avatarInStasis(false), _paintEditorItems(false), _inversion(0), _painting(false),
 		_showTouching(false), _timeOffset(0), _hasCheated(false), _cheatsEnabled(false),
 		_ttfOverrides(false), _audioMixer(0), _scalerGump(nullptr),
diff --git a/engines/ultima/ultima8/world/actors/main_actor.cpp b/engines/ultima/ultima8/world/actors/main_actor.cpp
index badeef6ad0..5201470b71 100644
--- a/engines/ultima/ultima8/world/actors/main_actor.cpp
+++ b/engines/ultima/ultima8/world/actors/main_actor.cpp
@@ -611,6 +611,34 @@ void MainActor::addKeycard(int bitno) {
 	_keycards |= (1 << bitno);
 }
 
+static uint16 getIdOfNextItemInList(const Std::vector<Item *> &items, uint16 current) {
+	const int n = items.size();
+	if (n <= 1)
+		return current;
+
+	int i;
+	for (i = 0; i < n; i++) {
+		if (items[i]->getObjId() == current) {
+			i++;
+			break;
+		}
+	}
+	return items[i % n]->getObjId();
+}
+
+void MainActor::nextWeapon() {
+	Std::vector<Item *> weapons;
+	getItemsWithShapeFamily(weapons, ShapeInfo::SF_CRUWEAPON, true);
+	_activeWeapon = getIdOfNextItemInList(weapons, _activeWeapon);
+}
+
+void MainActor::nextInvItem() {
+	Std::vector<Item *> items;
+	getItemsWithShapeFamily(items, ShapeInfo::SF_CRUINVITEM, true);
+	_activeInvItem = getIdOfNextItemInList(items, _activeInvItem);
+}
+
+
 void MainActor::saveData(Common::WriteStream *ws) {
 	Actor::saveData(ws);
 	uint8 jt = _justTeleported ? 1 : 0;
diff --git a/engines/ultima/ultima8/world/actors/main_actor.h b/engines/ultima/ultima8/world/actors/main_actor.h
index eb00ccde64..6a96378065 100644
--- a/engines/ultima/ultima8/world/actors/main_actor.h
+++ b/engines/ultima/ultima8/world/actors/main_actor.h
@@ -126,6 +126,12 @@ public:
 		return _activeInvItem;
 	}
 
+	//!< Swap to the next active weapon (in Crusader)
+	void nextWeapon();
+
+	//!< Swap to the next inventory item (in Crusader)
+	void nextInvItem();
+
 	bool loadData(Common::ReadStream *rs, uint32 version);
 	void saveData(Common::WriteStream *ws) override;
 




More information about the Scummvm-git-logs mailing list