[Scummvm-git-logs] scummvm master -> a9ae8be3c97f881c08bcc40f7d3fcf0e465be676

mduggan mgithub at guarana.org
Wed Jul 7 10:34:22 UTC 2021


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

Summary:
0b4878c00e ULTIMA8: Add engine option for high res mode
627932044b ULTIMA8: Fix Crusader paint order corner case
191e7c8180 ULTIMA8: Add simple unit tests for item sorting
a1d7389f10 ULTIMA8: Remove outdated TODO comment
5e97b554d1 ULTIMA8: Fix Crusader firedistance for non-avatar NPCs
e523874936 ULTIMA8: Allow running in No Remorse rebel base.
a9ae8be3c9 ULTIMA: Regenerate ultima.dat and bump U8 data version


Commit: 0b4878c00e934210a578fbf8d673ba757ff4a691
    https://github.com/scummvm/scummvm/commit/0b4878c00e934210a578fbf8d673ba757ff4a691
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2021-07-07T19:34:05+09:00

Commit Message:
ULTIMA8: Add engine option for high res mode

Changed paths:
    engines/ultima/metaengine.cpp
    engines/ultima/metaengine.h
    engines/ultima/shared/engine/ultima.cpp
    engines/ultima/ultima8/meta_engine.cpp
    engines/ultima/ultima8/ultima8.cpp
    engines/ultima/ultima8/ultima8.h


diff --git a/engines/ultima/metaengine.cpp b/engines/ultima/metaengine.cpp
index 93397d4ab9..0f59b22e13 100644
--- a/engines/ultima/metaengine.cpp
+++ b/engines/ultima/metaengine.cpp
@@ -27,6 +27,7 @@
 #include "common/savefile.h"
 #include "common/str-array.h"
 #include "common/memstream.h"
+#include "common/translation.h"
 #include "ultima/shared/early/ultima_early.h"
 #include "ultima/ultima4/ultima4.h"
 #include "ultima/ultima4/meta_engine.h"
diff --git a/engines/ultima/metaengine.h b/engines/ultima/metaengine.h
index ba14111f99..6d7c407dec 100644
--- a/engines/ultima/metaengine.h
+++ b/engines/ultima/metaengine.h
@@ -54,7 +54,6 @@ public:
 	* Return the extra GUI options used by the target.
 	*/
 	const ExtraGuiOptions getExtraGuiOptions(const Common::String &target) const override;
-
 };
 
 #endif
diff --git a/engines/ultima/shared/engine/ultima.cpp b/engines/ultima/shared/engine/ultima.cpp
index 60fdb729b2..82727d3d4e 100644
--- a/engines/ultima/shared/engine/ultima.cpp
+++ b/engines/ultima/shared/engine/ultima.cpp
@@ -76,6 +76,7 @@ bool UltimaEngine::hasFeature(EngineFeature f) const {
 	return
 		(f == kSupportsReturnToLauncher) ||
 		(f == kSupportsLoadingDuringRuntime) ||
+		(f == kSupportsChangingOptionsDuringRuntime) ||
 		(f == kSupportsSavingDuringRuntime);
 }
 
diff --git a/engines/ultima/ultima8/meta_engine.cpp b/engines/ultima/ultima8/meta_engine.cpp
index d375b41013..70bc136660 100644
--- a/engines/ultima/ultima8/meta_engine.cpp
+++ b/engines/ultima/ultima8/meta_engine.cpp
@@ -143,6 +143,12 @@ static const ExtraGuiOption COMMON_OPTIONS[] = {
 		"cheat",
 		false
 	},
+	{
+		_s("Enable high resolution"),
+		_s("Enable a higher resolution for the game"),
+		"usehighres",
+		false
+	},
 	{ nullptr, nullptr, nullptr, false }
 };
 
diff --git a/engines/ultima/ultima8/ultima8.cpp b/engines/ultima/ultima8/ultima8.cpp
index 3e0c1d23de..dc084e7215 100644
--- a/engines/ultima/ultima8/ultima8.cpp
+++ b/engines/ultima/ultima8/ultima8.cpp
@@ -130,7 +130,8 @@ Ultima8Engine::Ultima8Engine(OSystem *syst, const Ultima::UltimaGameDescription
 		_avatarInStasis(false), _cruStasis(false), _paintEditorItems(false), _inversion(0),
 		_showTouching(false), _timeOffset(0), _hasCheated(false), _cheatsEnabled(false),
 		_fontOverride(false), _fontAntialiasing(false), _audioMixer(0), _inverterGump(nullptr),
-	    _lerpFactor(256), _inBetweenFrame(false), _unkCrusaderFlag(false), _moveKeyFrame(0) {
+	    _lerpFactor(256), _inBetweenFrame(false), _unkCrusaderFlag(false), _moveKeyFrame(0),
+		_highRes(false) {
 	_instance = this;
 }
 
@@ -584,11 +585,14 @@ void Ultima8Engine::paint() {
 
 	tpaint -= g_system->getMillis();
 
-#ifdef DEBUG
-	// Fill the screen with an annoying color so we can see fast area bugs
 	Rect r;
 	_screen->GetSurfaceDims(r);
-	_screen->Fill32(0xFF1010FF, 0, 0, r.width(), r.height());
+	if (_highRes)
+		_screen->Fill32(0, 0, 0, r.width(), r.height());
+
+#ifdef DEBUG
+	// Fill the screen with an annoying color so we can see fast area bugs
+	_screen->Fill32(0xFF10FF10, 0, 0, r.width(), r.height());
 #endif
 
 	_desktopGump->Paint(_screen, _lerpFactor, false);
@@ -602,12 +606,16 @@ void Ultima8Engine::paint() {
 }
 
 void Ultima8Engine::GraphicSysInit() {
+	if (ConfMan.hasKey("usehighres")) {
+		_highRes = ConfMan.getBool("usehighres");
+	}
+
 	if (GAME_IS_U8) {
-		ConfMan.registerDefault("width", U8_DEFAULT_SCREEN_WIDTH);
-		ConfMan.registerDefault("height", U8_DEFAULT_SCREEN_HEIGHT);
+		ConfMan.registerDefault("width", _highRes ? U8_HIRES_SCREEN_WIDTH : U8_DEFAULT_SCREEN_WIDTH);
+		ConfMan.registerDefault("height", _highRes ? U8_HIRES_SCREEN_HEIGHT : U8_DEFAULT_SCREEN_HEIGHT);
 	} else {
-		ConfMan.registerDefault("width", CRUSADER_DEFAULT_SCREEN_WIDTH);
-		ConfMan.registerDefault("height", CRUSADER_DEFAULT_SCREEN_HEIGHT);
+		ConfMan.registerDefault("width", _highRes ? CRUSADER_HIRES_SCREEN_WIDTH : CRUSADER_DEFAULT_SCREEN_WIDTH);
+		ConfMan.registerDefault("height", _highRes ? CRUSADER_HIRES_SCREEN_HEIGHT : CRUSADER_DEFAULT_SCREEN_HEIGHT);
 	}
 	ConfMan.registerDefault("bpp", 16);
 
@@ -1204,7 +1212,6 @@ void Ultima8Engine::applyGameSettings() {
 	_frameLimit = ConfMan.getBool("frameLimit");
 	_interpolate = ConfMan.getBool("interpolate");
 	_cheatsEnabled = ConfMan.getBool("cheat");
-
 }
 
 void Ultima8Engine::openConfigDialog() {
diff --git a/engines/ultima/ultima8/ultima8.h b/engines/ultima/ultima8/ultima8.h
index a35011ec68..b74a526dc1 100644
--- a/engines/ultima/ultima8/ultima8.h
+++ b/engines/ultima/ultima8/ultima8.h
@@ -106,6 +106,7 @@ private:
 	int32 _lerpFactor;       //!< Interpolation factor for this frame (0-256)
 	bool _inBetweenFrame;    //!< Set true if we are doing an inbetween frame
 
+	bool _highRes;			 //!< Set to true to enable larger screen size
 	bool _frameSkip;         //!< Set to true to enable frame skipping (default false)
 	bool _frameLimit;        //!< Set to true to enable frame limiting (default true)
 	bool _interpolate;       //!< Set to true to enable interpolation (default true)
@@ -216,6 +217,11 @@ public:
 	static const int CRUSADER_DEFAULT_SCREEN_WIDTH = 640;
 	static const int CRUSADER_DEFAULT_SCREEN_HEIGHT = 480;
 
+	static const int U8_HIRES_SCREEN_WIDTH = 640;
+	static const int U8_HIRES_SCREEN_HEIGHT = 400;
+	static const int CRUSADER_HIRES_SCREEN_WIDTH = 1024;
+	static const int CRUSADER_HIRES_SCREEN_HEIGHT = 768;
+
 	INTRINSIC(I_getCurrentTimerTick);
 	INTRINSIC(I_setAvatarInStasis);
 	INTRINSIC(I_getAvatarInStasis);


Commit: 627932044bac482107ecfa2dca77b8269e114d7d
    https://github.com/scummvm/scummvm/commit/627932044bac482107ecfa2dca77b8269e114d7d
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2021-07-07T19:34:05+09:00

Commit Message:
ULTIMA8: Fix Crusader paint order corner case

Changed paths:
    engines/ultima/ultima8/graphics/shape_info.h
    engines/ultima/ultima8/world/item_sorter.cpp


diff --git a/engines/ultima/ultima8/graphics/shape_info.h b/engines/ultima/ultima8/graphics/shape_info.h
index 4156a55e18..44bf4261ac 100644
--- a/engines/ultima/ultima8/graphics/shape_info.h
+++ b/engines/ultima/ultima8/graphics/shape_info.h
@@ -147,6 +147,9 @@ public:
 	inline bool is_targetable() const {
 		return (_flags & (SI_OCCL | SI_CRU_TARGETABLE));
 	}
+	inline bool is_invitem() const {
+		return (_family == SF_CRUINVITEM);
+	}
 
 	bool hasQuantity() const {
 		return (_family == SF_QUANTITY || _family == SF_REAGENT);
diff --git a/engines/ultima/ultima8/world/item_sorter.cpp b/engines/ultima/ultima8/world/item_sorter.cpp
index 3554a7ec7c..ea1d43a067 100644
--- a/engines/ultima/ultima8/world/item_sorter.cpp
+++ b/engines/ultima/ultima8/world/item_sorter.cpp
@@ -109,6 +109,7 @@ struct SortItem {
 	bool    _fixed : 1;
 	bool    _land : 1;
 	bool 	_sprite : 1;         // Always-on-top sprite, for Crusader (U8 sprites appear in z order)
+	bool 	_invitem : 1;        // Crusader inventory item, should appear above other things
 
 	bool    _occluded : 1;       // Set true if occluded
 	bool  	_clipped : 1;        // Clipped to RenderSurface
@@ -309,6 +310,18 @@ inline bool SortItem::below(const SortItem &si2) const {
 	if (si1._sprite != si2._sprite)
 		return si1._sprite < si2._sprite;
 
+	// Clearly in y?
+	if (si1._y <= si2._yFar)
+		return true;
+	if (si1._yFar >= si2._y)
+		return false;
+
+	// Clearly in x?
+	if (si1._x <= si2._xLeft)
+		return true;
+	if (si1._xLeft >= si2._x)
+		return false;
+
 	// Specialist z flat handling
 	if (si1._flat && si2._flat) {
 		// Differing z is easy for flats
@@ -343,25 +356,22 @@ inline bool SortItem::below(const SortItem &si2) const {
 	}
 	// Mixed, or non flat
 	else {
+		// Inv items always drawn first if their z-bottom is equal or higher. 
+		// This is a bit of a hack as 2 places in Crusader there are keycards
+		// on tables but their z position is the bottom z of the table.
+		if (si1._invitem) {
+			if (si1._z >= si2._z)
+				return false;
+		}
+
 		// Clearly in z
 		if (si1._zTop <= si2._z)
 			return true;
+
 		if (si1._z >= si2._zTop)
 			return false;
 	}
 
-	// Clearly in y?
-	if (si1._y <= si2._yFar)
-		return true;
-	if (si1._yFar >= si2._y)
-		return false;
-
-	// Clearly in x?
-	if (si1._x <= si2._xLeft)
-		return true;
-	if (si1._xLeft >= si2._x)
-		return false;
-
 	// Are overlapping in all 3 dimentions if we come here
 
 	// Overlapping z-bottom check
@@ -553,7 +563,10 @@ void ItemSorter::AddItem(int32 x, int32 y, int32 z, uint32 shapeNum, uint32 fram
 	si->_trans = info->is_translucent();
 	si->_fixed = info->is_fixed();
 	si->_land = info->is_land();
-	si->_sprite = GAME_IS_CRUSADER && (si->_extFlags & Item::EXT_SPRITE);
+	if (GAME_IS_CRUSADER) {
+		si->_sprite = si->_extFlags & Item::EXT_SPRITE;
+		si->_invitem = info->is_invitem();
+	}
 
 	si->_occluded = false;
 	si->_order = -1;


Commit: 191e7c818059bc95f9fca7f123d122b8f9bb3c1a
    https://github.com/scummvm/scummvm/commit/191e7c818059bc95f9fca7f123d122b8f9bb3c1a
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2021-07-07T19:34:05+09:00

Commit Message:
ULTIMA8: Add simple unit tests for item sorting

Changed paths:
  A engines/ultima/ultima8/world/sort_item.h
  A test/engines/ultima/ultima8/world/sort_item.h
    engines/ultima/ultima8/world/item_sorter.cpp


diff --git a/engines/ultima/ultima8/world/item_sorter.cpp b/engines/ultima/ultima8/world/item_sorter.cpp
index ea1d43a067..1e475a40b8 100644
--- a/engines/ultima/ultima8/world/item_sorter.cpp
+++ b/engines/ultima/ultima8/world/item_sorter.cpp
@@ -37,408 +37,11 @@
 #include "ultima/ultima8/world/get_object.h"
 // --
 
+#include "ultima/ultima8/world/sort_item.h"
+
 namespace Ultima {
 namespace Ultima8 {
 
-// This does NOT need to be in the header
-struct SortItem {
-	SortItem(SortItem *n) : _next(n), _prev(nullptr), _itemNum(0),
-			_shape(nullptr), _order(-1), _depends(), _shapeNum(0),
-			_frame(0), _flags(0), _extFlags(0), _sx(0), _sy(0),
-			_sx2(0), _sy2(0), _x(0), _y(0), _z(0), _xLeft(0),
-			_yFar(0), _zTop(0), _sxLeft(0), _sxRight(0), _sxTop(0),
-			_syTop(0), _sxBot(0), _syBot(0),_f32x32(false), _flat(false),
-			_occl(false), _solid(false), _draw(false), _roof(false),
-			_noisy(false), _anim(false), _trans(false), _fixed(false),
-			_land(false), _occluded(false), _clipped(false), _sprite(false) { }
-
-	SortItem                *_next;
-	SortItem                *_prev;
-
-	uint16                  _itemNum;   // Owner item number
-
-	const Shape             *_shape;
-	uint32                  _shapeNum;
-	uint32                  _frame;
-	uint32                  _flags;     // Item flags
-	uint32                  _extFlags;  // Item extended flags
-
-	int                     _sx, _sx2;  // Screenspace X coords
-	int                     _sy, _sy2;  // Screenspace Y coords
-
-	/*
-	            Bounding Box layout
-
-	       1
-	     /   \
-	   /       \     1 = Left  Far  Top LFT --+
-	 2           3   2 = Left  Near Top LNT -++
-	 | \       / |   3 = Right Far  Top RFT +-+
-	 |   \   /   |   4 = Right Near Top RNT +++
-	 |     4     |   5 = Left  Near Bot LNB -+-
-	 |     |     |   6 = Right Far  Bot RFB +--
-	 5     |     6   7 = Right Near Bot RNB ++-
-	   \   |   /     8 = Left  Far  Bot LFB --- (not shown)
-	     \ | /
-	       7
-
-	*/
-
-	int32   _x, _xLeft;   // Worldspace bounding box x (xright = x)
-	int32   _y, _yFar;    // Worldspace bounding box y (ynear = y)
-	int32   _z, _zTop;    // Worldspace bounding box z (_zTop = z)
-
-	int32   _sxLeft;     // Screenspace bounding box left extent    (LNT x coord)
-	int32   _sxRight;    // Screenspace bounding box right extent   (RFT x coord)
-
-	int32   _sxTop;      // Screenspace bounding box top x coord    (LFT x coord)
-	int32   _syTop;      // Screenspace bounding box top extent     (LFT y coord)
-
-	int32   _sxBot;      // Screenspace bounding box bottom x coord (RNB x coord) ss origin
-	int32   _syBot;      // Screenspace bounding box bottom extent  (RNB y coord) ss origin
-
-	bool    _f32x32 : 1;         // Needs 1 bit  0
-	bool    _flat : 1;           // Needs 1 bit  1
-	bool    _occl : 1;           // Needs 1 bit  2
-	bool    _solid : 1;          // Needs 1 bit  3
-	bool    _draw : 1;           // Needs 1 bit  4
-	bool    _roof : 1;           // Needs 1 bit  5
-	bool    _noisy : 1;          // Needs 1 bit  6
-	bool    _anim : 1;           // Needs 1 bit  7
-	bool    _trans : 1;          // Needs 1 bit  8
-	bool    _fixed : 1;
-	bool    _land : 1;
-	bool 	_sprite : 1;         // Always-on-top sprite, for Crusader (U8 sprites appear in z order)
-	bool 	_invitem : 1;        // Crusader inventory item, should appear above other things
-
-	bool    _occluded : 1;       // Set true if occluded
-	bool  	_clipped : 1;        // Clipped to RenderSurface
-
-	int32   _order;      // Rendering _order. -1 is not yet drawn
-
-	// Note that Std::priority_queue could be used here, BUT there is no guarentee that it's implementation
-	// will be friendly to insertions
-	// Alternatively i could use Std::list, BUT there is no guarentee that it will keep wont delete
-	// the unused nodes after doing a clear
-	// So the only reasonable solution is to write my own list
-	struct DependsList {
-		struct Node {
-			Node        *_next;
-			Node        *_prev;
-			SortItem    *val;
-			Node() : _next(nullptr), _prev(nullptr), val(nullptr) { }
-		};
-
-		Node *list;
-		Node *tail;
-		Node *unused;
-
-		struct iterator {
-			Node *n;
-			SortItem *&operator *() {
-				return n->val;
-			}
-			iterator(Node *node) : n(node) { }
-			iterator &operator++() {
-				n = n->_next;
-				return *this;
-			}
-			bool operator != (const iterator &o) const {
-				return n != o.n;
-			}
-		};
-
-		iterator begin() const {
-			return iterator(list);
-		}
-		iterator end() const {
-			return iterator(nullptr);
-		}
-
-		void clear() {
-			if (tail) {
-				tail->_next = unused;
-				unused = list;
-				tail = nullptr;
-				list = nullptr;
-			}
-		}
-
-		void push_back(SortItem *other) {
-			if (!unused) unused = new Node();
-			Node *nn = unused;
-			unused = unused->_next;
-			nn->val = other;
-
-			// Put it at the end
-			if (tail) tail->_next = nn;
-			if (!list) list = nn;
-			nn->_next = nullptr;
-			nn->_prev = tail;
-			tail = nn;
-		}
-
-		void insert_sorted(SortItem *other) {
-			if (!unused) unused = new Node();
-			Node *nn = unused;
-			unused = unused->_next;
-			nn->val = other;
-
-			for (Node *n = list; n != nullptr; n = n->_next) {
-				// Get the insert point... which is before the first item that has higher z than us
-				if (other->ListLessThan(n->val)) {
-					nn->_next = n;
-					nn->_prev = n->_prev;
-					n->_prev = nn;
-					if (nn->_prev) nn->_prev->_next = nn;
-					else list = nn;
-					return;
-				}
-			}
-
-			// No suitable, so put at end
-			if (tail) tail->_next = nn;
-			if (!list) list = nn;
-			nn->_next = nullptr;
-			nn->_prev = tail;
-			tail = nn;
-		}
-
-		DependsList() : list(nullptr), tail(nullptr), unused(nullptr) { }
-
-		~DependsList() {
-			clear();
-			while (unused)  {
-				Node *n = unused->_next;
-				delete unused;
-				unused = n;
-			}
-		}
-	};
-
-	//Std::vector<SortItem *>   _depends;    // All this Items dependencies (i.e. all objects behind)
-	//Std::list<SortItem *> _depends;    // All this Items dependencies (i.e. all objects behind)
-	DependsList _depends;
-
-	// Functions
-
-	// Screenspace check to see if this overlaps si2
-	inline bool overlap(const SortItem &si2) const;
-
-	// Screenspace check to see if this occludes si2. Assumes this is above of si2
-	inline bool occludes(const SortItem &si2) const;
-
-	// Screenspace check to see if this is below si2. Assumes this overlaps si2
-	inline bool below(const SortItem &si2) const;
-
-	// Comparison for the sorted lists
-	inline bool ListLessThan(const SortItem *other) const {
-		return _z < other->_z || (_z == other->_z && _flat && !other->_flat);
-	}
-
-};
-
-inline bool SortItem::overlap(const SortItem &si2) const {
-	const int point_top_diff[2] = { _sxTop - si2._sxBot, _syTop - si2._syBot };
-	const int point_bot_diff[2] = { _sxBot - si2._sxTop, _syBot - si2._syTop };
-
-	// This function is a bit of a hack. It uses dot products between
-	// points and the lines. Nothing is normalized since that isn't
-	// important
-
-	// 'normal' of top  left line ( 2,-1) of the bounding box
-	const int32 dot_top_left = point_top_diff[0] + point_top_diff[1] * 2;
-
-	// 'normal' of top right line ( 2, 1) of the bounding box
-	const int32 dot_top_right = -point_top_diff[0] + point_top_diff[1] * 2;
-
-	// 'normal' of bot  left line (-2,-1) of the bounding box
-	const int32 dot_bot_left =  point_bot_diff[0] - point_bot_diff[1] * 2;
-
-	// 'normal' of bot right line (-2, 1) of the bounding box
-	const int32 dot_bot_right = -point_bot_diff[0] - point_bot_diff[1] * 2;
-
-	const bool right_clear = _sxRight <= si2._sxLeft;
-	const bool left_clear = _sxLeft >= si2._sxRight;
-	const bool top_left_clear = dot_top_left >= 0;
-	const bool top_right_clear = dot_top_right >= 0;
-	const bool bot_left_clear = dot_bot_left >= 0;
-	const bool bot_right_clear = dot_bot_right >= 0;
-
-	const bool clear = right_clear || left_clear ||
-	                   (bot_right_clear || bot_left_clear) ||
-	                   (top_right_clear || top_left_clear);
-
-	return !clear;
-}
-
-inline bool SortItem::occludes(const SortItem &si2) const {
-	const int point_top_diff[2] = { _sxTop - si2._sxTop, _syTop - si2._syTop };
-	const int point_bot_diff[2] = { _sxBot - si2._sxBot, _syBot - si2._syBot };
-
-	// This function is a bit of a hack. It uses dot products between
-	// points and the lines. Nothing is normalized since that isn't
-	// important
-
-	// 'normal' of top left line ( 2, -1) of the bounding box
-	const int32 dot_top_left = point_top_diff[0] + point_top_diff[1] * 2;
-
-	// 'normal' of top right line ( 2, 1) of the bounding box
-	const int32 dot_top_right = -point_top_diff[0] + point_top_diff[1] * 2;
-
-	// 'normal' of bot  left line (-2,-1) of the bounding box
-	const int32 dot_bot_left =  point_bot_diff[0] - point_bot_diff[1] * 2;
-
-	// 'normal' of bot right line (-2, 1) of the bounding box
-	const int32 dot_bot_right = -point_bot_diff[0] - point_bot_diff[1] * 2;
-
-
-	const bool right_res = _sxRight >= si2._sxRight;
-	const bool left_res = _sxLeft <= si2._sxLeft;
-	const bool top_left_res = dot_top_left <= 0;
-	const bool top_right_res = dot_top_right <= 0;
-	const bool bot_left_res = dot_bot_left <= 0;
-	const bool bot_right_res = dot_bot_right <= 0;
-
-	return right_res && left_res && bot_right_res && bot_left_res &&
-		top_right_res && top_left_res;
-}
-
-inline bool SortItem::below(const SortItem &si2) const {
-	const SortItem &si1 = *this;
-
-	if (si1._sprite != si2._sprite)
-		return si1._sprite < si2._sprite;
-
-	// Clearly in y?
-	if (si1._y <= si2._yFar)
-		return true;
-	if (si1._yFar >= si2._y)
-		return false;
-
-	// Clearly in x?
-	if (si1._x <= si2._xLeft)
-		return true;
-	if (si1._xLeft >= si2._x)
-		return false;
-
-	// Specialist z flat handling
-	if (si1._flat && si2._flat) {
-		// Differing z is easy for flats
-		if (si1._zTop != si2._zTop)
-			return si1._zTop < si2._zTop;
-
-		// Equal z
-
-		// Animated always gets drawn after
-		if (si1._anim != si2._anim)
-			return si1._anim < si2._anim;
-
-		// Trans always gets drawn after
-		if (si1._trans != si2._trans)
-			return si1._trans < si2._trans;
-
-		// Draw always gets drawn first
-		if (si1._draw != si2._draw)
-			return si1._draw > si2._draw;
-
-		// Solid always gets drawn first
-		if (si1._solid != si2._solid)
-			return si1._solid > si2._solid;
-
-		// Occludes always get drawn first
-		if (si1._occl != si2._occl)
-			return si1._occl > si2._occl;
-
-		// 32x32 flats get drawn first
-		if (si1._f32x32 != si2._f32x32)
-			return si1._f32x32 > si2._f32x32;
-	}
-	// Mixed, or non flat
-	else {
-		// Inv items always drawn first if their z-bottom is equal or higher. 
-		// This is a bit of a hack as 2 places in Crusader there are keycards
-		// on tables but their z position is the bottom z of the table.
-		if (si1._invitem) {
-			if (si1._z >= si2._z)
-				return false;
-		}
-
-		// Clearly in z
-		if (si1._zTop <= si2._z)
-			return true;
-
-		if (si1._z >= si2._zTop)
-			return false;
-	}
-
-	// Are overlapping in all 3 dimentions if we come here
-
-	// Overlapping z-bottom check
-	// If an object's base (z-bottom) is higher another's, it should be rendered after.
-	// This check must be on the z-bottom and not the z-top because two objects with the
-	// same z-position may have different heights (think of a mouse sorting vs the Avatar).
-	if (si1._z != si2._z)
-		return si1._z < si2._z;
-
-	// Partial in X + Y front
-	if (si1._x + si1._y != si2._x + si2._y)
-		return (si1._x + si1._y < si2._x + si2._y);
-
-	// Partial in X + Y back
-	if (si1._xLeft + si1._yFar != si2._xLeft + si2._yFar)
-		return (si1._xLeft + si1._yFar < si2._xLeft + si2._yFar);
-
-	// Partial in y?
-	if (si1._y != si2._y)
-		return si1._y < si2._y;
-
-	// Partial in x?
-	if (si1._x != si2._x)
-		return si1._x < si2._x;
-
-	// Just sort by shape number
-	if (si1._shapeNum != si2._shapeNum)
-		return si1._shapeNum < si2._shapeNum;
-
-	// And then by _frame
-	return si1._frame < si2._frame;
-}
-
-ConsoleStream &operator<<(ConsoleStream &cs, const SortItem &si) {
-	cs << si._shapeNum << ":" << si._frame <<
-		" (" << si._xLeft << "," << si._yFar << "," << si._z << ")" <<
-		" (" << si._x << "," << si._y << "," << si._zTop << "): ";
-	if (si._sprite)
-		cs << "sprite ";
-	if (si._flat)
-		cs << "flat ";
-	if (si._anim)
-		cs << "anim ";
-	if (si._trans)
-		cs << "trans ";
-	if (si._draw)
-		cs << "draw ";
-	if (si._solid)
-		cs << "solid ";
-	if (si._occl)
-		cs << "occl ";
-	if (si._f32x32)
-		cs << "f32x32 ";
-	if (si._roof)
-		cs << "roof ";
-	if (si._land)
-		cs << "land ";
-	if (si._noisy)
-		cs << "noisy ";
-
-	return cs;
-}
-
-//
-// ItemSorter
-//
-
 ItemSorter::ItemSorter() :
 	_shapes(nullptr), _surf(nullptr), _items(nullptr), _itemsTail(nullptr),
 	_itemsUnused(nullptr), _sortLimit(0), _camSx(0), _camSy(0), _orderCounter(0) {
diff --git a/engines/ultima/ultima8/world/sort_item.h b/engines/ultima/ultima8/world/sort_item.h
new file mode 100644
index 0000000000..d867e33171
--- /dev/null
+++ b/engines/ultima/ultima8/world/sort_item.h
@@ -0,0 +1,438 @@
+/* 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_WORLD_SORTITEM_H
+#define ULTIMA8_WORLD_SORTITEM_H
+
+#include "ultima/ultima8/misc/common_types.h"
+#include "ultima/ultima8/misc/debugger.h"
+
+namespace Ultima {
+namespace Ultima8 {
+
+class Shape;
+
+/**
+ * This class is basically private to ItemSorter, but is in a separate header
+ * to enable unit testing.
+ *
+ * Other code should have no reason to include it.
+ */
+struct SortItem {
+	SortItem(SortItem *n) : _next(n), _prev(nullptr), _itemNum(0),
+			_shape(nullptr), _order(-1), _depends(), _shapeNum(0),
+			_frame(0), _flags(0), _extFlags(0), _sx(0), _sy(0),
+			_sx2(0), _sy2(0), _x(0), _y(0), _z(0), _xLeft(0),
+			_yFar(0), _zTop(0), _sxLeft(0), _sxRight(0), _sxTop(0),
+			_syTop(0), _sxBot(0), _syBot(0),_f32x32(false), _flat(false),
+			_occl(false), _solid(false), _draw(false), _roof(false),
+			_noisy(false), _anim(false), _trans(false), _fixed(false),
+			_land(false), _occluded(false), _clipped(false), _sprite(false),
+			_invitem(false) { }
+
+	SortItem                *_next;
+	SortItem                *_prev;
+
+	uint16                  _itemNum;   // Owner item number
+
+	const Shape             *_shape;
+	uint32                  _shapeNum;
+	uint32                  _frame;
+	uint32                  _flags;     // Item flags
+	uint32                  _extFlags;  // Item extended flags
+
+	int                     _sx, _sx2;  // Screenspace X coords
+	int                     _sy, _sy2;  // Screenspace Y coords
+
+	/*
+	            Bounding Box layout
+
+	       1
+	     /   \
+	   /       \     1 = Left  Far  Top LFT --+
+	 2           3   2 = Left  Near Top LNT -++
+	 | \       / |   3 = Right Far  Top RFT +-+
+	 |   \   /   |   4 = Right Near Top RNT +++
+	 |     4     |   5 = Left  Near Bot LNB -+-
+	 |     |     |   6 = Right Far  Bot RFB +--
+	 5     |     6   7 = Right Near Bot RNB ++-
+	   \   |   /     8 = Left  Far  Bot LFB --- (not shown)
+	     \ | /
+	       7
+
+	*/
+
+	int32   _x, _xLeft;   // Worldspace bounding box x (xright = x)
+	int32   _y, _yFar;    // Worldspace bounding box y (ynear = y)
+	int32   _z, _zTop;    // Worldspace bounding box z (_zTop = z)
+
+	int32   _sxLeft;     // Screenspace bounding box left extent    (LNT x coord)
+	int32   _sxRight;    // Screenspace bounding box right extent   (RFT x coord)
+
+	int32   _sxTop;      // Screenspace bounding box top x coord    (LFT x coord)
+	int32   _syTop;      // Screenspace bounding box top extent     (LFT y coord)
+
+	int32   _sxBot;      // Screenspace bounding box bottom x coord (RNB x coord) ss origin
+	int32   _syBot;      // Screenspace bounding box bottom extent  (RNB y coord) ss origin
+
+	bool    _f32x32 : 1;         // Needs 1 bit  0
+	bool    _flat : 1;           // Needs 1 bit  1
+	bool    _occl : 1;           // Needs 1 bit  2
+	bool    _solid : 1;          // Needs 1 bit  3
+	bool    _draw : 1;           // Needs 1 bit  4
+	bool    _roof : 1;           // Needs 1 bit  5
+	bool    _noisy : 1;          // Needs 1 bit  6
+	bool    _anim : 1;           // Needs 1 bit  7
+	bool    _trans : 1;          // Needs 1 bit  8
+	bool    _fixed : 1;
+	bool    _land : 1;
+	bool 	_sprite : 1;         // Always-on-top sprite, for Crusader (U8 sprites appear in z order)
+	bool 	_invitem : 1;        // Crusader inventory item, should appear above other things
+
+	bool    _occluded : 1;       // Set true if occluded
+	bool  	_clipped : 1;        // Clipped to RenderSurface
+
+	int32   _order;      // Rendering _order. -1 is not yet drawn
+
+	// Note that Std::priority_queue could be used here, BUT there is no guarentee that it's implementation
+	// will be friendly to insertions
+	// Alternatively i could use Std::list, BUT there is no guarentee that it will keep wont delete
+	// the unused nodes after doing a clear
+	// So the only reasonable solution is to write my own list
+	struct DependsList {
+		struct Node {
+			Node        *_next;
+			Node        *_prev;
+			SortItem    *val;
+			Node() : _next(nullptr), _prev(nullptr), val(nullptr) { }
+		};
+
+		Node *list;
+		Node *tail;
+		Node *unused;
+
+		struct iterator {
+			Node *n;
+			SortItem *&operator *() {
+				return n->val;
+			}
+			iterator(Node *node) : n(node) { }
+			iterator &operator++() {
+				n = n->_next;
+				return *this;
+			}
+			bool operator != (const iterator &o) const {
+				return n != o.n;
+			}
+		};
+
+		iterator begin() const {
+			return iterator(list);
+		}
+		iterator end() const {
+			return iterator(nullptr);
+		}
+
+		void clear() {
+			if (tail) {
+				tail->_next = unused;
+				unused = list;
+				tail = nullptr;
+				list = nullptr;
+			}
+		}
+
+		void push_back(SortItem *other) {
+			if (!unused) unused = new Node();
+			Node *nn = unused;
+			unused = unused->_next;
+			nn->val = other;
+
+			// Put it at the end
+			if (tail) tail->_next = nn;
+			if (!list) list = nn;
+			nn->_next = nullptr;
+			nn->_prev = tail;
+			tail = nn;
+		}
+
+		void insert_sorted(SortItem *other) {
+			if (!unused) unused = new Node();
+			Node *nn = unused;
+			unused = unused->_next;
+			nn->val = other;
+
+			for (Node *n = list; n != nullptr; n = n->_next) {
+				// Get the insert point... which is before the first item that has higher z than us
+				if (other->ListLessThan(n->val)) {
+					nn->_next = n;
+					nn->_prev = n->_prev;
+					n->_prev = nn;
+					if (nn->_prev) nn->_prev->_next = nn;
+					else list = nn;
+					return;
+				}
+			}
+
+			// No suitable, so put at end
+			if (tail) tail->_next = nn;
+			if (!list) list = nn;
+			nn->_next = nullptr;
+			nn->_prev = tail;
+			tail = nn;
+		}
+
+		DependsList() : list(nullptr), tail(nullptr), unused(nullptr) { }
+
+		~DependsList() {
+			clear();
+			while (unused)  {
+				Node *n = unused->_next;
+				delete unused;
+				unused = n;
+			}
+		}
+	};
+
+	//Std::vector<SortItem *>   _depends;    // All this Items dependencies (i.e. all objects behind)
+	//Std::list<SortItem *> _depends;    // All this Items dependencies (i.e. all objects behind)
+	DependsList _depends;
+
+	// Functions
+
+	// Screenspace check to see if this overlaps si2
+	inline bool overlap(const SortItem &si2) const;
+
+	// Screenspace check to see if this occludes si2. Assumes this is above of si2
+	inline bool occludes(const SortItem &si2) const;
+
+	// Screenspace check to see if this is below si2. Assumes this overlaps si2
+	inline bool below(const SortItem &si2) const;
+
+	// Comparison for the sorted lists
+	inline bool ListLessThan(const SortItem *other) const {
+		return _z < other->_z || (_z == other->_z && _flat && !other->_flat);
+	}
+
+};
+
+inline bool SortItem::overlap(const SortItem &si2) const {
+	const int point_top_diff[2] = { _sxTop - si2._sxBot, _syTop - si2._syBot };
+	const int point_bot_diff[2] = { _sxBot - si2._sxTop, _syBot - si2._syTop };
+
+	// This function is a bit of a hack. It uses dot products between
+	// points and the lines. Nothing is normalized since that isn't
+	// important
+
+	// 'normal' of top  left line ( 2,-1) of the bounding box
+	const int32 dot_top_left = point_top_diff[0] + point_top_diff[1] * 2;
+
+	// 'normal' of top right line ( 2, 1) of the bounding box
+	const int32 dot_top_right = -point_top_diff[0] + point_top_diff[1] * 2;
+
+	// 'normal' of bot  left line (-2,-1) of the bounding box
+	const int32 dot_bot_left =  point_bot_diff[0] - point_bot_diff[1] * 2;
+
+	// 'normal' of bot right line (-2, 1) of the bounding box
+	const int32 dot_bot_right = -point_bot_diff[0] - point_bot_diff[1] * 2;
+
+	const bool right_clear = _sxRight <= si2._sxLeft;
+	const bool left_clear = _sxLeft >= si2._sxRight;
+	const bool top_left_clear = dot_top_left >= 0;
+	const bool top_right_clear = dot_top_right >= 0;
+	const bool bot_left_clear = dot_bot_left >= 0;
+	const bool bot_right_clear = dot_bot_right >= 0;
+
+	const bool clear = right_clear || left_clear ||
+	                   (bot_right_clear || bot_left_clear) ||
+	                   (top_right_clear || top_left_clear);
+
+	return !clear;
+}
+
+inline bool SortItem::occludes(const SortItem &si2) const {
+	const int point_top_diff[2] = { _sxTop - si2._sxTop, _syTop - si2._syTop };
+	const int point_bot_diff[2] = { _sxBot - si2._sxBot, _syBot - si2._syBot };
+
+	// This function is a bit of a hack. It uses dot products between
+	// points and the lines. Nothing is normalized since that isn't
+	// important
+
+	// 'normal' of top left line ( 2, -1) of the bounding box
+	const int32 dot_top_left = point_top_diff[0] + point_top_diff[1] * 2;
+
+	// 'normal' of top right line ( 2, 1) of the bounding box
+	const int32 dot_top_right = -point_top_diff[0] + point_top_diff[1] * 2;
+
+	// 'normal' of bot  left line (-2,-1) of the bounding box
+	const int32 dot_bot_left =  point_bot_diff[0] - point_bot_diff[1] * 2;
+
+	// 'normal' of bot right line (-2, 1) of the bounding box
+	const int32 dot_bot_right = -point_bot_diff[0] - point_bot_diff[1] * 2;
+
+
+	const bool right_res = _sxRight >= si2._sxRight;
+	const bool left_res = _sxLeft <= si2._sxLeft;
+	const bool top_left_res = dot_top_left <= 0;
+	const bool top_right_res = dot_top_right <= 0;
+	const bool bot_left_res = dot_bot_left <= 0;
+	const bool bot_right_res = dot_bot_right <= 0;
+
+	return right_res && left_res && bot_right_res && bot_left_res &&
+		top_right_res && top_left_res;
+}
+
+inline bool SortItem::below(const SortItem &si2) const {
+	const SortItem &si1 = *this;
+
+	if (si1._sprite != si2._sprite)
+		return si1._sprite < si2._sprite;
+
+	// Clearly in y?
+	if (si1._y <= si2._yFar)
+		return true;
+	if (si1._yFar >= si2._y)
+		return false;
+
+	// Clearly in x?
+	if (si1._x <= si2._xLeft)
+		return true;
+	if (si1._xLeft >= si2._x)
+		return false;
+
+	// Specialist z flat handling
+	if (si1._flat && si2._flat) {
+		// Differing z is easy for flats
+		if (si1._zTop != si2._zTop)
+			return si1._zTop < si2._zTop;
+
+		// Equal z
+
+		// Animated always gets drawn after
+		if (si1._anim != si2._anim)
+			return si1._anim < si2._anim;
+
+		// Trans always gets drawn after
+		if (si1._trans != si2._trans)
+			return si1._trans < si2._trans;
+
+		// Draw always gets drawn first
+		if (si1._draw != si2._draw)
+			return si1._draw > si2._draw;
+
+		// Solid always gets drawn first
+		if (si1._solid != si2._solid)
+			return si1._solid > si2._solid;
+
+		// Occludes always get drawn first
+		if (si1._occl != si2._occl)
+			return si1._occl > si2._occl;
+
+		// 32x32 flats get drawn first
+		if (si1._f32x32 != si2._f32x32)
+			return si1._f32x32 > si2._f32x32;
+	}
+	// Mixed, or non flat
+	else {
+		// Inv items always drawn first if their z-bottom is equal or higher. 
+		// This is a bit of a hack as 2 places in Crusader there are keycards
+		// on tables but their z position is the bottom z of the table.
+		if (si1._invitem) {
+			if (si1._z >= si2._z)
+				return false;
+		}
+
+		// Clearly in z
+		if (si1._zTop <= si2._z)
+			return true;
+
+		if (si1._z >= si2._zTop)
+			return false;
+	}
+
+	// Are overlapping in all 3 dimentions if we come here
+
+	// Overlapping z-bottom check
+	// If an object's base (z-bottom) is higher another's, it should be rendered after.
+	// This check must be on the z-bottom and not the z-top because two objects with the
+	// same z-position may have different heights (think of a mouse sorting vs the Avatar).
+	if (si1._z != si2._z)
+		return si1._z < si2._z;
+
+	// Partial in X + Y front
+	if (si1._x + si1._y != si2._x + si2._y)
+		return (si1._x + si1._y < si2._x + si2._y);
+
+	// Partial in X + Y back
+	if (si1._xLeft + si1._yFar != si2._xLeft + si2._yFar)
+		return (si1._xLeft + si1._yFar < si2._xLeft + si2._yFar);
+
+	// Partial in y?
+	if (si1._y != si2._y)
+		return si1._y < si2._y;
+
+	// Partial in x?
+	if (si1._x != si2._x)
+		return si1._x < si2._x;
+
+	// Just sort by shape number
+	if (si1._shapeNum != si2._shapeNum)
+		return si1._shapeNum < si2._shapeNum;
+
+	// And then by _frame
+	return si1._frame < si2._frame;
+}
+
+ConsoleStream &operator<<(ConsoleStream &cs, const SortItem &si) {
+	cs << si._shapeNum << ":" << si._frame <<
+		" (" << si._xLeft << "," << si._yFar << "," << si._z << ")" <<
+		" (" << si._x << "," << si._y << "," << si._zTop << "): ";
+	if (si._sprite)
+		cs << "sprite ";
+	if (si._flat)
+		cs << "flat ";
+	if (si._anim)
+		cs << "anim ";
+	if (si._trans)
+		cs << "trans ";
+	if (si._draw)
+		cs << "draw ";
+	if (si._solid)
+		cs << "solid ";
+	if (si._occl)
+		cs << "occl ";
+	if (si._f32x32)
+		cs << "f32x32 ";
+	if (si._roof)
+		cs << "roof ";
+	if (si._land)
+		cs << "land ";
+	if (si._noisy)
+		cs << "noisy ";
+
+	return cs;
+}
+
+} // End of namespace Ultima8
+} // End of namespace Ultima
+
+#endif
diff --git a/test/engines/ultima/ultima8/world/sort_item.h b/test/engines/ultima/ultima8/world/sort_item.h
new file mode 100644
index 0000000000..f471816b4a
--- /dev/null
+++ b/test/engines/ultima/ultima8/world/sort_item.h
@@ -0,0 +1,77 @@
+#include <cxxtest/TestSuite.h>
+#include "engines/ultima/ultima8/world/sort_item.h"
+
+/**
+ * Test suite for the functions in engines/ultima/ultima8/world/sort_item.h
+ *
+ * Be aware that the x and y coordinates go opposite to what you might expect,
+ * see the notes in sort_item.h
+ *
+ * Still TODO tests:
+ *  * overlapping in various dimensions
+ *  * flat (z == zTop) items with various flags
+ *  * special case for crusader inventory items
+ *  * items that are flat in x or y (what should these do?)
+ */
+class U8SortItemTestSuite : public CxxTest::TestSuite {
+	public:
+	U8SortItemTestSuite() {
+	}
+
+	/* Non-overlapping with lower Y position should always be below */
+	void test_basic_y_sort() {
+		Ultima::Ultima8::SortItem si1(nullptr);
+		Ultima::Ultima8::SortItem si2(nullptr);
+
+		si1._yFar = 0;
+		si1._y = 10;
+		si2._yFar = 20;
+		si2._y = 30;
+		si1._x = si2._x = 10;
+		TS_ASSERT(si1.below(si2));
+		TS_ASSERT(!si2.below(si1));
+	}
+
+	/* Non-overlapping with lower X position should always be below */
+	void test_basic_x_sort() {
+		Ultima::Ultima8::SortItem si1(nullptr);
+		Ultima::Ultima8::SortItem si2(nullptr);
+
+		si1._y = si2._y = 10;
+		si1._x = 10;
+		si2._xLeft = 20;
+		si2._x = 30;
+		TS_ASSERT(si1.below(si2));
+		TS_ASSERT(!si2.below(si1));
+	}
+
+	/* Non-overlapping with lower Z position should always be below */
+	void test_basic_z_sort() {
+		Ultima::Ultima8::SortItem si1(nullptr);
+		Ultima::Ultima8::SortItem si2(nullptr);
+
+		si1._x = si2._x = si1._y = si2._y = 10;
+		si1._zTop = 10;
+		si2._z = 20;
+		si2._zTop = 30;
+		TS_ASSERT(si1.below(si2));
+		TS_ASSERT(!si2.below(si1));
+	}
+
+	/* Sprites should always be at the top regardless of x/y/z */
+	void test_sprite_sort() {
+		Ultima::Ultima8::SortItem si1(nullptr);
+		Ultima::Ultima8::SortItem si2(nullptr);
+
+		si2._sprite = true;
+		TS_ASSERT(si1.below(si2));
+		TS_ASSERT(!si2.below(si1));
+
+		si2._x = si2._xLeft = si1._y = si2._yFar = 10;
+		si2._z = 20;
+		si2._zTop = 30;
+		TS_ASSERT(si1.below(si2));
+		TS_ASSERT(!si2.below(si1));
+	}
+
+};


Commit: a1d7389f106eaa50777d6eda485da5504edd1083
    https://github.com/scummvm/scummvm/commit/a1d7389f106eaa50777d6eda485da5504edd1083
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2021-07-07T19:34:05+09:00

Commit Message:
ULTIMA8: Remove outdated TODO comment

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


diff --git a/engines/ultima/ultima8/world/item.cpp b/engines/ultima/ultima8/world/item.cpp
index c616e7e610..908dea3ee4 100644
--- a/engines/ultima/ultima8/world/item.cpp
+++ b/engines/ultima/ultima8/world/item.cpp
@@ -1366,7 +1366,6 @@ uint16 Item::fireDistance(const Item *other, Direction dir, int16 xoff, int16 yo
 			else
 				anim = Animation::kneelAndFireLargeWeapon;
 		} else {
-			// TODO: fireLarge seems to be different ID in Regret, check me.
 			if (ma || smallwpn)
 				anim = Animation::fireSmallWeapon;
 			else


Commit: 5e97b554d11d7a21de78c6744628c8f9b229cc5c
    https://github.com/scummvm/scummvm/commit/5e97b554d11d7a21de78c6744628c8f9b229cc5c
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2021-07-07T19:34:05+09:00

Commit Message:
ULTIMA8: Fix Crusader firedistance for non-avatar NPCs

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


diff --git a/engines/ultima/ultima8/world/item.cpp b/engines/ultima/ultima8/world/item.cpp
index 908dea3ee4..6336a19432 100644
--- a/engines/ultima/ultima8/world/item.cpp
+++ b/engines/ultima/ultima8/world/item.cpp
@@ -1366,7 +1366,7 @@ uint16 Item::fireDistance(const Item *other, Direction dir, int16 xoff, int16 yo
 			else
 				anim = Animation::kneelAndFireLargeWeapon;
 		} else {
-			if (ma || smallwpn)
+			if (smallwpn || !ma)
 				anim = Animation::fireSmallWeapon;
 			else
 				anim = Animation::fireLargeWeapon;


Commit: e523874936ae9e7dd733f9ea7cb41634ce1ec762
    https://github.com/scummvm/scummvm/commit/e523874936ae9e7dd733f9ea7cb41634ce1ec762
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2021-07-07T19:34:05+09:00

Commit Message:
ULTIMA8: Allow running in No Remorse rebel base.

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


diff --git a/engines/ultima/ultima8/world/actors/cru_avatar_mover_process.cpp b/engines/ultima/ultima8/world/actors/cru_avatar_mover_process.cpp
index 548697626a..3be9ac8af4 100644
--- a/engines/ultima/ultima8/world/actors/cru_avatar_mover_process.cpp
+++ b/engines/ultima/ultima8/world/actors/cru_avatar_mover_process.cpp
@@ -310,7 +310,12 @@ void CruAvatarMoverProcess::handleNormalMode() {
 	if (!hasMovementFlags(MOVE_ANY_DIRECTION) && lastanim == Animation::run) {
 		// if we were running, slow to a walk before stopping
 		// (even in stasis)
-		waitFor(avatar->doAnim(Animation::stopRunningAndDrawSmallWeapon, direction));
+		Animation::Sequence nextanim;
+		if (rebelBase)
+			nextanim = Animation::stand;
+		else
+			nextanim = Animation::stopRunningAndDrawSmallWeapon;
+		waitFor(avatar->doAnim(nextanim, direction));
 		avatar->setInCombat(0);
 		return;
 	}
@@ -330,7 +335,7 @@ void CruAvatarMoverProcess::handleNormalMode() {
 
 	Animation::Sequence nextanim = Animation::walk;
 
-	if (!rebelBase && hasMovementFlags(MOVE_RUN)) {
+	if (hasMovementFlags(MOVE_RUN)) {
 		if (lastanim == Animation::run
 			|| lastanim == Animation::startRun
 			|| lastanim == Animation::startRunSmallWeapon


Commit: a9ae8be3c97f881c08bcc40f7d3fcf0e465be676
    https://github.com/scummvm/scummvm/commit/a9ae8be3c97f881c08bcc40f7d3fcf0e465be676
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2021-07-07T19:34:05+09:00

Commit Message:
ULTIMA: Regenerate ultima.dat and bump U8 data version

* Updates information for Crusader games
* Remove Thumbs.db files from zip
* Bump data version number to 2.0

Changed paths:
    devtools/create_ultima/files/ultima8/version.txt
    dists/engine-data/ultima.dat
    engines/ultima/ultima8/ultima8.cpp


diff --git a/devtools/create_ultima/files/ultima8/version.txt b/devtools/create_ultima/files/ultima8/version.txt
index d3827e75a5..cd5ac039d6 100644
--- a/devtools/create_ultima/files/ultima8/version.txt
+++ b/devtools/create_ultima/files/ultima8/version.txt
@@ -1 +1 @@
-1.0
+2.0
diff --git a/dists/engine-data/ultima.dat b/dists/engine-data/ultima.dat
index 1b18ac3f07..7e861baf46 100644
Binary files a/dists/engine-data/ultima.dat and b/dists/engine-data/ultima.dat differ
diff --git a/engines/ultima/ultima8/ultima8.cpp b/engines/ultima/ultima8/ultima8.cpp
index dc084e7215..42f4d49377 100644
--- a/engines/ultima/ultima8/ultima8.cpp
+++ b/engines/ultima/ultima8/ultima8.cpp
@@ -1624,7 +1624,9 @@ uint32 Ultima8Engine::I_moveKeyDownRecently(const uint8 *args, unsigned int /*ar
 
 bool Ultima8Engine::isDataRequired(Common::String &folder, int &majorVersion, int &minorVersion) {
 	folder = "ultima8";
-	majorVersion = 1;
+	// Version 1: Initial release
+	// Version 2: Add data for Crusader games
+	majorVersion = 2;
 	minorVersion = 0;
 	return true;
 }




More information about the Scummvm-git-logs mailing list