diff --git a/Source/inv.cpp b/Source/inv.cpp index d6a05e951..83a3b8d2c 100644 --- a/Source/inv.cpp +++ b/Source/inv.cpp @@ -1769,29 +1769,30 @@ void SyncGetItem(Point position, int32_t iseed, _item_indexes idx, uint16_t ci) bool CanPut(Point position) { - if (dItem[position.x][position.y] != 0) + if (!InDungeonBounds(position)) { return false; - if (nSolidTable[dPiece[position.x][position.y]]) + } + + if (IsTileSolid(position)) { return false; - if (dObject[position.x][position.y] != 0 && Objects[abs(dObject[position.x][position.y]) - 1]._oSolidFlag) + } + + if (dItem[position.x][position.y] != 0) { return false; + } - if (dObject[position.x + 1][position.y + 1] != 0) { - int8_t oi = abs(dObject[position.x + 1][position.y + 1]) - 1; - if (Objects[oi]._oSelFlag != 0) { + if (currlevel == 0) { + if (dMonster[position.x][position.y] != 0) { return false; } - } - - if (dObject[position.x + 1][position.y] > 0 && dObject[position.x][position.y + 1] > 0) { - if (Objects[dObject[position.x + 1][position.y] - 1]._oSelFlag != 0 && Objects[dObject[position.x][position.y + 1] - 1]._oSelFlag != 0) + if (dMonster[position.x + 1][position.y + 1] != 0) { return false; + } } - if (currlevel == 0 && dMonster[position.x][position.y] != 0) - return false; - if (currlevel == 0 && dMonster[position.x + 1][position.y + 1] != 0) + if (IsItemBlockingObjectAtPosition(position)) { return false; + } return true; } diff --git a/Source/items.cpp b/Source/items.cpp index ad1a5e736..f27b3a722 100644 --- a/Source/items.cpp +++ b/Source/items.cpp @@ -396,11 +396,11 @@ bool ItemPlace(Point position) return false; if (dItem[position.x][position.y] != 0) return false; - if (dObject[position.x][position.y] != 0) + if (IsObjectAtPosition(position)) return false; if (TileContainsSetPiece(position)) return false; - if (nSolidTable[dPiece[position.x][position.y]]) + if (IsTileSolid(position)) return false; return true; @@ -3106,44 +3106,31 @@ void CreatePlrItems(int playerId) bool ItemSpaceOk(Point position) { - if (!InDungeonBounds(position)) + if (!InDungeonBounds(position)) { return false; + } - if (dMonster[position.x][position.y] != 0) + if (IsTileSolid(position)) { return false; + } - if (dPlayer[position.x][position.y] != 0) + if (dItem[position.x][position.y] != 0) { return false; + } - if (dItem[position.x][position.y] != 0) + if (dMonster[position.x][position.y] != 0) { return false; - - if (dObject[position.x][position.y] != 0) { - int oi = abs(dObject[position.x][position.y]) - 1; - if (Objects[oi]._oSolidFlag) - return false; } - Point south = position + Direction::South; - if (InDungeonBounds(south)) { - int objectId = dObject[south.x][south.y]; - if (objectId != 0 && Objects[abs(objectId) - 1]._oSelFlag != 0) - return false; + if (dPlayer[position.x][position.y] != 0) { + return false; } - Point southEast = position + Direction::SouthEast; - Point southWest = position + Direction::SouthWest; - if (InDungeonBounds(southEast) && InDungeonBounds(southWest)) { - int objectIdSE = dObject[southEast.x][southEast.y]; - int objectIdSW = dObject[southWest.x][southWest.y]; - if (objectIdSE > 0 && objectIdSW > 0) { - if (Objects[objectIdSE - 1]._oSelFlag != 0 && Objects[objectIdSW - 1]._oSelFlag != 0) { - return false; - } - } + if (IsItemBlockingObjectAtPosition(position)) { + return false; } - return IsTileNotSolid(position); + return true; } int AllocateItem() diff --git a/Source/objects.cpp b/Source/objects.cpp index 9acb08757..c07028c1b 100644 --- a/Source/objects.cpp +++ b/Source/objects.cpp @@ -4347,19 +4347,46 @@ bool Object::IsDisabled() const return IsAnyOf(static_cast(_oVar1), shrine_type::ShrineFascinating, shrine_type::ShrineOrnate, shrine_type::ShrineSacred); } -Object *ObjectAtPosition(Point position) +Object *ObjectAtPosition(Point position, bool considerLargeObjects) { - if (InDungeonBounds(position) && dObject[position.x][position.y] != 0) { - return &Objects[abs(dObject[position.x][position.y]) - 1]; + if (!InDungeonBounds(position)) { + return nullptr; + } + + auto objectId = dObject[position.x][position.y]; + + if (objectId > 0 || (considerLargeObjects && objectId != 0)) { + return &Objects[abs(objectId) - 1]; } // nothing at this position, return a nullptr return nullptr; } -bool IsObjectAtPosition(Point position) +bool IsItemBlockingObjectAtPosition(Point position) { - return ObjectAtPosition(position) != nullptr; + Object *object = ObjectAtPosition(position); + if (object != nullptr && object->_oSolidFlag) { + // solid object + return true; + } + + object = ObjectAtPosition(position + Direction::South); + if (object != nullptr && object->_oSelFlag != 0) { + // An unopened container or breakable object exists which potentially overlaps this tile, the player might not be able to pick up an item dropped here. + return true; + } + + object = ObjectAtPosition(position + Direction::SouthEast, false); + if (object != nullptr) { + Object *otherDoor = ObjectAtPosition(position + Direction::SouthWest, false); + if (otherDoor != nullptr && object->_oSelFlag != 0 && otherDoor->_oSelFlag != 0) { + // Two interactive objects potentially overlap both sides of this tile, as above the player might not be able to pick up an item which is dropped here. + return true; + } + } + + return false; } void InitObjectGFX() diff --git a/Source/objects.h b/Source/objects.h index 1c6192886..5b9e23b70 100644 --- a/Source/objects.h +++ b/Source/objects.h @@ -242,16 +242,28 @@ extern bool LoadingMapObjects; * @brief Find an object given a point in map coordinates * * @param position The map coordinate to test + * @param considerLargeObjects Default behaviour will return a pointer to a large object that covers this tile, set + * this param to false if you only want the object whose base position matches this tile * @return A pointer to the object or nullptr if no object exists at this location */ -Object *ObjectAtPosition(Point position); +Object *ObjectAtPosition(Point position, bool considerLargeObjects = true); /** * @brief Check whether an item occupies this tile position * @param position The map coordinate to test * @return true if the tile is occupied */ -bool IsObjectAtPosition(Point position); +inline bool IsObjectAtPosition(Point position) +{ + return ObjectAtPosition(position) != nullptr; +} + +/** + * @brief Check whether an item blocking object (solid object or open door) is located at this tile position + * @param position The map coordinate to test + * @return true if the tile is blocked + */ +bool IsItemBlockingObjectAtPosition(Point position); void InitObjectGFX(); void FreeObjectGFX();