[Scummvm-git-logs] scummvm master -> 2620f50339eeb7bd326ac28bc4676a86b089abac

OMGPizzaGuy noreply at scummvm.org
Wed Oct 11 00:55:56 UTC 2023


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

Summary:
2620f50339 ULTIMA8: Refactor valid position checking


Commit: 2620f50339eeb7bd326ac28bc4676a86b089abac
    https://github.com/scummvm/scummvm/commit/2620f50339eeb7bd326ac28bc4676a86b089abac
Author: Matthew Jimenez (matthew.jimenez at outlook.com)
Date: 2023-10-10T19:55:34-05:00

Commit Message:
ULTIMA8: Refactor valid position checking

Changed paths:
  A engines/ultima/ultima8/world/position_info.h
    engines/ultima/ultima8/gumps/game_map_gump.cpp
    engines/ultima/ultima8/world/actors/actor.cpp
    engines/ultima/ultima8/world/actors/animation_tracker.cpp
    engines/ultima/ultima8/world/actors/quick_avatar_mover_process.cpp
    engines/ultima/ultima8/world/actors/rolling_thunder_process.cpp
    engines/ultima/ultima8/world/camera_process.cpp
    engines/ultima/ultima8/world/current_map.cpp
    engines/ultima/ultima8/world/current_map.h
    engines/ultima/ultima8/world/item.cpp


diff --git a/engines/ultima/ultima8/gumps/game_map_gump.cpp b/engines/ultima/ultima8/gumps/game_map_gump.cpp
index 648f4ca57b2..53a0a297c89 100644
--- a/engines/ultima/ultima8/gumps/game_map_gump.cpp
+++ b/engines/ultima/ultima8/gumps/game_map_gump.cpp
@@ -91,18 +91,19 @@ void GameMapGump::PaintThis(RenderSurface *surf, int32 lerp_factor, bool scaled)
 	uint16 roofid = 0;
 	int zlimit = 1 << 16; // should be high enough
 
+	const Item *roof = nullptr;
 	if (!camera) {
 		// Check roof
 		//!! This is _not_ the right place for this...
-		int32 ax, ay, az, axd, ayd, azd;
 		const Actor *av = getMainActor();
-		av->getLocation(ax, ay, az);
-		av->getFootpadWorld(axd, ayd, azd);
-		map->isValidPosition(ax, ay, az, 32, 32, 8, 0, 1, 0, &roofid);
-	} else
+		Box b = av->getWorldBox();
+		PositionInfo info = map->getPositionInfo(b, b, 0, 1);
+		roof = info.roof;
+	} else {
 		roofid = camera->findRoof(lerp_factor);
+		roof = getItem(roofid);
+	}
 
-	const Item *roof = getItem(roofid);
 	if (roof) {
 		zlimit = roof->getZ();
 	}
diff --git a/engines/ultima/ultima8/world/actors/actor.cpp b/engines/ultima/ultima8/world/actors/actor.cpp
index a904a4ab8bd..88382241cf4 100644
--- a/engines/ultima/ultima8/world/actors/actor.cpp
+++ b/engines/ultima/ultima8/world/actors/actor.cpp
@@ -1444,7 +1444,7 @@ ProcId Actor::dieCru(uint16 damageType, uint16 damagePts, Direction srcDir) {
 				moveToEtherealVoid();
 				CurrentMap *cm = world->getCurrentMap();
 				/* 0x576 - flaming guy running around */
-				bool can_create_koresh = cm->isValidPosition(_x, _y, _z, 0x576, _objId);
+				bool can_create_koresh = cm->getPositionInfo(_x, _y, _z, 0x576, _objId).valid;
 				returnFromEtherealVoid();
 
 				if (can_create_koresh) {
diff --git a/engines/ultima/ultima8/world/actors/animation_tracker.cpp b/engines/ultima/ultima8/world/actors/animation_tracker.cpp
index 405a57edbb9..b9d0e7fe724 100644
--- a/engines/ultima/ultima8/world/actors/animation_tracker.cpp
+++ b/engines/ultima/ultima8/world/actors/animation_tracker.cpp
@@ -307,16 +307,13 @@ bool AnimationTracker::step() {
 		// If it succeeded, we proceed as usual
 	}
 
-	const Item *support;
-	bool targetok = cm->isValidPosition(tx, ty, tz,
-	                                    _startX, _startY, _startZ,
-	                                    xd, yd, zd,
-	                                    a->getShapeInfo()->_flags,
-	                                    _actor, &support, 0);
-
-	if (is_u8 && targetok && support) {
+	Box target(tx, ty, tz, xd, yd, zd);
+	Box start(_startX, _startY, _startZ, xd, yd, zd);
+	PositionInfo info = cm->getPositionInfo(target, start, a->getShapeInfo()->_flags, _actor);
+
+	if (is_u8 && info.valid && info.supported) {
 		// Might need to check for bridge traversal adjustments
-		uint32 supportshape = support->getShape();
+		uint32 supportshape = info.floor->getShape();
 		if (supportshape >= 675 && supportshape <= 681) {
 			// Could be a sloping portion of a bridge.  For a bridge along the
 			// X axis, positive descent delta is a positive change in Y when
@@ -331,13 +328,13 @@ bool AnimationTracker::step() {
 				descentdelta = -20;         // Descend
 
 			if (descentdelta) {
-				if (dy == 0 && dx != 0 && !support->hasFlags(Item::FLG_FLIPPED)) {
+				if (dy == 0 && dx != 0 && !info.floor->hasFlags(Item::FLG_FLIPPED)) {
 					// Moving left or right on horizontal bridge
 					// descentdelta = 60*dy/dx
 					// 60*dy = descentdelta * dx
 					// dy = descentdelta * dx / 60;
 					ty += descentdelta * dx / 60;
-				} else if (dx == 0 && dy != 0 && support->hasFlags(Item::FLG_FLIPPED)) {
+				} else if (dx == 0 && dy != 0 && info.floor->hasFlags(Item::FLG_FLIPPED)) {
 					// Moving up or down on vertical bridge
 					tx += descentdelta * dy / 60;
 				}
@@ -345,14 +342,14 @@ bool AnimationTracker::step() {
 		}
 	}
 
-	if (!targetok || (f.is_onground() && !support)) {
+	if (!info.valid || (f.is_onground() && !info.supported)) {
 		// If on ground, try to adjust properly. Never do it for dead Crusader NPCs,
 		// as they don't get gravity and the death process gets stuck.
 		// TODO: Profile the effect of disabling this for pathfinding.
 		//       It shouldn't be necessary in that case, and may provide a
 		//       worthwhile speed-up.
 		if (f.is_onground() && zd > 8 && !(is_crusader && a->isDead())) {
-			targetok = cm->scanForValidPosition(tx, ty, tz, a, _dir,
+			bool targetok = cm->scanForValidPosition(tx, ty, tz, a, _dir,
 			                                    true, tx, ty, tz);
 
 			if (!targetok) {
@@ -367,7 +364,7 @@ bool AnimationTracker::step() {
 #endif
 			}
 		} else {
-			if (!targetok) {
+			if (!info.valid) {
 				_blocked = true;
 				return false;
 			}
@@ -394,14 +391,10 @@ bool AnimationTracker::step() {
 
 	if (f.is_onground()) {
 		// needs support
+		target = Box(tx, ty, tz, xd, yd, zd);
+		info = cm->getPositionInfo(target, start, a->getShapeInfo()->_flags, _actor);
 
-		/*bool targetok = */ cm->isValidPosition(tx, ty, tz,
-		        _startX, _startY, _startZ,
-		        xd, yd, zd,
-		        a->getShapeInfo()->_flags,
-		        _actor, &support, 0);
-
-		if (!support) {
+		if (!info.supported) {
 			_unsupported = true;
 			return false;
 		}
diff --git a/engines/ultima/ultima8/world/actors/quick_avatar_mover_process.cpp b/engines/ultima/ultima8/world/actors/quick_avatar_mover_process.cpp
index 929fb14b412..25408aac61a 100644
--- a/engines/ultima/ultima8/world/actors/quick_avatar_mover_process.cpp
+++ b/engines/ultima/ultima8/world/actors/quick_avatar_mover_process.cpp
@@ -99,53 +99,62 @@ void QuickAvatarMoverProcess::run() {
 	int32 dyv = dy;
 	int32 dzv = dz;
 
-	for (int j = 0; j < 3; j++) {
-		dxv = dx;
-		dyv = dy;
-		dzv = dz;
-
-		if (j == 1) dxv = 0;
-		else if (j == 2) dyv = 0;
-
-		if (_quarter) {
-			dxv /= 4;
-			dyv /= 4;
-			dzv /= 4;
-		}
+	if (_clipping) {
+		for (int j = 0; j < 3; j++) {
+			dxv = dx;
+			dyv = dy;
+			dzv = dz;
+
+			if (j == 1)
+				dxv = 0;
+			else if (j == 2)
+				dyv = 0;
+
+			if (_quarter) {
+				dxv /= 4;
+				dyv /= 4;
+				dzv /= 4;
+			}
 
-		bool ok = false;
-
-		while (dxv || dyv || dzv) {
-			uint32 shapeFlags = avatar->getShapeInfo()->_flags;
-
-			if (!_clipping || cm->isValidPosition(x + dxv, y + dyv, z + dzv, ixd, iyd, izd, _flags, 1, 0, 0)) {
-				if (_clipping && !dzv) {
-					if (cm->isValidPosition(x + dxv, y + dyv, z - 8, ixd, iyd, izd, _flags, 1, 0, 0) &&
-					        !cm->isValidPosition(x, y, z - 8, ixd, iyd, izd, _flags, 1, 0, 0)) {
-						dzv = -8;
-					} else if (cm->isValidPosition(x + dxv, y + dyv, z - 16, ixd, iyd, izd, _flags, 1, 0, 0) &&
-					           !cm->isValidPosition(x, y, z - 16, ixd, iyd, izd, _flags, 1, 0, 0)) {
-						dzv = -16;
-					} else if (cm->isValidPosition(x + dxv, y + dyv, z - 24, ixd, iyd, izd, _flags, 1, 0, 0) &&
-					           !cm->isValidPosition(x, y, z - 24, ixd, iyd, izd, _flags, 1, 0, 0)) {
-						dzv = -24;
-					} else if (cm->isValidPosition(x + dxv, y + dyv, z - 32, ixd, iyd, izd, _flags, 1, 0, 0) &&
-					           !cm->isValidPosition(x, y, z - 32, ixd, iyd, izd, _flags, 1, 0, 0)) {
-						dzv = -32;
+			bool ok = false;
+
+			while (dxv || dyv || dzv) {
+				uint32 shapeFlags = avatar->getShapeInfo()->_flags;
+
+				Box start(x, y, z, ixd, iyd, izd);
+				PositionInfo info = cm->getPositionInfo(Box(x + dxv, y + dyv, z + dzv, ixd, iyd, izd), start, shapeFlags, 1);
+				if (info.valid) {
+					if (!dzv && !info.supported) {
+						// Adjust to stay on ground
+						if (cm->getPositionInfo(Box(x + dxv, y + dyv, z - 8, ixd, iyd, izd), start, shapeFlags, 1).valid &&
+							!cm->getPositionInfo(Box(x, y, z - 8, ixd, iyd, izd), start, shapeFlags, 1).valid) {
+							dzv = -8;
+						} else if (cm->getPositionInfo(Box(x + dxv, y + dyv, z - 16, ixd, iyd, izd), start, shapeFlags, 1).valid &&
+								   !cm->getPositionInfo(Box(x, y, z - 16, ixd, iyd, izd), start, shapeFlags, 1).valid) {
+							dzv = -16;
+						} else if (cm->getPositionInfo(Box(x + dxv, y + dyv, z - 24, ixd, iyd, izd), start, shapeFlags, 1).valid &&
+								   !cm->getPositionInfo(Box(x, y, z - 24, ixd, iyd, izd), start, shapeFlags, 1).valid) {
+							dzv = -24;
+						} else if (cm->getPositionInfo(Box(x + dxv, y + dyv, z - 32, ixd, iyd, izd), start, shapeFlags, 1).valid &&
+								   !cm->getPositionInfo(Box(x, y, z - 32, ixd, iyd, izd), start, shapeFlags, 1).valid) {
+							dzv = -32;
+						}
 					}
+					ok = true;
+					break;
+				} else if (cm->getPositionInfo(Box(x + dxv, y + dyv, z + dzv + 8, ixd, iyd, izd), start, shapeFlags, 1).valid) {
+					dzv += 8;
+					ok = true;
+					break;
 				}
-				ok = true;
-				break;
-			} else if (cm->isValidPosition(x + dxv, y + dyv, z + dzv + 8, ixd, iyd, izd, shapeFlags, 1, 0, 0)) {
-				dzv += 8;
-				ok = true;
-				break;
+				dxv /= 2;
+				dyv /= 2;
+				dzv /= 2;
 			}
-			dxv /= 2;
-			dyv /= 2;
-			dzv /= 2;
+
+			if (ok)
+				break;
 		}
-		if (ok) break;
 	}
 
 	// Yes, i know, not entirely correct
diff --git a/engines/ultima/ultima8/world/actors/rolling_thunder_process.cpp b/engines/ultima/ultima8/world/actors/rolling_thunder_process.cpp
index 0ef8ab0fd2b..811e8d08e91 100644
--- a/engines/ultima/ultima8/world/actors/rolling_thunder_process.cpp
+++ b/engines/ultima/ultima8/world/actors/rolling_thunder_process.cpp
@@ -260,11 +260,9 @@ bool RollingThunderProcess::fireDistance(Direction dir, int32 x, int32 y, int32
 		int32 cy = y + (i == 0 ? yoff : yoff2);
 		int32 cz = z + (i == 0 ? zoff : zoff2);
 
-		const Item *blocker = nullptr;
-		bool valid = cm->isValidPosition(cx, cy, cz, BULLET_SPLASH_SHAPE,
-									   _itemNum, nullptr, nullptr, &blocker);
-		if (!valid) {
-			if (blocker->getObjId() == target->getObjId())
+		PositionInfo info = cm->getPositionInfo(cx, cy, cz, BULLET_SPLASH_SHAPE, _itemNum);
+		if (!info.valid && info.blocker) {
+			if (info.blocker->getObjId() == target->getObjId())
 				dist = MAX(abs(x - tx), abs(y - ty));
 		} else {
 			int32 ocx, ocy, ocz;
diff --git a/engines/ultima/ultima8/world/camera_process.cpp b/engines/ultima/ultima8/world/camera_process.cpp
index e4610db2cdb..38f45ae51a7 100644
--- a/engines/ultima/ultima8/world/camera_process.cpp
+++ b/engines/ultima/ultima8/world/camera_process.cpp
@@ -289,9 +289,10 @@ uint16 CameraProcess::findRoof(int32 factor) {
 
 	int32 dx, dy, dz;
 	avatar->getFootpadWorld(dx, dy, dz);
-	uint16 roofid;
-	World::get_instance()->getCurrentMap()->isValidPosition(x, y, z - 10, dx / 2, dy / 2, dz / 2, 0, 1, 0, &roofid);
-	return roofid;
+
+	Box target(x, y, z - 10, dx / 2, dy / 2, dz / 2);
+	PositionInfo info = World::get_instance()->getCurrentMap()->getPositionInfo(target, target, 0, 1);
+	return info.roof ? info.roof->getObjId() : 0;
 }
 
 void CameraProcess::saveData(Common::WriteStream *ws) {
diff --git a/engines/ultima/ultima8/world/current_map.cpp b/engines/ultima/ultima8/world/current_map.cpp
index afe2aae44e6..590d3a05396 100644
--- a/engines/ultima/ultima8/world/current_map.cpp
+++ b/engines/ultima/ultima8/world/current_map.cpp
@@ -35,6 +35,7 @@
 #include "ultima/ultima8/games/game_data.h"
 #include "ultima/ultima8/graphics/main_shape_archive.h"
 #include "ultima/ultima8/gumps/game_map_gump.h"
+#include "ultima/ultima8/misc/box.h"
 #include "ultima/ultima8/misc/direction_util.h"
 #include "ultima/ultima8/world/get_object.h"
 
@@ -47,8 +48,6 @@ namespace Ultima8 {
 
 typedef Std::list<Item *> item_list;
 
-static const int INT_MAX_VALUE = 0x7fffffff;
-
 CurrentMap::CurrentMap() : _currentMap(0), _eggHatcher(0),
 	  _fastXMin(-1), _fastYMin(-1), _fastXMax(-1), _fastYMax(-1) {
 	for (unsigned int i = 0; i < MAP_NUM_CHUNKS; i++) {
@@ -726,55 +725,30 @@ const Std::list<Item *> *CurrentMap::getItemList(int32 gx, int32 gy) const {
 	return &_items[gx][gy];
 }
 
-
-bool CurrentMap::isValidPosition(int32 x, int32 y, int32 z,
-								 uint32 shape,
-								 ObjId item, const Item **support,
-								 ObjId *roof, const Item **blocker) const {
-	const ShapeInfo *si = GameData::get_instance()->
-	                getMainShapes()->getShapeInfo(shape);
+PositionInfo CurrentMap::getPositionInfo(int32 x, int32 y, int32 z, uint32 shape, ObjId id) const {
+	const ShapeInfo *si = GameData::get_instance()->getMainShapes()->getShapeInfo(shape);
 	int32 xd, yd, zd;
 	// Note: this assumes the shape to be placed is not flipped
 	si->getFootpadWorld(xd, yd, zd, 0);
+	Box target(x, y, z, xd, yd, zd);
+	Box empty;
 
-	return isValidPosition(x, y, z,
-	                       INT_MAX_VALUE / 2, INT_MAX_VALUE / 2, INT_MAX_VALUE / 2,
-	                       xd, yd, zd,
-	                       si->_flags, item, support, roof, blocker);
+	return getPositionInfo(target, empty, si->_flags, id);
 }
 
-bool CurrentMap::isValidPosition(int32 x, int32 y, int32 z,
-								 int xd, int yd, int zd,
-								 uint32 shapeflags,
-								 ObjId item, const Item **support,
-								 ObjId *roof, const Item **blocker) const {
-	return isValidPosition(x, y, z,
-	                       INT_MAX_VALUE / 2, INT_MAX_VALUE / 2, INT_MAX_VALUE / 2,
-	                       xd, yd, zd,
-	                       shapeflags, item, support, roof, blocker);
-}
-
-
-bool CurrentMap::isValidPosition(int32 x, int32 y, int32 z,
-								 int32 startx, int32 starty, int32 startz,
-								 int xd, int yd, int zd,
-								 uint32 shapeflags,
-								 ObjId item_, const Item **support_,
-								 ObjId *roof_, const Item **blocker_) const {
+PositionInfo CurrentMap::getPositionInfo(const Box &target, const Box &start, uint32 shapeflags, ObjId id) const {
+	PositionInfo info;
 	static const uint32 flagmask = (ShapeInfo::SI_SOLID | ShapeInfo::SI_DAMAGING |
 	                         ShapeInfo::SI_ROOF);
 	static const uint32 blockflagmask = (ShapeInfo::SI_SOLID | ShapeInfo::SI_DAMAGING);
 
-	bool valid = true;
-	const Item *support = nullptr;
-	const Item *blocker = nullptr;
-	ObjId roof = 0;
-	int32 roofz = INT_MAX_VALUE;
+	int32 floorz = INT32_MIN;
+	int32 roofz = INT32_MAX;
 
-	int minx = ((x - xd) / _mapChunkSize) - 1;
-	int maxx = (x / _mapChunkSize) + 1;
-	int miny = ((y - yd) / _mapChunkSize) - 1;
-	int maxy = (y / _mapChunkSize) + 1;
+	int minx = ((target._x - target._xd) / _mapChunkSize) - 1;
+	int maxx = (target._x / _mapChunkSize) + 1;
+	int miny = ((target._y - target._yd) / _mapChunkSize) - 1;
+	int maxy = (target._y / _mapChunkSize) + 1;
 	clipMapChunks(minx, maxx, miny, maxy);
 
 	for (int cx = minx; cx <= maxx; cx++) {
@@ -783,7 +757,7 @@ bool CurrentMap::isValidPosition(int32 x, int32 y, int32 z,
 			for (iter = _items[cx][cy].begin();
 				 iter != _items[cx][cy].end(); ++iter) {
 				const Item *item = *iter;
-				if (item->getObjId() == item_)
+				if (item->getObjId() == id)
 					continue;
 				if (item->hasExtFlags(Item::EXT_SPRITE))
 					continue;
@@ -793,65 +767,42 @@ bool CurrentMap::isValidPosition(int32 x, int32 y, int32 z,
 				if (!(si->_flags & flagmask))
 					continue; // not an interesting item
 
-				int32 ix, iy, iz, ixd, iyd, izd;
-				item->getFootpadWorld(ixd, iyd, izd);
-				item->getLocation(ix, iy, iz);
-
-#if 0
-				if (item->getShape() == 145) {
-					debugC(kDebugObject, "Shape 145: (%d, %d, %d)-(%d, %d, %d) %s",
-						ix - ixd, iy - iyd, iz, ix, iy, iz + izd,
-						si->is_solid() ? "solid" : "not solid");
-				}
-#endif
+				Box ib = item->getWorldBox();
 
 				// check overlap
 				if ((si->_flags & shapeflags & blockflagmask) &&
-				        /* not non-overlapping */
-				        !(x <= ix - ixd || x - xd >= ix ||
-				          y <= iy - iyd || y - yd >= iy ||
-				          z + zd <= iz || z >= iz + izd) &&
-				        /* non-overlapping start position */
-				        (startx <= ix - ixd || startx - xd >= ix ||
-				         starty <= iy - iyd || starty - yd >= iy ||
-				         startz + zd <= iz || startz >= iz + izd)) {
+					target.overlaps(ib) && !start.overlaps(ib)) {
 					// overlapping an item. Invalid position
 #if 0
 					debugC(kDebugObject, "%s", item->dumpInfo().c_str());
 #endif
-					if (blocker == nullptr) {
-						blocker = item;
+					if (info.blocker == nullptr) {
+						info.blocker = item;
 					}
-					valid = false;
 				}
 
 				// check xy overlap
-				if (!(x <= ix - ixd || x - xd >= ix ||
-				      y <= iy - iyd || y - yd >= iy)) {
-					// check support
-					if (support == nullptr && si->is_solid() &&
-					        iz + izd == z) {
-						support = item;
+				if (target._x > ib._x - ib._xd && target._x - target._xd < ib._x &&
+					target._y > ib._y - ib._yd && target._y - target._yd < ib._y) {
+					// check floor
+					if (si->is_solid() && ib._z + ib._zd > floorz && ib._z + ib._zd <= target._z) {
+						info.floor = item;
+						floorz = ib._z + ib._zd;
 					}
 
 					// check roof
-					if (si->is_roof() && iz < roofz && iz >= z + zd) {
-						roof = item->getObjId();
-						roofz = iz;
+					if (si->is_roof() && ib._z < roofz && ib._z >= target._z + target._zd) {
+						info.roof = item;
+						roofz = ib._z;
 					}
 				}
 			}
 		}
 	}
 
-	if (support_)
-		*support_ = support;
-	if (blocker_)
-		*blocker_ = blocker;
-	if (roof_)
-		*roof_ = roof;
-
-	return valid;
+	info.valid = info.blocker == nullptr;
+	info.supported = floorz == target._z;
+	return info;
 }
 
 bool CurrentMap::scanForValidPosition(int32 x, int32 y, int32 z, const Item *item,
@@ -1322,9 +1273,9 @@ uint32 CurrentMap::I_canExistAt(const uint8 *args, unsigned int argsize) {
 	//
 
 	const CurrentMap *cm = World::get_instance()->getCurrentMap();
-	bool valid = cm->isValidPosition(x, y, z, shape, 0, 0, 0);
+	PositionInfo info = cm->getPositionInfo(x, y, z, shape, 0);
 
-	if (valid)
+	if (info.valid)
 		return 1;
 	else
 		return 0;
@@ -1350,9 +1301,9 @@ uint32 CurrentMap::I_canExistAtPoint(const uint8 *args, unsigned int /*argsize*/
 	World_FromUsecodeXY(x, y);
 
 	const CurrentMap *cm = World::get_instance()->getCurrentMap();
-	bool valid = cm->isValidPosition(x, y, z, shape, 0, 0, 0);
+	PositionInfo info = cm->getPositionInfo(x, y, z, shape, 0);
 
-	if (valid)
+	if (info.valid)
 		return 1;
 	else
 		return 0;
diff --git a/engines/ultima/ultima8/world/current_map.h b/engines/ultima/ultima8/world/current_map.h
index a202bafe703..b0e5bea0baa 100644
--- a/engines/ultima/ultima8/world/current_map.h
+++ b/engines/ultima/ultima8/world/current_map.h
@@ -24,11 +24,13 @@
 
 #include "ultima/shared/std/containers.h"
 #include "ultima/ultima8/usecode/intrinsics.h"
+#include "ultima/ultima8/world/position_info.h"
 #include "ultima/ultima8/misc/direction.h"
 
 namespace Ultima {
 namespace Ultima8 {
 
+struct Box;
 class Map;
 class Item;
 class UCList;
@@ -105,35 +107,13 @@ public:
 	                   int32 origin[3], int32 dims[2],
 	                   bool above, bool below, bool recurse = false) const;
 
-	// Collision detection. Returns true if the box [x,y,z]-[x-xd,y-yd,z+zd]
-	// does not collide with any solid items.
-	// Additionally:
-	// * If support is not NULL, *support is set to the item supporting
-	//   the given box, or 0 if it isn't supported.
-	// * If roof is not NULL, *roof is set to the roof item with the lowest
-	//   z coordinate that's over the box, or 0 if there is no roof above box.
-	// * If blocker is not NULL, *blocker will be set to an item blocking
-	//   the whole box if there is one, or 0 if there is no such item.
-	// Ignores collisions which were already occurring at the start position.
-	// NB: isValidPosition doesn't consider item 'item'.
-	bool isValidPosition(int32 x, int32 y, int32 z,
-	                     int32 startx, int32 starty, int32 startz,
-	                     int xd, int yd, int zd, uint32 shapeflags,
-	                     ObjId item, const Item **support = 0,
-	                     ObjId *roof = 0, const Item **blocker = 0) const;
-
-	// Note that this version of isValidPosition does not look for start
-	// position collisions.
-	bool isValidPosition(int32 x, int32 y, int32 z,
-	                     int xd, int yd, int zd, uint32 shapeflags,
-	                     ObjId item, const Item **support = 0,
-						 ObjId *roof = 0, const Item **blocker = 0) const;
-
-	// Note that this version of isValidPosition can not take 'flipped'
-	// into account!
-	bool isValidPosition(int32 x, int32 y, int32 z, uint32 shape,
-	                     ObjId item, const Item **support = 0,
-						 ObjId *roof = 0, const Item **blocker = 0) const;
+	// Collision detection. Returns position information with valid being true
+	// when the target box does not collide with any solid items.
+	// Ignores collisions when overlapping with the start box.
+	PositionInfo getPositionInfo(const Box &target, const Box &start, uint32 shapeflags, ObjId id) const;
+
+	// Note that this version of getPositionInfo can not take 'flipped' into account!
+	PositionInfo getPositionInfo(int32 x, int32 y, int32 z, uint32 shape, ObjId id) const;
 
 	//! Scan for a valid position for item in directions orthogonal to movedir
 	bool scanForValidPosition(int32 x, int32 y, int32 z, const Item *item,
diff --git a/engines/ultima/ultima8/world/item.cpp b/engines/ultima/ultima8/world/item.cpp
index cbfa3836602..5279b0a7a5a 100644
--- a/engines/ultima/ultima8/world/item.cpp
+++ b/engines/ultima/ultima8/world/item.cpp
@@ -653,10 +653,13 @@ bool Item::isPartlyOnScreen() const {
 
 bool Item::canExistAt(int32 x, int32 y, int32 z, bool needsupport) const {
 	CurrentMap *cm = World::get_instance()->getCurrentMap();
-	const Item *support;
-	bool valid = cm->isValidPosition(x, y, z, getShape(), getObjId(),
-	                                 &support, 0);
-	return valid && (!needsupport || support);
+	int32 xd, yd, zd;
+	getFootpadWorld(xd, yd, zd);
+	Box target(x, y, z, xd, yd, zd);
+	Box empty;
+
+	PositionInfo info = cm->getPositionInfo(target, empty, 0, getObjId());
+	return info.valid && (!needsupport || info.supported);
 }
 
 Direction Item::getDirToItemCentre(const Item &item2) const {
@@ -1210,13 +1213,12 @@ uint16 Item::fireWeapon(int32 x, int32 y, int32 z, Direction dir, int firetype,
 	Common::RandomSource &rs = Ultima8Engine::get_instance()->getRandomSource();
 	int damage = firetypedat->getRandomDamage();
 
-	const Item *blocker = nullptr;
 	// CHECKME: the original doesn't exclude the source like this,
 	// but it seems obvious we have to or NPCs shoot themselves?
-	bool isvalid = currentmap->isValidPosition(ix, iy, iz, BULLET_SPLASH_SHAPE, _objId, nullptr, nullptr, &blocker);
+	PositionInfo info = currentmap->getPositionInfo(ix, iy, iz, BULLET_SPLASH_SHAPE, _objId);
 
-	if (!isvalid && blocker) {
-		Item *block = getItem(blocker->getObjId());
+	if (!info.valid && info.blocker) {
+		Item *block = getItem(info.blocker->getObjId());
 		Point3 blockpt;
 		block->getLocation(blockpt);
 		Direction damagedir = Direction_GetWorldDir(blockpt.y - iy, blockpt.x - ix, dirmode_8dirs);
@@ -1433,11 +1435,9 @@ uint16 Item::fireDistance(const Item *other, Direction dir, int16 xoff, int16 yo
 		int32 cx = x + (i == 0 ? xoff : xoff2);
 		int32 cy = y + (i == 0 ? yoff : yoff2);
 		int32 cz = z + (i == 0 ? zoff : zoff2);
-		const Item *blocker = nullptr;
-		bool valid = cm->isValidPosition(cx, cy, cz, BULLET_SPLASH_SHAPE,
-										 getObjId(), nullptr, nullptr, &blocker);
-		if (!valid) {
-			if (blocker->getObjId() == other->getObjId())
+		PositionInfo info = cm->getPositionInfo(cx, cy, cz, BULLET_SPLASH_SHAPE, getObjId());
+		if (!info.valid && info.blocker) {
+			if (info.blocker->getObjId() == other->getObjId())
 				dist = MAX(abs(_x - ox), abs(_y - oy));
 		} else {
 			int32 ocx, ocy, ocz;
@@ -3142,8 +3142,8 @@ uint32 Item::I_legalCreateAtPoint(const uint8 *args, unsigned int /*argsize*/) {
 
 	// check if item can exist
 	CurrentMap *cm = World::get_instance()->getCurrentMap();
-	bool valid = cm->isValidPosition(x, y, z, shape, 0, 0, 0);
-	if (!valid)
+	PositionInfo info = cm->getPositionInfo(x, y, z, shape, 0);
+	if (!info.valid)
 		return 0;
 
 	Item *newitem = ItemFactory::createItem(shape, frame, 0, 0, 0, 0, 0, true);
@@ -3174,8 +3174,8 @@ uint32 Item::I_legalCreateAtCoords(const uint8 *args, unsigned int /*argsize*/)
 
 	// check if item can exist
 	CurrentMap *cm = World::get_instance()->getCurrentMap();
-	bool valid = cm->isValidPosition(x, y, z, shape, 0, 0, 0);
-	if (!valid)
+	PositionInfo info = cm->getPositionInfo(x, y, z, shape, 0);
+	if (!info.valid)
 		return 0;
 
 	// if yes, create it
diff --git a/engines/ultima/ultima8/world/position_info.h b/engines/ultima/ultima8/world/position_info.h
new file mode 100644
index 00000000000..0097d3921af
--- /dev/null
+++ b/engines/ultima/ultima8/world/position_info.h
@@ -0,0 +1,57 @@
+/* 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef ULTIMA8_WORLD_POSITION_INFO_H
+#define ULTIMA8_WORLD_POSITION_INFO_H
+
+namespace Ultima {
+namespace Ultima8 {
+
+class Item;
+
+/**
+ * Position information for a desired target box for an item.
+ * The position is valid when the target box does not collide with any solid items.
+ *
+ * Floor is set to the solid item with the highest z coordinate under the target box,
+ * or null if there is no floor below box.
+ * 
+ * Roof is set to the roof item with the lowest z coordinate over the target box,
+ * or null if there is no roof above box.
+ *
+ * Blocker is set to an item blocking the target box, or null if there is no such item.
+ *
+ * Supported is true if the floor is appropriate to support the item when at the target.
+ */
+struct PositionInfo {
+	bool valid;
+	bool supported;
+	const Item *floor;
+	const Item *roof;
+	const Item *blocker;
+
+	PositionInfo() : valid(false), supported(false), floor(nullptr), roof(nullptr), blocker(nullptr) {}
+};
+
+} // End of namespace Ultima8
+} // End of namespace Ultima
+
+#endif




More information about the Scummvm-git-logs mailing list