[Scummvm-git-logs] scummvm master -> 1fb7bf0940ff3bb367ea5932a086083a4b4117c2

mduggan mgithub at guarana.org
Wed Jun 17 09:03:49 UTC 2020


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

Summary:
d5bd2826f6 ULTIMA8: Store crusader globals in byteset rather than bitset
3e680ea65d ULTIMA8: More Crusader usecode for items
46f971d6c5 ULTIMA8: More crusader intrinsics supported
a4d204f8e0 ULTIMA8: Print offsets in shape viewer
580efb4a49 ULTIMA8: Fix free order of music stream
1fb7bf0940 ULTIMA8: Halve Crusader coords given to usecode


Commit: d5bd2826f6d1e431dc32f475bd7395955f59a268
    https://github.com/scummvm/scummvm/commit/d5bd2826f6d1e431dc32f475bd7395955f59a268
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2020-06-17T18:02:41+09:00

Commit Message:
ULTIMA8: Store crusader globals in byteset rather than bitset

The size parameter for Usecode globals in Crusader is only ever 1 or 2, so this
is a really minimal implementation.

Changed paths:
  A engines/ultima/ultima8/usecode/byte_set.cpp
  A engines/ultima/ultima8/usecode/byte_set.h
  A engines/ultima/ultima8/usecode/global_storage.h
    engines/ultima/module.mk
    engines/ultima/ultima8/misc/debugger.cpp
    engines/ultima/ultima8/usecode/bit_set.cpp
    engines/ultima/ultima8/usecode/bit_set.h
    engines/ultima/ultima8/usecode/uc_machine.cpp
    engines/ultima/ultima8/usecode/uc_machine.h


diff --git a/engines/ultima/module.mk b/engines/ultima/module.mk
index 7902f0b249..3bc82d415b 100644
--- a/engines/ultima/module.mk
+++ b/engines/ultima/module.mk
@@ -510,6 +510,7 @@ MODULE_OBJS := \
 	ultima8/misc/istring.o \
 	ultima8/misc/util.o \
 	ultima8/usecode/bit_set.o \
+	ultima8/usecode/byte_set.o \
 	ultima8/usecode/uc_list.o \
 	ultima8/usecode/uc_machine.o \
 	ultima8/usecode/uc_process.o \
diff --git a/engines/ultima/ultima8/misc/debugger.cpp b/engines/ultima/ultima8/misc/debugger.cpp
index 3a9e3ec996..82100d1c2d 100644
--- a/engines/ultima/ultima8/misc/debugger.cpp
+++ b/engines/ultima/ultima8/misc/debugger.cpp
@@ -1566,7 +1566,7 @@ bool Debugger::cmdGetGlobal(int argc, const char **argv) {
 	unsigned int offset = strtol(argv[1], 0, 0);
 	unsigned int size = strtol(argv[2], 0, 0);
 
-	debugPrintf("[%04X %02X] = %d\n", offset, size, uc->_globals->getBits(offset, size));
+	debugPrintf("[%04X %02X] = %d\n", offset, size, uc->_globals->getEntries(offset, size));
 	return true;
 }
 
@@ -1581,9 +1581,9 @@ bool Debugger::cmdSetGlobal(int argc, const char **argv) {
 	unsigned int size = strtol(argv[2], 0, 0);
 	unsigned int value = strtol(argv[3], 0, 0);
 
-	uc->_globals->setBits(offset, size, value);
+	uc->_globals->setEntries(offset, size, value);
 
-	debugPrintf("[%04X %02X] = %d\n", offset, size, uc->_globals->getBits(offset, size));
+	debugPrintf("[%04X %02X] = %d\n", offset, size, uc->_globals->getEntries(offset, size));
 	return true;
 }
 
diff --git a/engines/ultima/ultima8/usecode/bit_set.cpp b/engines/ultima/ultima8/usecode/bit_set.cpp
index dca9ec460f..ef108958c1 100644
--- a/engines/ultima/ultima8/usecode/bit_set.cpp
+++ b/engines/ultima/ultima8/usecode/bit_set.cpp
@@ -50,7 +50,7 @@ void BitSet::setSize(unsigned int size) {
 		_data[i] = 0;
 }
 
-uint32 BitSet::getBits(unsigned int pos, unsigned int n) const {
+uint32 BitSet::getEntries(unsigned int pos, unsigned int n) const {
 	assert(n <= 32);
 	assert(pos + n <= _size);
 	if (n == 0) return 0;
@@ -81,7 +81,7 @@ uint32 BitSet::getBits(unsigned int pos, unsigned int n) const {
 	return ret;
 }
 
-void BitSet::setBits(unsigned int pos, unsigned int n, uint32 bits) {
+void BitSet::setEntries(unsigned int pos, unsigned int n, uint32 bits) {
 	assert(n <= 32);
 	assert(pos + n <= _size);
 	if (n == 0) return;
diff --git a/engines/ultima/ultima8/usecode/bit_set.h b/engines/ultima/ultima8/usecode/bit_set.h
index a1c21df49a..cbe32a4a01 100644
--- a/engines/ultima/ultima8/usecode/bit_set.h
+++ b/engines/ultima/ultima8/usecode/bit_set.h
@@ -23,10 +23,12 @@
 #ifndef ULTIMA8_USECODE_BITSET_H
 #define ULTIMA8_USECODE_BITSET_H
 
+#include "ultima/ultima8/usecode/global_storage.h"
+
 namespace Ultima {
 namespace Ultima8 {
 
-class BitSet {
+class BitSet : public GlobalStorage {
 public:
 	BitSet();
 	BitSet(unsigned int size);
@@ -34,22 +36,22 @@ public:
 
 	//! set the size. The old value is cleared
 	//! \param size the new size (in bits)
-	void setSize(unsigned int size);
+	void setSize(unsigned int size) override;
 
 	//! get a value
 	//! \param pos zero-based position (in bits)
 	//! \param n number of bits (no greater than 32)
 	//! \return the value these bits represent
-	uint32 getBits(unsigned int pos, unsigned int n) const;
+	uint32 getEntries(unsigned int pos, unsigned int n) const override;
 
 	//! set a value
 	//! \param pos zero-based position (in bits)
 	//! \param n number of bits (no greater than 32)
 	//! \param bits the value to set
-	void setBits(unsigned int pos, unsigned int n, uint32 bits);
+	void setEntries(unsigned int pos, unsigned int n, uint32 bits) override;
 
-	void save(Common::WriteStream *ws);
-	bool load(Common::ReadStream *rs, uint32 version);
+	void save(Common::WriteStream *ws) override;
+	bool load(Common::ReadStream *rs, uint32 version) override;
 
 private:
 	unsigned int _size;
diff --git a/engines/ultima/ultima8/usecode/byte_set.cpp b/engines/ultima/ultima8/usecode/byte_set.cpp
new file mode 100644
index 0000000000..8f9c6cbf53
--- /dev/null
+++ b/engines/ultima/ultima8/usecode/byte_set.cpp
@@ -0,0 +1,90 @@
+/* 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/usecode/byte_set.h"
+
+namespace Ultima {
+namespace Ultima8 {
+
+ByteSet::ByteSet() : _size(0), _data(nullptr) {
+}
+
+
+ByteSet::ByteSet(unsigned int size) : _data(nullptr) {
+	setSize(size);
+}
+
+ByteSet::~ByteSet() {
+	delete[] _data;
+}
+
+void ByteSet::setSize(unsigned int size) {
+	if (_data) delete[] _data;
+
+	_size = size;
+	_data = new uint8[_size];
+	for (unsigned int i = 0; i < _size; ++i)
+		_data[i] = 0;
+}
+
+uint32 ByteSet::getEntries(unsigned int pos, unsigned int n) const {
+	assert(n <= 2);
+	assert(pos + n <= _size);
+	if (n == 0) return 0;
+
+    if (n == 1) {
+        return _data[pos];
+    } else if (n == 2) {
+        return (_data[pos] << 8) | _data[pos + 1];
+    }
+	return 0;
+}
+
+void ByteSet::setEntries(unsigned int pos, unsigned int n, uint32 val) {
+	assert(n <= 2);
+	assert(pos + n <= _size);
+	if (n == 0) return;
+
+    if (n == 1) {
+        _data[pos] = static_cast<uint8>(val);
+    } else if (n == 2) {
+        _data[pos] = static_cast<uint8>((val & 0xFF00) >> 8);
+        _data[pos + 1] = static_cast<uint8>(val & 0xFF);
+    }
+}
+
+void ByteSet::save(Common::WriteStream *ws) {
+	ws->writeUint32LE(_size);
+	ws->write(_data, _size);
+}
+
+bool ByteSet::load(Common::ReadStream *rs, uint32 version) {
+	uint32 s = rs->readUint32LE();
+	setSize(s);
+	rs->read(_data, _size);
+
+	return true;
+}
+
+} // End of namespace Ultima8
+} // End of namespace Ultima
diff --git a/engines/ultima/ultima8/usecode/byte_set.h b/engines/ultima/ultima8/usecode/byte_set.h
new file mode 100644
index 0000000000..ac13356295
--- /dev/null
+++ b/engines/ultima/ultima8/usecode/byte_set.h
@@ -0,0 +1,66 @@
+/* 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 ULTIMA8_USECODE_BYTESET_H
+#define ULTIMA8_USECODE_BYTESET_H
+
+#include "ultima/ultima8/usecode/global_storage.h"
+
+namespace Ultima {
+namespace Ultima8 {
+
+// This is a minimal implementation just to support what Crusader needs.
+
+class ByteSet : public GlobalStorage {
+public:
+	ByteSet();
+	ByteSet(unsigned int size);
+	~ByteSet();
+
+	//! set the size. The old value is cleared
+	//! \param size the new size (in bytes)
+	void setSize(unsigned int size) override;
+
+	//! get a value
+	//! \param pos zero-based position (in bytes:)
+	//! \param n number of bytes (no greater than 2)
+	//! \return the value these bytes represent
+	uint32 getEntries(unsigned int pos, unsigned int n) const override;
+
+	//! set a value
+	//! \param pos zero-based position (in bytes)
+	//! \param n number of bytes (no greater than 2)
+	//! \param val the value to set
+	void setEntries(unsigned int pos, unsigned int n, uint32 val) override;
+
+	void save(Common::WriteStream *ws) override;
+	bool load(Common::ReadStream *rs, uint32 version) override;
+
+private:
+	unsigned int _size;
+	uint8 *_data;
+};
+
+} // End of namespace Ultima8
+} // End of namespace Ultima
+
+#endif
diff --git a/engines/ultima/ultima8/usecode/global_storage.h b/engines/ultima/ultima8/usecode/global_storage.h
new file mode 100644
index 0000000000..38223e12ac
--- /dev/null
+++ b/engines/ultima/ultima8/usecode/global_storage.h
@@ -0,0 +1,54 @@
+/* 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 ULTIMA8_USECODE_GLOBAL_STORAGE_H
+#define ULTIMA8_USECODE_GLOBAL_STORAGE_H
+
+namespace Ultima {
+namespace Ultima8 {
+
+class GlobalStorage {
+public:
+	virtual ~GlobalStorage() {};
+
+	virtual void setSize(unsigned int size) = 0;
+
+	//! get a value
+	//! \param pos zero-based position
+	//! \param n number of entries to read
+	//! \return the value these entries represent
+	virtual uint32 getEntries(unsigned int pos, unsigned int n) const = 0;
+
+	//! set a value
+	//! \param pos zero-based position
+	//! \param n number of entries (no greater than one uint32-worth)
+	//! \param val the value to set
+	virtual void setEntries(unsigned int pos, unsigned int n, uint32 val) = 0;
+
+	virtual void save(Common::WriteStream *ws) = 0;
+	virtual bool load(Common::ReadStream *rs, uint32 version) = 0;
+};
+
+} // End of namespace Ultima8
+} // End of namespace Ultima
+
+#endif
diff --git a/engines/ultima/ultima8/usecode/uc_machine.cpp b/engines/ultima/ultima8/usecode/uc_machine.cpp
index 5a8d51cdb4..2f89cf066e 100644
--- a/engines/ultima/ultima8/usecode/uc_machine.cpp
+++ b/engines/ultima/ultima8/usecode/uc_machine.cpp
@@ -31,6 +31,7 @@
 #include "ultima/ultima8/world/current_map.h"
 #include "ultima/ultima8/world/world.h"
 #include "ultima/ultima8/usecode/bit_set.h"
+#include "ultima/ultima8/usecode/byte_set.h"
 #include "ultima/ultima8/usecode/uc_list.h"
 #include "ultima/ultima8/misc/id_man.h"
 #include "ultima/ultima8/world/get_object.h"
@@ -87,13 +88,15 @@ UCMachine::UCMachine(Intrinsic *iset, unsigned int icount) {
 	_ucMachine = this;
 
 	// zero _globals
-	_globals = new BitSet(0x1000);
 
 	if (GAME_IS_U8) {
+		_globals = new BitSet(0x1000);
 		_convUse = new ConvertUsecodeU8();
 	} else if (GAME_IS_REMORSE) {
+		_globals = new ByteSet(0x1000);
 		_convUse = new ConvertUsecodeCrusader();
 	} else {
+		_globals = new ByteSet(0x1000);
 		// TODO: Need a separate convertor for Regret
 		_convUse = new ConvertUsecodeCrusader();
 	}
@@ -409,7 +412,7 @@ void UCMachine::execProcess(UCProcess *p) {
 			// https://sourceforge.net/tracker/index.php?func=detail&aid=1018748&group_id=53819&atid=471709
 			if (GAME_IS_U8 && p->_classId == 0x48B && func == 0xD0) {
 				// 0xD0 = setAvatarInStasis
-				_globals->setBits(0, 1, 1);
+				_globals->setEntries(0, 1, 1);
 			}
 
 		}
@@ -1225,7 +1228,7 @@ void UCMachine::execProcess(UCProcess *p) {
 			ui16b = cs.readByte();
 			// TODO: get flagname for output?
 
-			ui32a = _globals->getBits(ui16a, ui16b);
+			ui32a = _globals->getEntries(ui16a, ui16b);
 			p->_stack.push2(static_cast<uint16>(ui32a));
 			LOGPF(("push\t\tglobal [%04X %02X] = %02X\n", ui16a, ui16b, ui32a));
 			break;
@@ -1237,15 +1240,19 @@ void UCMachine::execProcess(UCProcess *p) {
 			ui16b = cs.readByte();
 			// TODO: get flagname for output?
 			ui32a = p->_stack.pop2();
-			_globals->setBits(ui16a, ui16b, ui32a);
+			_globals->setEntries(ui16a, ui16b, ui32a);
 
-			if (ui32a & ~(((1 << ui16b) - 1))) {
-				perr << "Warning: value popped into a bitflag it doesn't fit in (" << Std::hex
+			if ((GAME_IS_U8 && (ui32a & ~(((1 << ui16b) - 1)))) || ui16b > 2) {
+				perr << "Warning: value popped into a flag it doesn't fit in (" << Std::hex
 					 << ui16a << " " << ui16b << " " << ui32a << ")" << Std::endl;
 			}
 
 			// paranoid :-)
-			assert(_globals->getBits(ui16a, ui16b) == (ui32a & ((1 << ui16b) - 1)));
+			if (GAME_IS_U8) {
+				assert(_globals->getEntries(ui16a, ui16b) == (ui32a & ((1 << ui16b) - 1)));
+			} else {
+				assert(_globals->getEntries(ui16a, ui16b) == ui32a);
+		    }
 
 			LOGPF(("pop\t\tglobal [%04X %02X] = %02X\n", ui16a, ui16b, ui32a));
 			break;
diff --git a/engines/ultima/ultima8/usecode/uc_machine.h b/engines/ultima/ultima8/usecode/uc_machine.h
index ea974fe8cc..528b2988a4 100644
--- a/engines/ultima/ultima8/usecode/uc_machine.h
+++ b/engines/ultima/ultima8/usecode/uc_machine.h
@@ -34,7 +34,7 @@ class Debugger;
 class Process;
 class UCProcess;
 class ConvertUsecode;
-class BitSet;
+class GlobalStorage;
 class UCList;
 class idMan;
 
@@ -98,7 +98,7 @@ private:
 	Intrinsic *_intrinsics;
 	unsigned int _intrinsicCount;
 
-	BitSet *_globals;
+	GlobalStorage *_globals;
 
 	Std::map<uint16, UCList *> _listHeap;
 	Std::map<uint16, Std::string> _stringHeap;


Commit: 3e680ea65df5aeb8c0a8f2ca24acfeffce455d3f
    https://github.com/scummvm/scummvm/commit/3e680ea65df5aeb8c0a8f2ca24acfeffce455d3f
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2020-06-17T18:02:41+09:00

Commit Message:
ULTIMA8: More Crusader usecode for items

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


diff --git a/engines/ultima/ultima8/convert/crusader/convert_usecode_crusader.h b/engines/ultima/ultima8/convert/crusader/convert_usecode_crusader.h
index 2cee3110f5..9f59d76761 100644
--- a/engines/ultima/ultima8/convert/crusader/convert_usecode_crusader.h
+++ b/engines/ultima/ultima8/convert/crusader/convert_usecode_crusader.h
@@ -160,7 +160,7 @@ const char* const ConvertUsecodeCrusader::_intrinsics[] = {
 	"void I_NPCSetActivityProbably_055(Actor *, int)", // part of same coff set 055, 07D, 0CD, 0DB, 0F2, 131
 	"void Intrinsic056(2 bytes)",
 	"int16 Item::I_getSOMETHING_57(Item *)",
-	"byte Item::I_doSOMETHING_58(Item *, uint16 unk)",
+	"byte Item::Item::I_isCentreOn(Item *, uint16 other)",
 	"void Item::I_setFrame(Item *, frame)", // based on same coff as 002
 	"int16 Actor::I_getLastAnimSet(4 bytes)", // part of same coff set 01D, 05A, 0B9, 0D7, 0E4, 124
 	"byte Item::I_legalCreateAtPoint(Item *, int16 shape, int16 frame, Point *)", // see PEPSIEW::use
@@ -388,7 +388,7 @@ const char* const ConvertUsecodeCrusader::_intrinsics[] = {
 	"byte Item::I_isOn(Item *, itemno)", // part of same coff set 044, 046, 048, 04A, 04C, 04E, 0A5, 0BC, 0C5, 0DC, 0F1, 0FA, 12C
 	"void Item::I_getFootpadData(Item *, uint *, uint *, uint *)", // same coff as 064
 	"byte Actor::I_isDead(Item *)", // same coff as 122, 039
-	"int16 Intrinsic12F(Item *, uint16 other_itemno)",
+	"int16 MonsterEgg::I_monsterEggHatch(Item *, uint16 other_itemno)",
 	// 0130
 	"void Actor::I_clrImmortal(Actor *)", // same coff as 07B
 	"void I_NPCSetActivityProbably_131(Actor *, int)", // part of same coff set 055, 07D, 0CD, 0DB, 0F2, 131
diff --git a/engines/ultima/ultima8/usecode/remorse_intrinsics.h b/engines/ultima/ultima8/usecode/remorse_intrinsics.h
index 4063561c76..50bf996bf4 100644
--- a/engines/ultima/ultima8/usecode/remorse_intrinsics.h
+++ b/engines/ultima/ultima8/usecode/remorse_intrinsics.h
@@ -128,7 +128,7 @@ Intrinsic RemorseIntrinsics[] = {
 	0, // void Intrinsic055(6 bytes)
 	0, // void Intrinsic056(2 bytes)
 	0, // void Intrinsic057(4 bytes)
-	0, // int Intrinsic058(6 bytes)
+	Item::I_isCentreOn, // int Intrinsic058(6 bytes)
 	Item::I_setFrame, // based on same coff as 002
 	Actor::I_getLastAnimSet, // void Intrinsic05A(4 bytes)
 	Item::I_legalCreateAtPoint, // probably. see PEPSIEW::use
diff --git a/engines/ultima/ultima8/world/item.cpp b/engines/ultima/ultima8/world/item.cpp
index 8c20e0e25b..a7b484f578 100644
--- a/engines/ultima/ultima8/world/item.cpp
+++ b/engines/ultima/ultima8/world/item.cpp
@@ -462,7 +462,7 @@ void Item::setShape(uint32 shape_) {
 	_cachedShape = nullptr;
 }
 
-bool Item::overlaps(Item &item2) const {
+bool Item::overlaps(const Item &item2) const {
 	int32 x1a, y1a, z1a, x1b, y1b, z1b;
 	int32 x2a, y2a, z2a, x2b, y2b, z2b;
 	getLocation(x1b, y1b, z1a);
@@ -485,7 +485,7 @@ bool Item::overlaps(Item &item2) const {
 	return true;
 }
 
-bool Item::overlapsxy(Item &item2) const {
+bool Item::overlapsxy(const Item &item2) const {
 	int32 x1a, y1a, z1a, x1b, y1b;
 	int32 x2a, y2a, z2a, x2b, y2b;
 	getLocation(x1b, y1b, z1a);
@@ -505,7 +505,7 @@ bool Item::overlapsxy(Item &item2) const {
 	return true;
 }
 
-bool Item::isOn(Item &item2) const {
+bool Item::isOn(const Item &item2) const {
 	int32 x1a, y1a, z1a, x1b, y1b;
 	int32 x2a, y2a, z2a, x2b, y2b, z2b;
 	getLocation(x1b, y1b, z1a);
@@ -527,6 +527,49 @@ bool Item::isOn(Item &item2) const {
 	return false;
 }
 
+bool Item::isCompletelyOn(const Item &item2) const {
+	// FIXME: this is just a copy of isOn at the moment
+	int32 x1a, y1a, z1a, x1b, y1b;
+	int32 x2a, y2a, z2a, x2b, y2b, z2b;
+	getLocation(x1b, y1b, z1a);
+	item2.getLocation(x2b, y2b, z2a);
+
+	int32 xd, yd, zd;
+	getFootpadWorld(xd, yd, zd);
+	x1a = x1b - xd;
+	y1a = y1b - yd;
+
+	item2.getFootpadWorld(xd, yd, zd);
+	x2a = x2b - xd;
+	y2a = y2b - yd;
+	z2b = z2a + zd;
+
+	if (x1b <= x2a || x2b <= x1a) return false;
+	if (y1b <= y2a || y2b <= y1a) return false;
+	if (z2b == z1a) return true;
+	return false;
+}
+
+bool Item::isCentreOn(const Item &item2) const {
+	int32 x1c, y1c, z1c;
+	int32 x2a, y2a, z2a, x2b, y2b, z2b;
+	item2.getLocation(x2b, y2b, z2a);
+
+	getCentre(x1c, y1c, z1c);
+
+	int32 xd, yd, zd;
+	item2.getFootpadWorld(xd, yd, zd);
+	x2a = x2b - xd;
+	y2a = y2b - yd;
+	z2b = z2a + zd;
+
+	if (x1c <= x2a || x2b <= x1c) return false;
+	if (y1c <= y2a || y2b <= y1c) return false;
+	if (z2b == z1c) return true;
+	return false;
+}
+
+
 bool Item::canExistAt(int32 x_, int32 y_, int32 z_, bool needsupport) const {
 	CurrentMap *cm = World::get_instance()->getCurrentMap();
 	const Item *support;
@@ -2492,6 +2535,18 @@ uint32 Item::I_isOn(const uint8 *args, unsigned int /*argsize*/) {
 		return 0;
 }
 
+uint32 Item::I_isCentreOn(const uint8 *args, unsigned int /*argsize*/) {
+	ARG_ITEM_FROM_PTR(item);
+	ARG_ITEM_FROM_ID(item2);
+	if (!item) return 0;
+	if (!item2) return 0;
+
+	if (item->isCentreOn(*item2))
+		return 1;
+	else
+		return 0;
+}
+
 uint32 Item::I_getFamilyOfType(const uint8 *args, unsigned int /*argsize*/) {
 	ARG_UINT16(shape);
 
diff --git a/engines/ultima/ultima8/world/item.h b/engines/ultima/ultima8/world/item.h
index aa02961970..4c92ac7561 100644
--- a/engines/ultima/ultima8/world/item.h
+++ b/engines/ultima/ultima8/world/item.h
@@ -263,13 +263,19 @@ public:
 	virtual void destroy(bool delnow = false);
 
 	//! Check if this item overlaps another item in 3D world-space
-	bool overlaps(Item &item2) const;
+	bool overlaps(const Item &item2) const;
 
 	//! Check if this item overlaps another item in the xy dims in 3D space
-	bool overlapsxy(Item &item2) const;
+	bool overlapsxy(const Item &item2) const;
 
-	//! Check if this item is on top another item
-	bool isOn(Item &item2) const;
+	//! Check if this item is on top of another item
+	bool isOn(const Item &item2) const;
+
+	//! Check if this item is on completely on top of another item
+	bool isCompletelyOn(const Item &item2) const;
+
+	//! Check if the centre of this item is on top of another item
+	bool isCentreOn(const Item &item2) const;
 
 	//! Check if this item can exist at the given coordinates
 	bool canExistAt(int32 x_, int32 y_, int32 z_, bool needsupport = false) const;
@@ -487,6 +493,8 @@ public:
 	INTRINSIC(I_overlaps);
 	INTRINSIC(I_overlapsXY);
 	INTRINSIC(I_isOn);
+	INTRINSIC(I_isCompletelyOn);
+	INTRINSIC(I_isCentreOn);
 	INTRINSIC(I_ascend);
 	INTRINSIC(I_getWeight);
 	INTRINSIC(I_getWeightIncludingContents);


Commit: 46f971d6c506e997f92f53d87a15b453aa689956
    https://github.com/scummvm/scummvm/commit/46f971d6c506e997f92f53d87a15b453aa689956
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2020-06-17T18:02:41+09:00

Commit Message:
ULTIMA8: More crusader intrinsics supported

Changed paths:
    engines/ultima/ultima8/convert/crusader/convert_usecode_crusader.h
    engines/ultima/ultima8/graphics/anim_dat.cpp
    engines/ultima/ultima8/graphics/palette_fader_process.cpp
    engines/ultima/ultima8/usecode/remorse_intrinsics.h
    engines/ultima/ultima8/world/actors/actor.cpp
    engines/ultima/ultima8/world/actors/anim_action.h
    engines/ultima/ultima8/world/item.cpp
    engines/ultima/ultima8/world/item.h


diff --git a/engines/ultima/ultima8/convert/crusader/convert_usecode_crusader.h b/engines/ultima/ultima8/convert/crusader/convert_usecode_crusader.h
index 9f59d76761..8613acdaa4 100644
--- a/engines/ultima/ultima8/convert/crusader/convert_usecode_crusader.h
+++ b/engines/ultima/ultima8/convert/crusader/convert_usecode_crusader.h
@@ -74,7 +74,7 @@ const char* const ConvertUsecodeCrusader::_intrinsics[] = {
 	"int16 Item::I_getStatus(Item *)",
 	"void Item::I_orStatus(Item *, uint16 flags)",
 	"int16 Intrinsic006(6 bytes)", // same coff as 0B5
-	"byte Item::I_getSOMETHING_07(Item *)", // called for gattling guns and camera
+	"byte Item::I_isOnScreen(Item *)", // called for gattling guns and camera
 	"byte Actor::I_isNPC(Item *)", // proably - actually checks is itemno < 256?
 	"byte Item::I_getZ(Item *)",
 	"void Item::I_destroy(Item *)", // probably? often called after creating a replacement object and setting it to the same position (eg, LUGGAGE::gotHit)
@@ -108,7 +108,7 @@ const char* const ConvertUsecodeCrusader::_intrinsics[] = {
 	"void Item::I_setShape(Item *, int16 shapeno)", // probably. See PEPSIEW::gotHit.
 	"void Item::I_touch(Item *)", // same code as U8
 	"int16 Item::I_getQHi(Item *)", // guess, based on variable name in BOUNCBOX::gotHit
-	"int16 I_getDirectionSomething(x1, y1, x2, y2, numdirs, aa, bb)",  // TODO: understand the decompile of this better.. what is it doing with aa and bb?
+	"int16 I_getClosestDirectionInRange(x1, y1, x2, y2, numdirs, aa, bb)",  // TODO: understand the decompile of this better.. what is it doing with aa and bb?
 	"int16 Item::I_hurl(Item *,8 bytes)", // part of same coff set 028, 08D, 0BD, 0C0, 0C2, 0C8, 0F7, 0F9, 118, 11D
 	"int16 Game::I_getDifficultyLevel(void)",
 	"void AudioProcess::I_playAmbientSFXCru(Item *, sndno)",
@@ -122,7 +122,7 @@ const char* const ConvertUsecodeCrusader::_intrinsics[] = {
 	"void Item::I_andStatus(Item *, uint16 status)", // part of same coff set 01A, 031, 069, 06E, 099, 0B2, 0BF, 0C1, 0C3, 0E9, 0FC, 101, 104, 106, 108, 10A, 10C, 10E, 110, 114, 117, 11A, 128, 132
 	"void Item::I_receiveHit(Item *, other, dir, damage, damagetype)", // based on disasm
 	"byte Actor::I_isBusy(4 bytes)", // same code as U8
-	"int16 Actor::I_getDir16(x1, y1, x2, y2)",
+	"int16 Item::I_getDirFromTo16(x1, y1, x2, y2)",
 	"byte Actor::I_getSomeFlagMaybeCrouch(Item *)",
 	"int16 Actor::I_doAnim(12 bytes)", // v. similar code to U8
 	"byte Intrinsic037(4 bytes)", // same coff as 0B8
@@ -193,10 +193,10 @@ const char* const ConvertUsecodeCrusader::_intrinsics[] = {
 	"void Ultima8Engine::I_clrUnkCrusaderFlag(void)",
 	"void Ultima8Engine::I_clrAvatarInStasis(void)",
 	"void AudioProcess::I_stopSFX(Item *)",
-	"int16 Intrinsic077_Fade(void)", // something about fades
+	"int16 PaletteFaderProcess::I_fadeToBlack(void)", // fade to black, no args (40 frames)
 	"void Intrinsic078(void)",
 	"int16 MainActor::I_teleportToEgg(int, int, int)",
-	"int16 Intrinsic07A_Fade(void)", // something about fades
+	"int16 PaletteFaderProcess::I_fadeFromBlack(void)", // from black, no arg (40 frames)
 	"void Actor::I_clrImmortal(Actor *)", // same coff as 130
 	"int16 I_GetNPCDataField0_07C(Actor *)",
 	"void I_NPCSetActivityProbably_07D(Actor *, int)", // part of same coff set 055, 07D, 0CD, 0DB, 0F2, 131
@@ -218,7 +218,7 @@ const char* const ConvertUsecodeCrusader::_intrinsics[] = {
 	"void Item::I_doSomethingAndSetStatusFlag0x8000(Item *)", // same coff as 119, 12A
 	"int16 Item::I_hurl(Item *,8 bytes)", // part of same coff set 028, 08D, 0BD, 0C0, 0C2, 0C8, 0F7, 0F9, 118, 11D
 	"int16 Item::I_getNPCNum(Item *)", // part of same coff set 067, 06D, 089, 08E, 0AD, 0F8, 100, 102, 105, 107, 109, 10B, 10D, 10F, 111, 115, 11C, 123, 129
-	"void Intrinsic08F_Fade(void)", // something about fades
+	"void PaletteFaderProcess::I_setPalToAllBlack(void)",
 	// 0090
 	"void Intrinsic090(void)",
 	"void I_setSomeMovieGlobal(void)", // sets some global (cleared by 93)
@@ -230,12 +230,12 @@ const char* const ConvertUsecodeCrusader::_intrinsics[] = {
 	"void Intrinsic097(void)",
 	"void I_resetVargasHealthTo500(void)",
 	"void Item::I_andStatus(Item *, uint16 status)", // part of same coff set 01A, 031, 069, 06E, 099, 0B2, 0BF, 0C1, 0C3, 0E9, 0FC, 101, 104, 106, 108, 10A, 10C, 10E, 110, 114, 117, 11A, 128, 132
-	"void Intrinsic09A_Fade(void)", // something about fades
-	"int16 Intrinsic09B_Fade(2 bytes)", // something about fades
-	"int16 Intrinsic09C_Fade(4 bytes)", // something about fades
-	"int16 Intrinsic09D_Fade(2 bytes)", // something about fades
-	"int16 Intrinsic09E_Fade(4 bytes)", // something about fades
-	"int16 Intrinsic09F_Fade(10 bytes)", // something about fades
+	"void PaletteFaderProcess::I_stopFadesAndResetToGamePal(void)",
+	"int16 PaletteFaderProcess::I_fadeFromBlack(nsteps)",
+	"int16 PaletteFaderProcess::I_fadeFromBlackWithParam(nsteps, unk)", // TODO: what's the param?
+	"int16 PaletteFaderProcess::I_fadeToBlack(nsteps)",
+	"int16 PaletteFaderProcess::I_fadeToBlackWithParam(nsteps, unk)", // TODO: what's the param?
+	"int16 PaletteFaderProcess::I_fadeToColor(r, g, b, nsteps, unk)", // TODO: what's the other param?
 	// 00A0
 	"void I_SetItemFlag0x8000AndNPCField0x13Flag0_0A0(Actor *)", // part of same coff set 021, 060, 073, 0A0, 0A8, 0D8, 0E7, 135
 	"int16 Item::I_getQLo(Item *)", // same as 02B based on same coff set 010, 02B, 066, 084, 0A1, 0AE, 0D9, 0EA
@@ -358,7 +358,7 @@ const char* const ConvertUsecodeCrusader::_intrinsics[] = {
 	// 0110
 	"void Item::I_andStatus(Item *, uint16 status)", // part of same coff set 01A, 031, 069, 06E, 099, 0B2, 0BF, 0C1, 0C3, 0E9, 0FC, 101, 104, 106, 108, 10A, 10C, 10E, 110, 114, 117, 11A, 128, 132
 	"int16 Item::I_getNPCNum(Item *)", // part of same coff set 067, 06D, 089, 08E, 0AD, 0F8, 100, 102, 105, 107, 109, 10B, 10D, 10F, 111, 115, 11C, 123, 129
-	"byte Actor::I_getDir(4 bytes)", // same coff as 01C, 121
+	"byte Actor::I_getDir(Actor *)", // same coff as 01C, 121
 	"int16 UCMachine::I_numToStr(int16 num)", // based on VMAIL::func0A example usage
 	"void Item::I_andStatus(Item *, uint16 status)", // part of same coff set 01A, 031, 069, 06E, 099, 0B2, 0BF, 0C1, 0C3, 0E9, 0FC, 101, 104, 106, 108, 10A, 10C, 10E, 110, 114, 117, 11A, 128, 132
 	"int16 Item::I_getNPCNum(Item *)", // part of same coff set 067, 06D, 089, 08E, 0AD, 0F8, 100, 102, 105, 107, 109, 10B, 10D, 10F, 111, 115, 11C, 123, 129
diff --git a/engines/ultima/ultima8/graphics/anim_dat.cpp b/engines/ultima/ultima8/graphics/anim_dat.cpp
index 32270aa36b..bb85dbd7e5 100644
--- a/engines/ultima/ultima8/graphics/anim_dat.cpp
+++ b/engines/ultima/ultima8/graphics/anim_dat.cpp
@@ -175,6 +175,8 @@ void AnimDat::load(Common::SeekableReadStream *rs) {
 						f._deltaDir = rs->readSByte();
 						f._flags = rs->readByte();
 						f._flags += (x & 0xF8) << 8;
+						f._unk1 = 0;
+						f._unk2 = 0;
 					} else if (GAME_IS_CRUSADER) {
 						// byte 0: low byte of frame
 						f._frame = rs->readByte();
@@ -182,13 +184,13 @@ void AnimDat::load(Common::SeekableReadStream *rs) {
 						uint8 x = rs->readByte();
 						f._frame += (x & 0xF) << 8;
 						// byte 2, 3: unknown; byte 3 might contain flags
-						rs->skip(2);
+						f._unk1 = rs->readSint16LE();
 						// byte 4: deltadir (signed)
 						f._deltaDir = rs->readSByte();
 						// byte 5: flags?
 						f._flags = rs->readByte();
 						// byte 6, 7: unknown
-						rs->skip(2);
+						f._unk2 = rs->readSint16LE();
 
 						f._deltaZ = 0;
 						f._sfx = 0;
diff --git a/engines/ultima/ultima8/graphics/palette_fader_process.cpp b/engines/ultima/ultima8/graphics/palette_fader_process.cpp
index 8ddd1ccfd2..d6082ed774 100644
--- a/engines/ultima/ultima8/graphics/palette_fader_process.cpp
+++ b/engines/ultima/ultima8/graphics/palette_fader_process.cpp
@@ -24,6 +24,7 @@
 #include "ultima/ultima8/graphics/palette_fader_process.h"
 #include "ultima/ultima8/kernel/kernel.h"
 #include "ultima/ultima8/graphics/palette.h"
+#include "ultima/ultima8/kernel/core_app.h"
 
 namespace Ultima {
 namespace Ultima8 {
@@ -141,21 +142,33 @@ uint32 PaletteFaderProcess::I_fadeToPaletteTransform(const uint8 *args,
 	return Kernel::get_instance()->addProcess(_fader);
 }
 
-uint32 PaletteFaderProcess::I_fadeToBlack(const uint8 * /*args*/,
-        unsigned int /*argsize*/) {
+uint32 PaletteFaderProcess::I_fadeToBlack(const uint8 *args,
+        unsigned int argsize) {
 	if (_fader && _fader->_priority > 0x7FFF) return 0;
 	else if (_fader) _fader->terminate();
 
-	_fader = new PaletteFaderProcess(0x00000000, false, 0x7FFF, 30, true);
+	int nsteps = (GAME_IS_U8 ? 30 : 40);
+	if (argsize > 0) {
+		ARG_UINT16(n);
+		nsteps = n;
+	}
+
+	_fader = new PaletteFaderProcess(0x00000000, false, 0x7FFF, nsteps, true);
 	return Kernel::get_instance()->addProcess(_fader);
 }
 
-uint32 PaletteFaderProcess::I_fadeFromBlack(const uint8 * /*args*/,
-        unsigned int /*argsize*/) {
+uint32 PaletteFaderProcess::I_fadeFromBlack(const uint8 *args,
+        unsigned int argsize) {
 	if (_fader && _fader->_priority > 0x7FFF) return 0;
 	else if (_fader) _fader->terminate();
 
-	_fader = new PaletteFaderProcess(0x00000000, true, 0x7FFF, 30, false);
+	int nsteps = (GAME_IS_U8 ? 30 : 40);
+	if (argsize > 0) {
+		ARG_UINT16(n);
+		nsteps = n;
+	}
+
+	_fader = new PaletteFaderProcess(0x00000000, true, 0x7FFF, nsteps, false);
 	return Kernel::get_instance()->addProcess(_fader);
 }
 
diff --git a/engines/ultima/ultima8/usecode/remorse_intrinsics.h b/engines/ultima/ultima8/usecode/remorse_intrinsics.h
index 50bf996bf4..69b409aabd 100644
--- a/engines/ultima/ultima8/usecode/remorse_intrinsics.h
+++ b/engines/ultima/ultima8/usecode/remorse_intrinsics.h
@@ -42,7 +42,7 @@ Intrinsic RemorseIntrinsics[] = {
 	Item::I_getStatus, // probably - see usage in GATGUNEW::enterFastArea - always followed by an AND against a single bit
 	Item::I_orStatus, // probably - see usage in GATGUNEW::enterFastArea
 	Item::I_equip, // void Intrinsic006(6 bytes)
-	0, // ? byte Item::I_getSOMETHING_07(Item *)
+	Item::I_isOnScreen, //
 	Actor::I_isNPC, // byte Intrinsic008(Item *) // probably.. disasm checks for < 256
 	Item::I_getZ, // byte Intrinsic009(4 bytes) // probably, see PEPSIEW::use() variable names
 	Item::I_destroy, // void Intrinsic00A(4 bytes) // probably, often called after creating replacement object in same position eg, LUGGAGE::gotHit
@@ -76,7 +76,7 @@ Intrinsic RemorseIntrinsics[] = {
 	Item::I_setShape, // Probably, see PEPSIEW::gotHit
 	Item::I_touch,
 	Item::I_getQHi, // int16 Intrinsic026(Item *), // guess, based on variable name in BOUNCBOX::gotHit
-	0, // int Intrinsic027(14 bytes)
+	Item::I_getClosestDirectionInRange, // int Intrinsic027(14 bytes)
 	Item::I_hurl, // int Intrinsic028(12 bytes)
 	UCMachine::I_true, // TODO: This is actually game difficulty level.  Make an intrinsic for that once it's implemented (for now return 1, easiest difficulty).
 	AudioProcess::I_playAmbientSFXCru, // Confirmed!
@@ -90,7 +90,7 @@ Intrinsic RemorseIntrinsics[] = {
 	Item::I_andStatus,
 	Item::I_receiveHit, // void Intrinsic032(12 bytes)
 	Actor::I_isBusy, // int Intrinsic033(4 bytes)
-	0, // TODO: void Actor::I_getDir16(8 bytes)
+	Item::I_getDirFromTo16,
 	0, // int Intrinsic035(4 bytes)
 	Actor::I_doAnim, // void Intrinsic036(12 bytes)
 	0, // int Intrinsic037(4 bytes)
@@ -161,10 +161,10 @@ Intrinsic RemorseIntrinsics[] = {
 	Ultima8Engine::I_clrUnkCrusaderFlag, // void Intrinsic074(void)
 	Ultima8Engine::I_clrAvatarInStasis,
 	AudioProcess::I_stopSFXCru, // takes Item *, from disasm
-	0, // void Intrinsic077(void)
+	PaletteFaderProcess::I_fadeToBlack, // void Intrinsic077(void)
 	0, // void Intrinsic078(void)
 	MainActor::I_teleportToEgg, // different than U8's? void Intrinsic079(6 bytes)
-	0, // void Intrinsic07A(void)
+	PaletteFaderProcess::I_fadeFromBlack, // void Intrinsic07A(void)
 	Actor::I_clrImmortal, // based on disasm
 	0, // void Intrinsic07C(4 bytes) // I_getQIfSomething, see disassembly
 	0, // void Intrinsic07D(6 bytes)
@@ -186,7 +186,7 @@ Intrinsic RemorseIntrinsics[] = {
 	Item::I_doSomethingAndSetUnkCruFlag, // void Intrinsic08C(4 bytes)
 	Item::I_hurl, // void Intrinsic08D(12 bytes)
 	Item::I_getNpcNum, // based on same coff as 102 (-> variable name in TRIGGER::ordinal21)
-	0, // void Intrinsic08F(void)
+	0, // TODO: PaletteFaderProcess::I_setPalToAllBlack
 	// 0x090
 	0, // void Intrinsic090(void)
 	0, // void Intrinsic091(void)
@@ -198,12 +198,12 @@ Intrinsic RemorseIntrinsics[] = {
 	0, // void Intrinsic097(void)
 	0, // void Intrinsic098(void)
 	Item::I_andStatus, // void Intrinsic099(6 bytes)
-	0, // void Intrinsic09A(void)
-	0, // void Intrinsic09B(2 bytes)
-	0, // void Intrinsic09C(4 bytes)
-	0, // void Intrinsic09D(2 bytes)
-	0, // void Intrinsic09E(4 bytes)
-	0, // void Intrinsic09F(10 bytes)
+	0, // TODO: PaletteFaderProcess::I_stopFadesAndResetToGamePal(void),
+	PaletteFaderProcess::I_fadeFromBlack, // fade to game pal with number of steps
+	0, // TODO: PaletteFaderProcess::I_fadeFromBlackWithParam
+	PaletteFaderProcess::I_fadeToBlack, // fade to black with number of steps
+	0, // TODO: PaletteFaderProcess::I_fadeToBlackWithParam
+	0, // TODO: PaletteFaderProcess::I_fadeToColor
 	// 0x0A0
 	Actor::I_setDead,
 	Item::I_getQLo, // based on same coff set as 02B
@@ -211,7 +211,7 @@ Intrinsic RemorseIntrinsics[] = {
 	Egg::I_setEggXRange, // void Intrinsic0A3(6 bytes)
 	Item::I_overlaps,
 	Item::I_isOn,
-	0, // ? TODO: I_getAnimationsDiabled -> default to 0 (it's fine..)
+	0, // TODO: I_getAnimationsDiabled -> default to 0 (fine for now..)
 	Egg::I_getEggXRange, // void Intrinsic0A7(4 bytes)
 	Actor::I_setDead,
 	0, // I_playFlic(char *) Intrinsic0A9(void)
@@ -356,7 +356,7 @@ Intrinsic RemorseIntrinsics[] = {
 	Item::I_isOn,
 	Item::I_getFootpadData, // void Intrinsic12D(16 bytes)
 	Actor::I_isDead, // int Intrinsic12E(4 bytes)
-	0, // void Intrinsic12F(6 bytes)
+	MonsterEgg::I_monsterEggHatch, // void Intrinsic12F(6 bytes) - FIXME: this is pretty different .. will it work?
 	// 0x130
 	Actor::I_clrImmortal, // void Intrinsic130(4 bytes)
 	0, // void Intrinsic131(6 bytes)
diff --git a/engines/ultima/ultima8/world/actors/actor.cpp b/engines/ultima/ultima8/world/actors/actor.cpp
index 65c1eaf84e..3937fa08eb 100644
--- a/engines/ultima/ultima8/world/actors/actor.cpp
+++ b/engines/ultima/ultima8/world/actors/actor.cpp
@@ -38,6 +38,7 @@
 #include "ultima/ultima8/world/actors/pathfinder.h"
 #include "ultima/ultima8/world/actors/animation.h"
 #include "ultima/ultima8/kernel/delay_process.h"
+#include "ultima/ultima8/kernel/core_app.h"
 #include "ultima/ultima8/world/actors/resurrection_process.h"
 #include "ultima/ultima8/world/destroy_item_process.h"
 #include "ultima/ultima8/world/actors/clear_feign_death_process.h"
@@ -698,7 +699,8 @@ void Actor::receiveHit(uint16 other, int dir, int damage, uint16 damage_type) {
 		}
 	}
 
-	if (damage && !fallingprocid) {
+	// FIXME: What are the equivalent Crusader animations here?
+	if (damage && !fallingprocid && GAME_IS_U8) {
 		ProcId anim1pid = doAnim(Animation::stumbleBackwards, dir);
 		ProcId anim2pid;
 		if (isInCombat())
diff --git a/engines/ultima/ultima8/world/actors/anim_action.h b/engines/ultima/ultima8/world/actors/anim_action.h
index f879f1d152..8acbd282c9 100644
--- a/engines/ultima/ultima8/world/actors/anim_action.h
+++ b/engines/ultima/ultima8/world/actors/anim_action.h
@@ -35,6 +35,8 @@ struct AnimFrame {
 	int _deltaZ;
 	int _deltaDir;
 	int _sfx;
+	uint16 _unk1;
+	uint16 _unk2;
 	uint32 _flags;
 
 	enum AnimFrameFlags {
diff --git a/engines/ultima/ultima8/world/item.cpp b/engines/ultima/ultima8/world/item.cpp
index a7b484f578..fadcdfac7e 100644
--- a/engines/ultima/ultima8/world/item.cpp
+++ b/engines/ultima/ultima8/world/item.cpp
@@ -33,6 +33,7 @@
 #include "ultima/ultima8/world/actors/actor.h"
 #include "ultima/ultima8/kernel/kernel.h"
 #include "ultima/ultima8/world/get_object.h"
+#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/shape.h"
@@ -569,6 +570,28 @@ bool Item::isCentreOn(const Item &item2) const {
 	return false;
 }
 
+bool Item::isOnScreen() const {
+	// TODO: would be cleaner to have this logic inside GameMapGump itself.
+	GameMapGump *game_map = Ultima8Engine::get_instance()->getGameMapGump();
+
+	if (!game_map)
+		return false;
+
+	Rect game_map_dims;
+	int32 screenx = -1;
+	int32 screeny = -1;
+	game_map->GetLocationOfItem(_objId, screenx, screeny);
+	game_map->GetDims(game_map_dims);
+	int32 xd, yd, zd;
+	getFootpadWorld(xd, yd, zd);
+
+	if (game_map_dims.InRect(screenx, screeny) &&
+		game_map_dims.InRect(screenx + xd, screeny + yd)) {
+		return true;
+	}
+
+	return false;
+}
 
 bool Item::canExistAt(int32 x_, int32 y_, int32 z_, bool needsupport) const {
 	CurrentMap *cm = World::get_instance()->getCurrentMap();
@@ -2635,7 +2658,7 @@ uint32 Item::I_popToCoords(const uint8 *args, unsigned int /*argsize*/) {
 
 	item->move(x, y, z);
 
-#if 0
+#if 1
 	perr << "Popping item into map: " << item->getShape() << "," << item->getFrame() << " at (" << x << "," << y << "," << z << ")" << Std::endl;
 #endif
 
@@ -2791,9 +2814,10 @@ uint32 Item::I_getDirToCoords(const uint8 *args, unsigned int /*argsize*/) {
 	int32 ix, iy, iz;
 	item->getLocationAbsolute(ix, iy, iz);
 
-	// FIXME: Crusader directions have double value
-	// - does that make any difference here?
-	return Get_WorldDirection(y - iy, x - ix);
+	uint32 retval = static_cast<uint32>(Get_WorldDirection(y - iy, x - ix));
+	if (GAME_IS_CRUSADER)
+		retval *= 2;
+	return retval;
 }
 
 uint32 Item::I_getDirFromCoords(const uint8 *args, unsigned int /*argsize*/) {
@@ -2805,7 +2829,10 @@ uint32 Item::I_getDirFromCoords(const uint8 *args, unsigned int /*argsize*/) {
 	int32 ix, iy, iz;
 	item->getLocationAbsolute(ix, iy, iz);
 
-	return Get_WorldDirection(iy - y, ix - x);
+	uint32 retval = static_cast<uint32>(Get_WorldDirection(iy - y, ix - x));
+	if (GAME_IS_CRUSADER)
+		retval *= 2;
+	return retval;
 }
 
 uint32 Item::I_getDirToItem(const uint8 *args, unsigned int /*argsize*/) {
@@ -2820,7 +2847,10 @@ uint32 Item::I_getDirToItem(const uint8 *args, unsigned int /*argsize*/) {
 	int32 i2x, i2y, i2z;
 	item2->getLocationAbsolute(i2x, i2y, i2z);
 
-	return Get_WorldDirection(i2y - iy, i2x - ix);
+	uint32 retval = static_cast<uint32>(Get_WorldDirection(i2y - iy, i2x - ix));
+	if (GAME_IS_CRUSADER)
+		retval *= 2;
+	return retval;
 }
 
 uint32 Item::I_getDirFromItem(const uint8 *args, unsigned int /*argsize*/) {
@@ -2835,7 +2865,70 @@ uint32 Item::I_getDirFromItem(const uint8 *args, unsigned int /*argsize*/) {
 	int32 i2x, i2y, i2z;
 	item2->getLocationAbsolute(i2x, i2y, i2z);
 
-	return (Get_WorldDirection(i2y - iy, i2x - ix) + 4) % 8;
+	uint32 retval = static_cast<uint32>((Get_WorldDirection(i2y - iy, i2x - ix) + 4) % 8);
+	if (GAME_IS_CRUSADER)
+		retval *= 2;
+	return retval;
+}
+
+uint32 Item::I_getDirFromTo16(const uint8 *args, unsigned int /*argsize*/) {
+	ARG_UINT16(x1);
+	ARG_UINT16(y1);
+	ARG_UINT16(x2);
+	ARG_UINT16(y2);
+
+	if (x1 == x2 && y1 == y2)
+		return 16;
+
+	// TODO: Implement proper 16 directions here.
+	uint32 retval = static_cast<uint32>(Get_WorldDirection(y2 - y1, x2 - x1));
+	return retval * 2;
+}
+
+uint32 Item::I_getClosestDirectionInRange(const uint8 *args, unsigned int /*argsize*/) {
+	ARG_UINT16(x1);
+	ARG_UINT16(y1);
+	ARG_UINT16(x2);
+	ARG_UINT16(y2);
+	ARG_UINT16(ndirs);
+	ARG_UINT16(mindir);
+	ARG_UINT16(maxdir);
+
+	// TODO: Implement proper 16 directions here.
+	uint32 dir = static_cast<uint32>(Get_WorldDirection(y2 - y1, x2 - x1));
+	if (ndirs == 16) {
+		dir *= 2;
+	}
+
+	if ((dir < mindir) || (dir > maxdir)) {
+		int32 dmin1 = dir - mindir;
+		int32 dmin2 = mindir - dir;
+		if (dmin1 < 0) {
+			dmin1 = dmin1 + ndirs;
+		}
+		if (dmin2 < 0) {
+			dmin2 = dmin2 + ndirs;
+		}
+		int32 dist_to_min = MIN(dmin1, dmin2);
+
+		int dmax1 = dir - maxdir;
+		int dmax2 = maxdir - dir;
+		if (dmax1 < 0) {
+			dmax1 = dmax1 + ndirs;
+		}
+		if (dmax2 < 0) {
+			dmax2 = dmax2 + ndirs;
+		}
+		int32 dist_to_max = MIN(dmax1, dmax2);
+
+		if (dist_to_min < dist_to_max) {
+			return mindir;
+		} else {
+			return maxdir;
+		}
+	}
+
+	return dir;
 }
 
 uint32 Item::I_hurl(const uint8 *args, unsigned int /*argsize*/) {
@@ -3051,5 +3144,13 @@ uint32 Item::I_inFastArea(const uint8 *args, unsigned int /*argsize*/) {
 	return item->hasFlags(FLG_FASTAREA);
 }
 
+uint32 Item::I_isOnScreen(const uint8 *args, unsigned int /*argsize*/) {
+	ARG_ITEM_FROM_PTR(item);
+	if (!item) return 0;
+
+	return item->isOnScreen();
+}
+
+
 } // End of namespace Ultima8
 } // End of namespace Ultima
diff --git a/engines/ultima/ultima8/world/item.h b/engines/ultima/ultima8/world/item.h
index 4c92ac7561..7ea968d89b 100644
--- a/engines/ultima/ultima8/world/item.h
+++ b/engines/ultima/ultima8/world/item.h
@@ -277,6 +277,9 @@ public:
 	//! Check if the centre of this item is on top of another item
 	bool isCentreOn(const Item &item2) const;
 
+	//! Check if the item is currently visible on screen
+	bool isOnScreen() const;
+
 	//! Check if this item can exist at the given coordinates
 	bool canExistAt(int32 x_, int32 y_, int32 z_, bool needsupport = false) const;
 
@@ -508,6 +511,8 @@ public:
 	INTRINSIC(I_getDirFromCoords);
 	INTRINSIC(I_getDirToItem);
 	INTRINSIC(I_getDirFromItem);
+	INTRINSIC(I_getDirFromTo16);
+	INTRINSIC(I_getClosestDirectionInRange);
 	INTRINSIC(I_look);
 	INTRINSIC(I_use);
 	INTRINSIC(I_gotHit);
@@ -550,6 +555,7 @@ public:
 	INTRINSIC(I_equip);
 	INTRINSIC(I_unequip);
 	INTRINSIC(I_avatarStoleSomething);
+	INTRINSIC(I_isOnScreen);
 
 private:
 	uint32 _shape;   // DO NOT modify this directly! Always use setShape()!


Commit: a4d204f8e09281a6cb635b1517a614dca63dc843
    https://github.com/scummvm/scummvm/commit/a4d204f8e09281a6cb635b1517a614dca63dc843
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2020-06-17T18:02:41+09:00

Commit Message:
ULTIMA8: Print offsets in shape viewer

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


diff --git a/engines/ultima/ultima8/gumps/shape_viewer_gump.cpp b/engines/ultima/ultima8/gumps/shape_viewer_gump.cpp
index 837271336f..96e6eb2d49 100644
--- a/engines/ultima/ultima8/gumps/shape_viewer_gump.cpp
+++ b/engines/ultima/ultima8/gumps/shape_viewer_gump.cpp
@@ -144,7 +144,7 @@ void ShapeViewerGump::PaintThis(RenderSurface *surf, int32 lerp_factor, bool /*s
 				uint8 px_g = shape_->getPalette()->_palette[rawpx * 3 + 1];
 				uint8 px_b = shape_->getPalette()->_palette[rawpx * 3 + 2];
 				
-				sprintf(buf2, "px: (%d, %d): %d (%d, %d, %d)", relx, rely, rawpx, px_r, px_g, px_b);
+				sprintf(buf2, "px: (%d/%d, %d/%d): %d (%d, %d, %d)", relx, frame->_xoff, rely, frame->_yoff, rawpx, px_r, px_g, px_b);
 				rendtext = font->renderText(buf2, remaining);
 				rendtext->draw(surf, 20, 25);
 				delete rendtext;


Commit: 580efb4a4920f5d09049b28376477c11c849c850
    https://github.com/scummvm/scummvm/commit/580efb4a4920f5d09049b28376477c11c849c850
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2020-06-17T18:02:41+09:00

Commit Message:
ULTIMA8: Fix free order of music stream

Changed paths:
    engines/ultima/ultima8/audio/remorse_music_process.cpp


diff --git a/engines/ultima/ultima8/audio/remorse_music_process.cpp b/engines/ultima/ultima8/audio/remorse_music_process.cpp
index a02d5826f0..9383a35729 100644
--- a/engines/ultima/ultima8/audio/remorse_music_process.cpp
+++ b/engines/ultima/ultima8/audio/remorse_music_process.cpp
@@ -121,8 +121,7 @@ void RemorseMusicProcess::playMusic_internal(int track) {
 	mixer->stopHandle(_soundHandle);
 	_soundHandle = Audio::SoundHandle();
 	if (_playingStream) {
-		// FIXME: This gets use-after-free.. is it deleted already?
-		//delete _playingStream;
+		delete _playingStream;
 		_playingStream = nullptr;
 	}
 
@@ -137,18 +136,22 @@ void RemorseMusicProcess::playMusic_internal(int track) {
 			return;
 		}
 
-		_playingStream = Audio::makeModXmS3mStream(rs, DisposeAfterUse::YES);
+		_playingStream = Audio::makeModXmS3mStream(rs, DisposeAfterUse::NO);
 		if (!_playingStream) {
 			error("Couldn't create stream from AMF file: %s", fname.c_str());
 			return;
 		}
-		mixer->playStream(Audio::Mixer::kMusicSoundType, &_soundHandle, _playingStream);
+		mixer->playStream(Audio::Mixer::kMusicSoundType, &_soundHandle, _playingStream, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO);
 	}
 }
 
 void RemorseMusicProcess::run() {
-	if (!_playingStream || !_playingStream->endOfStream()) {
-		// nothing to do
+	if (!_playingStream) {
+		return;
+	}
+	if (_playingStream->endOfStream()) {
+		delete _playingStream;
+		_playingStream = nullptr;
 		return;
 	}
 	// hit end of stream, play it again.


Commit: 1fb7bf0940ff3bb367ea5932a086083a4b4117c2
    https://github.com/scummvm/scummvm/commit/1fb7bf0940ff3bb367ea5932a086083a4b4117c2
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2020-06-17T18:02:41+09:00

Commit Message:
ULTIMA8: Halve Crusader coords given to usecode

Changed paths:
    engines/ultima/ultima8/convert/crusader/convert_usecode_crusader.h
    engines/ultima/ultima8/graphics/anim_dat.cpp
    engines/ultima/ultima8/usecode/remorse_intrinsics.h
    engines/ultima/ultima8/world/item.cpp


diff --git a/engines/ultima/ultima8/convert/crusader/convert_usecode_crusader.h b/engines/ultima/ultima8/convert/crusader/convert_usecode_crusader.h
index 8613acdaa4..427a50d156 100644
--- a/engines/ultima/ultima8/convert/crusader/convert_usecode_crusader.h
+++ b/engines/ultima/ultima8/convert/crusader/convert_usecode_crusader.h
@@ -104,7 +104,7 @@ const char* const ConvertUsecodeCrusader::_intrinsics[] = {
 	"void Item::I_popToCoords(Item *, uint16 x, uint16 y, uint16 z)", // set coords, used after creating blood spills in NPCDEATH
 	"void Actor::I_setDead(4 bytes)", // part of same coff set 021, 060, 073, 0A0, 0A8, 0D8, 0E7, 135
 	"void I_push(Item *)", // same code as U8
-	"int16 Intrinsic023(void)", // returns some ItemCache global in disassembly
+	"int16 Item::I_getEtherealTop(void)", // from disasmww
 	"void Item::I_setShape(Item *, int16 shapeno)", // probably. See PEPSIEW::gotHit.
 	"void Item::I_touch(Item *)", // same code as U8
 	"int16 Item::I_getQHi(Item *)", // guess, based on variable name in BOUNCBOX::gotHit
@@ -336,7 +336,7 @@ const char* const ConvertUsecodeCrusader::_intrinsics[] = {
 	"int16 Item::I_getQHi(Item *)", // same as 026 based on same coff set 026, 045, 047, 049, 04B, 04D, 04F, 0AF, 0BE, 0C9, 0F0, 0F3, 0FB, 133
 	"void Item::I_andStatus(Item *, uint16 status)", // part of same coff set 01A, 031, 069, 06E, 099, 0B2, 0BF, 0C1, 0C3, 0E9, 0FC, 101, 104, 106, 108, 10A, 10C, 10E, 110, 114, 117, 11A, 128, 132
 	"byte I_SomethingAboutGlobal7e2f_0FD(int)",
-	"void Intrinsic0FE(4 bytes)",
+	"void I_displayText_0FE(char *)",
 	"int16 UCMachine::I_numToStr(int16 num)", // same as 113 based on same coff set 0FF, 113, 126
 	// 0100
 	"int16 Item::I_getNPCNum(Item *)", // part of same coff set 067, 06D, 089, 08E, 0AD, 0F8, 100, 102, 105, 107, 109, 10B, 10D, 10F, 111, 115, 11C, 123, 129
diff --git a/engines/ultima/ultima8/graphics/anim_dat.cpp b/engines/ultima/ultima8/graphics/anim_dat.cpp
index bb85dbd7e5..2c97bda6df 100644
--- a/engines/ultima/ultima8/graphics/anim_dat.cpp
+++ b/engines/ultima/ultima8/graphics/anim_dat.cpp
@@ -185,8 +185,8 @@ void AnimDat::load(Common::SeekableReadStream *rs) {
 						f._frame += (x & 0xF) << 8;
 						// byte 2, 3: unknown; byte 3 might contain flags
 						f._unk1 = rs->readSint16LE();
-						// byte 4: deltadir (signed)
-						f._deltaDir = rs->readSByte();
+						// byte 4: deltadir (signed) - convert to pixels
+						f._deltaDir = rs->readSByte() * 2;
 						// byte 5: flags?
 						f._flags = rs->readByte();
 						// byte 6, 7: unknown
diff --git a/engines/ultima/ultima8/usecode/remorse_intrinsics.h b/engines/ultima/ultima8/usecode/remorse_intrinsics.h
index 69b409aabd..0e17108225 100644
--- a/engines/ultima/ultima8/usecode/remorse_intrinsics.h
+++ b/engines/ultima/ultima8/usecode/remorse_intrinsics.h
@@ -72,7 +72,7 @@ Intrinsic RemorseIntrinsics[] = {
 	Item::I_popToCoords, // void Intrinsic020(10 bytes)
 	Actor::I_setDead, // void Intrinsic021(4 bytes)
 	Item::I_push,
-	0, // int Intrinsic023(void)
+	Item::I_getEtherealTop, // int Intrinsic023(void)
 	Item::I_setShape, // Probably, see PEPSIEW::gotHit
 	Item::I_touch,
 	Item::I_getQHi, // int16 Intrinsic026(Item *), // guess, based on variable name in BOUNCBOX::gotHit
diff --git a/engines/ultima/ultima8/world/item.cpp b/engines/ultima/ultima8/world/item.cpp
index fadcdfac7e..0e03456b73 100644
--- a/engines/ultima/ultima8/world/item.cpp
+++ b/engines/ultima/ultima8/world/item.cpp
@@ -103,7 +103,7 @@ void Item::dumpInfo() const {
 
 	pout << ") q:" << getQuality()
 	     << ", m:" << getMapNum() << ", n:" << getNpcNum()
-	     << ", f: 0x" << Std::hex << getFlags() << ", ef:0x"
+	     << ", f:0x" << Std::hex << getFlags() << ", ef:0x"
 		 << getExtFlags();
 
 	ShapeInfo *info = getShapeInfo();
@@ -1955,7 +1955,10 @@ uint32 Item::I_getX(const uint8 *args, unsigned int /*argsize*/) {
 
 	int32 x, y, z;
 	item->getLocationAbsolute(x, y, z);
-	return x;
+	if (GAME_IS_CRUSADER)
+		return x / 2;
+	else
+		return x;
 }
 
 uint32 Item::I_getY(const uint8 *args, unsigned int /*argsize*/) {
@@ -1964,7 +1967,10 @@ uint32 Item::I_getY(const uint8 *args, unsigned int /*argsize*/) {
 
 	int32 x, y, z;
 	item->getLocationAbsolute(x, y, z);
-	return y;
+	if (GAME_IS_CRUSADER)
+		return y / 2;
+	else
+		return y;
 }
 
 uint32 Item::I_getZ(const uint8 *args, unsigned int /*argsize*/) {
@@ -2020,6 +2026,11 @@ uint32 Item::I_getPoint(const uint8 *args, unsigned int /*argsize*/) {
 	int32 x, y, z;
 	item->getLocationAbsolute(x, y, z);
 
+	if (GAME_IS_CRUSADER) {
+		x /= 2;
+		y /= 2;
+	}
+
 	WorldPoint point;
 	point.setX(x);
 	point.setY(y);
@@ -2579,7 +2590,13 @@ uint32 Item::I_getFamilyOfType(const uint8 *args, unsigned int /*argsize*/) {
 
 uint32 Item::I_push(const uint8 *args, unsigned int /*argsize*/) {
 	ARG_ITEM_FROM_PTR(item);
-	if (!item) return 0;
+	if (!item)
+		return 0;
+
+	#if 1
+		perr << "Pushing item to ethereal void: " << item->getShape() << "," << item->getFrame() << Std::endl;
+	#endif
+
 
 	item->moveToEtherealVoid();
 
@@ -2600,8 +2617,8 @@ uint32 Item::I_create(const uint8 *args, unsigned int /*argsize*/) {
 	uint16 objID = newitem->getObjId();
 
 #if 0
-	pout << "Item::create: created item " << objID << " (" << _shape
-	     << "," << _frame << ")" << Std::endl;
+	pout << "Item::create: created item " << objID << " (" << shape
+	     << "," << frame << ")" << Std::endl;
 #endif
 
 	newitem->moveToEtherealVoid();
@@ -2619,7 +2636,8 @@ uint32 Item::I_pop(const uint8 *args, unsigned int /*argsize*/) {
 
 	World *w = World::get_instance();
 
-	if (w->etherealEmpty()) return 0; // no items left on stack
+	if (w->etherealEmpty())
+		return 0; // no items left on stack
 
 	uint16 _objId = w->etherealPeek();
 	Item *item = getItem(_objId);
@@ -2647,7 +2665,8 @@ uint32 Item::I_popToCoords(const uint8 *args, unsigned int /*argsize*/) {
 
 	World *w = World::get_instance();
 
-	if (w->etherealEmpty()) return 0; // no items left on stack
+	if (w->etherealEmpty())
+		return 0; // no items left on stack
 
 	uint16 objId = w->etherealPeek();
 	Item *item = getItem(objId);
@@ -2656,9 +2675,14 @@ uint32 Item::I_popToCoords(const uint8 *args, unsigned int /*argsize*/) {
 		return 0; // top item was invalid
 	}
 
+	if (GAME_IS_CRUSADER) {
+		x *= 2;
+		y *= 2;
+	}
+
 	item->move(x, y, z);
 
-#if 1
+#if 0
 	perr << "Popping item into map: " << item->getShape() << "," << item->getFrame() << " at (" << x << "," << y << "," << z << ")" << Std::endl;
 #endif
 
@@ -2678,7 +2702,8 @@ uint32 Item::I_popToContainer(const uint8 *args, unsigned int /*argsize*/) {
 
 	World *w = World::get_instance();
 
-	if (w->etherealEmpty()) return 0; // no items left on stack
+	if (w->etherealEmpty())
+		return 0; // no items left on stack
 
 	uint16 _objId = w->etherealPeek();
 	Item *item = getItem(_objId);
@@ -2705,7 +2730,8 @@ uint32 Item::I_popToEnd(const uint8 *args, unsigned int /*argsize*/) {
 
 	World *w = World::get_instance();
 
-	if (w->etherealEmpty()) return 0; // no items left on stack
+	if (w->etherealEmpty())
+		return 0; // no items left on stack
 
 	uint16 _objId = w->etherealPeek();
 	Item *item = getItem(_objId);
@@ -2729,9 +2755,18 @@ uint32 Item::I_move(const uint8 *args, unsigned int /*argsize*/) {
 	ARG_UINT16(x);
 	ARG_UINT16(y);
 	ARG_UINT16(z);
-	if (!item) return 0;
+	if (!item)
+		return 0;
 
 	//! What should this do to ethereal items?
+	if (GAME_IS_CRUSADER) {
+		x *= 2;
+		y *= 2;
+	}
+
+	#if 0
+		perr << "Moving item: " << item->getShape() << "," << item->getFrame() << " to (" << x << "," << y << "," << z << ")" << Std::endl;
+	#endif
 
 	item->move(x, y, z);
 	//item->collideMove(x, y, z, true, true);
@@ -2744,6 +2779,11 @@ uint32 Item::I_legalMoveToPoint(const uint8 *args, unsigned int argsize) {
 	ARG_UINT16(force); // 0/1
 	ARG_UINT16(unknown2); // always 0
 
+	if (GAME_IS_CRUSADER) {
+		point.setX(point.getX() * 2);
+		point.setY(point.getY() * 2);
+	}
+
 	//! What should this do to ethereal items?
 
 //	if (item->canExistAt(point.getX(), point.getY(), point.getZ())) {
@@ -2811,6 +2851,11 @@ uint32 Item::I_getDirToCoords(const uint8 *args, unsigned int /*argsize*/) {
 	ARG_UINT16(y);
 	if (!item) return 0;
 
+	if (GAME_IS_CRUSADER) {
+		x *= 2;
+		y *= 2;
+	}
+
 	int32 ix, iy, iz;
 	item->getLocationAbsolute(ix, iy, iz);
 
@@ -2826,6 +2871,11 @@ uint32 Item::I_getDirFromCoords(const uint8 *args, unsigned int /*argsize*/) {
 	ARG_UINT16(y);
 	if (!item) return 0;
 
+	if (GAME_IS_CRUSADER) {
+		x *= 2;
+		y *= 2;
+	}
+
 	int32 ix, iy, iz;
 	item->getLocationAbsolute(ix, iy, iz);
 




More information about the Scummvm-git-logs mailing list