From 77a783098df62652b713a86aaaf0490cbef4ec96 Mon Sep 17 00:00:00 2001 From: ephphatha Date: Fri, 29 Oct 2021 00:21:11 +1100 Subject: [PATCH] Use ObjectAtPosition when checking if an item can be dropped This extracts some of the common object logic from ItemSpaceOk and CanPut, these functions are almost identical except for the way players and monsters are checked... --- Source/inv.cpp | 27 ++++++++++++++------------- Source/items.cpp | 41 ++++++++++++++--------------------------- Source/objects.cpp | 37 ++++++++++++++++++++++++++++++++----- Source/objects.h | 16 ++++++++++++++-- 4 files changed, 74 insertions(+), 47 deletions(-) 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();