diff --git a/.github/workflows/build-docker.yml b/.github/workflows/build-docker.yml
index b91216e6..7dd7b657 100644
--- a/.github/workflows/build-docker.yml
+++ b/.github/workflows/build-docker.yml
@@ -43,12 +43,12 @@ jobs:
uses: gittools/actions/gitversion/execute@v0.9.15
- name: Set up Docker Buildx
- uses: docker/setup-buildx-action@v3.7.1
+ uses: docker/setup-buildx-action@v2
with:
install: true
- name: Login to GitHub Container Registry
- uses: docker/login-action@v3.3.0
+ uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
@@ -73,8 +73,6 @@ jobs:
tags: ghcr.io/${{ github.repository }}:${{ steps.gitversion.outputs.semVer }}
cache-from: type=gha, scope=${{ github.workflow }}
cache-to: type=gha, scope=${{ github.workflow }}
- secrets: |
- DEBUG=1
- name: Image digest
if: ${{ github.event_name == 'push' }}
@@ -99,7 +97,7 @@ jobs:
uses: gittools/actions/gitversion/execute@v0.9.15
- name: Set up Docker Buildx
- uses: docker/setup-buildx-action@v3.7.1
+ uses: docker/setup-buildx-action@v2
with:
install: true
diff --git a/config.lua.dist b/config.lua.dist
index 9a02d117..8cb6bb79 100644
--- a/config.lua.dist
+++ b/config.lua.dist
@@ -21,9 +21,6 @@ toggleMaintainMode = false
-- @field [parent=#global] #string maintainModeMessage an empty string by default, set a custom message if needed.
maintainModeMessage = ""
--- toggleTestMode get player infinite health mana and soul.
-toggleTestMode = false
-
-- Rook system
-- NOTE: Town ids: Dawnport = 2, Rookgaard = 3
toggleRookSystem = false
diff --git a/data-global/monster/bosses/chagorz.lua b/data-global/monster/bosses/chagorz.lua
index bec5b74e..27e8c158 100644
--- a/data-global/monster/bosses/chagorz.lua
+++ b/data-global/monster/bosses/chagorz.lua
@@ -4,7 +4,7 @@ local monster = {}
monster.description = "Chagorz"
monster.experience = 3250000
monster.outfit = {
- lookType = 1665,
+ lookType = 1666,
lookHead = 0,
lookBody = 0,
lookLegs = 0,
diff --git a/data-global/monster/bosses/vemiath.lua b/data-global/monster/bosses/vemiath.lua
index 97be142a..ba63097f 100644
--- a/data-global/monster/bosses/vemiath.lua
+++ b/data-global/monster/bosses/vemiath.lua
@@ -4,7 +4,7 @@ local monster = {}
monster.description = "Vemiath"
monster.experience = 3250000
monster.outfit = {
- lookType = 1665,
+ lookType = 1668,
lookHead = 0,
lookBody = 0,
lookLegs = 0,
diff --git a/data-global/monster/familiars/druid_familiar.lua b/data-global/monster/familiars/druid_familiar.lua
index 4e3861b5..2c4f3a55 100644
--- a/data-global/monster/familiars/druid_familiar.lua
+++ b/data-global/monster/familiars/druid_familiar.lua
@@ -1,4 +1,4 @@
-local mType = Game.createMonsterType("Druid Familiar")
+local mType = Game.createMonsterType("Druid familiar")
local monster = {}
monster.description = "a druid familiar"
diff --git a/data-global/monster/familiars/knight_familiar.lua b/data-global/monster/familiars/knight_familiar.lua
index ce507c0a..83c61969 100644
--- a/data-global/monster/familiars/knight_familiar.lua
+++ b/data-global/monster/familiars/knight_familiar.lua
@@ -1,4 +1,4 @@
-local mType = Game.createMonsterType("Knight Familiar")
+local mType = Game.createMonsterType("Knight familiar")
local monster = {}
monster.description = "a knight familiar"
diff --git a/data-global/monster/familiars/paladin_familiar.lua b/data-global/monster/familiars/paladin_familiar.lua
index 42b4736a..181e96f3 100644
--- a/data-global/monster/familiars/paladin_familiar.lua
+++ b/data-global/monster/familiars/paladin_familiar.lua
@@ -1,4 +1,4 @@
-local mType = Game.createMonsterType("Paladin Familiar")
+local mType = Game.createMonsterType("Paladin familiar")
local monster = {}
monster.description = "a paladin familiar"
diff --git a/data-global/monster/quests/rotten_bood/mushroom.lua b/data-global/monster/quests/rotten_bood/mushroom.lua
index d5611b8e..26945aba 100644
--- a/data-global/monster/quests/rotten_bood/mushroom.lua
+++ b/data-global/monster/quests/rotten_bood/mushroom.lua
@@ -4,7 +4,7 @@ local monster = {}
monster.description = "a Mushroom"
monster.experience = 0
monster.outfit = {
- lookType = 1669, --todo get correct lookType
+ lookType = 1773,
lookHead = 0,
lookBody = 0,
lookLegs = 0,
diff --git a/data/XML/groups.xml b/data/XML/groups.xml
index e5c697ce..2c3fee37 100644
--- a/data/XML/groups.xml
+++ b/data/XML/groups.xml
@@ -147,4 +147,9 @@
+
+
+
+
+
diff --git a/data/libs/functions/boss_lever.lua b/data/libs/functions/boss_lever.lua
index 33b1b3e7..bc3b0aba 100644
--- a/data/libs/functions/boss_lever.lua
+++ b/data/libs/functions/boss_lever.lua
@@ -178,7 +178,8 @@ function BossLever:onUse(player)
end
local checkAccountType = creature:getAccountType() < ACCOUNT_TYPE_GAMEMASTER
- if checkAccountType and creature:getLevel() < self.requiredLevel then
+ local isGameTester = player:hasFlag(PlayerFlag_IsGameTester)
+ if checkAccountType and not isGameTester and creature:getLevel() < self.requiredLevel then
local message = "All players need to be level " .. self.requiredLevel .. " or higher."
creature:sendTextMessage(MESSAGE_EVENT_ADVANCE, message)
player:sendTextMessage(MESSAGE_EVENT_ADVANCE, message)
@@ -186,7 +187,7 @@ function BossLever:onUse(player)
end
local infoPositions = lever:getInfoPositions()
- if creature:getGroup():getId() < GROUP_TYPE_GOD and checkAccountType and self:lastEncounterTime(creature) > os.time() then
+ if creature:getGroup():getId() < GROUP_TYPE_GOD and checkAccountType and not isGameTester and self:lastEncounterTime(creature) > os.time() then
for _, posInfo in pairs(infoPositions) do
local currentPlayer = posInfo.creature
if currentPlayer then
diff --git a/data/scripts/actions/objects/cask_and_kegs.lua b/data/scripts/actions/objects/cask_and_kegs.lua
index 1180257e..6eeb4b6f 100644
--- a/data/scripts/actions/objects/cask_and_kegs.lua
+++ b/data/scripts/actions/objects/cask_and_kegs.lua
@@ -26,7 +26,7 @@ local targetIdList = {
local flasks = Action()
function flasks.onUse(player, item, fromPosition, target, toPosition, isHotkey)
- if not target or not target:getItem() then
+ if not target or not target:isItem() then
return false
end
diff --git a/data/scripts/talkactions/god/flags.lua b/data/scripts/talkactions/god/flags.lua
index 3364fd01..58246043 100644
--- a/data/scripts/talkactions/god/flags.lua
+++ b/data/scripts/talkactions/god/flags.lua
@@ -39,6 +39,7 @@ local PlayerFlags_t = {
["IsAlwaysPremium"] = IsAlwaysPremium,
["CanMapClickTeleport"] = CanMapClickTeleport,
["IgnoredByNpcs"] = IgnoredByNpcs,
+ ["IsGameTester"] = IsGameTester,
}
local function sendValidKeys(player)
diff --git a/data/scripts/talkactions/god/manage_storage.lua b/data/scripts/talkactions/god/manage_storage.lua
index e1973ebd..ec723386 100644
--- a/data/scripts/talkactions/god/manage_storage.lua
+++ b/data/scripts/talkactions/god/manage_storage.lua
@@ -7,29 +7,30 @@ function Player.getStorageValueTalkaction(self, param)
end
local split = param:split(",")
- if split[2] == nil then
- player:sendCancelMessage("Insufficient parameters.")
+ if not split[2] then
+ self:sendCancelMessage("Insufficient parameters.")
return true
end
- local target = Player(split[1])
- if target == nil then
+ local target = Player(split[1]:trim())
+ if not target then
self:sendCancelMessage("A player with that name is not online.")
return true
end
- split[2] = split[2]:trimSpace()
+ -- Storage key Validation
+ local storageKey = tonumber(split[2]) or split[2]:trim()
+ if not storageKey then
+ self:sendCancelMessage("Invalid storage key or name.")
+ return true
+ end
- -- Try to convert the second parameter to a number. If it's not a number, treat it as a storage name
- local storageKey = tonumber(split[2])
- if storageKey == nil then
- -- Get the key for this storage name
- local storageName = tostring(split[2])
- local storageValue = target:getStorageValueByName(storageName)
- self:sendTextMessage(MESSAGE_EVENT_ADVANCE, "The storage with id: " .. storageName .. " from player " .. split[1] .. " is: " .. storageValue .. ".")
+ -- Get the storage key
+ local storageValue = target:getStorageValue(storageKey)
+ if storageValue == nil then
+ self:sendTextMessage(MESSAGE_EVENT_ADVANCE, "The storage with id: " .. split[2] .. " does not exist or is not set for player " .. target:getName() .. ".")
else
- local storageValue = target:getStorageValue(storageKey)
- self:sendTextMessage(MESSAGE_EVENT_ADVANCE, "The storage with id: " .. storageKey .. " from player " .. split[1] .. " is: " .. storageValue .. ".")
+ self:sendTextMessage(MESSAGE_EVENT_ADVANCE, "The storage with id: " .. split[2] .. " from player " .. target:getName() .. " is: " .. storageValue .. ".")
end
return true
@@ -44,7 +45,6 @@ end
storageGet:separator(" ")
storageGet:groupType("gamemaster")
storageGet:register()
-
---------------- // ----------------
function Player.setStorageValueTalkaction(self, param)
-- Sanity check for parameters
diff --git a/markdowns/CHANGELOG.md b/markdowns/CHANGELOG.md
index 246d2349..8802e2f1 100644
--- a/markdowns/CHANGELOG.md
+++ b/markdowns/CHANGELOG.md
@@ -13,7 +13,7 @@
- Moved emote spells to `kv` instead of `storage`. ([Tryller](https://github.com/jprzimba))
- Updated npcs and spells from 13.40 updates. ([murilo09](https://github.com/murilo09))
- Added a Rook system with configurations in `config.lua`. ([Tryller](https://github.com/jprzimba))
-- Added a Test Mode with configurations in `config.lua`. ([Tryller](https://github.com/jprzimba))
+- Added a new group `game tester` with flag `isgametester` in `groups.xml` and a new player flag `PlayerFlag_IsGameTester`. ([Tryller](https://github.com/jprzimba))
## Added files
@@ -29,6 +29,7 @@
- config.lua
- data/items/assets.dat
- data/items/items.xml
+- data/libs/functions/boss_lever.lua
- data/libs/systems/features.lua
- data/scripts/creaturescripts/player/login.lua
- data/scripts/movements/special_tiles.lua
@@ -39,12 +40,14 @@
- data/scripts/talkactions/god/create_item.lua
- data/scripts/talkactions/god/create_summon.lua
- data/scripts/talkactions/god/create_npc.lua
+- data/scripts/talkactions/god/flags.lua
- data/scripts/talkactions/player/chain_system.lua
- data/scripts/talkactions/player/emote_spell.lua
- data/scripts/spells/attack/annihilation.lua
- data/scripts/spells/attack/ultimate_ice_strike.lua
- data/scripts/spells/attack/ultimate_terra_strike.lua
- data/XML/imbuements.xml
+- data/XML/groups.xml
- data-global/npc (all npc files)
- data-global/npc/the_oracle.lua (modified to rook system)
- data-global/world/world.otbm (7z file)
diff --git a/src/config/config_enums.hpp b/src/config/config_enums.hpp
index b36b37de..f85860c5 100644
--- a/src/config/config_enums.hpp
+++ b/src/config/config_enums.hpp
@@ -356,7 +356,6 @@ enum ConfigKey_t : uint16_t {
EXPERT_PVP_CANWALKTHROUGHMAGICWALLS,
SPELL_NAME_INSTEAD_WORDS,
LOG_PLAYERS_STATEMENTS,
- TOGGLE_TEST_MODE,
ROOK_SYSTEM,
ROOK_TOWN,
LEVEL_TO_ROOK,
diff --git a/src/config/configmanager.cpp b/src/config/configmanager.cpp
index 14626a59..d2bfac19 100644
--- a/src/config/configmanager.cpp
+++ b/src/config/configmanager.cpp
@@ -177,7 +177,6 @@ bool ConfigManager::load() {
loadBoolConfig(L, EXPERT_PVP_CANWALKTHROUGHMAGICWALLS, "canWalkThroughMagicWalls", false);
loadBoolConfig(L, SPELL_NAME_INSTEAD_WORDS, "spellNameInsteadOfWords", false);
loadBoolConfig(L, LOG_PLAYERS_STATEMENTS, "logPlayersStatements", false);
- loadBoolConfig(L, TOGGLE_TEST_MODE, "toggleTestMode", false);
loadBoolConfig(L, ROOK_SYSTEM, "toggleRookSystem", false);
loadBoolConfig(L, TOGGLE_ADD_ROOK_ITEMS, "toggleAddRookItems", false);
diff --git a/src/creatures/combat/combat.cpp b/src/creatures/combat/combat.cpp
index 3d64a26b..3d50fc64 100644
--- a/src/creatures/combat/combat.cpp
+++ b/src/creatures/combat/combat.cpp
@@ -129,7 +129,7 @@ void Combat::getCombatArea(const Position ¢erPos, const Position &targetPos,
}
if (area) {
- area->getList(centerPos, targetPos, list);
+ area->getList(centerPos, targetPos, list, getDirectionTo(targetPos, centerPos));
} else {
list.emplace_back(g_game().map.getOrCreateTile(targetPos));
}
@@ -261,18 +261,14 @@ ReturnValue Combat::canTargetCreature(const std::shared_ptr &player, con
ReturnValue Combat::canDoCombat(const std::shared_ptr &caster, const std::shared_ptr &tile, bool aggressive) {
if (tile->hasProperty(CONST_PROP_BLOCKPROJECTILE)) {
- return RETURNVALUE_NOTENOUGHROOM;
+ return RETURNVALUE_CANNOTTHROW;
}
if (aggressive && tile->hasFlag(TILESTATE_PROTECTIONZONE)) {
return RETURNVALUE_ACTIONNOTPERMITTEDINPROTECTIONZONE;
}
- if (tile->hasFlag(TILESTATE_FLOORCHANGE)) {
- return RETURNVALUE_NOTENOUGHROOM;
- }
-
if (tile->getTeleportItem()) {
- return RETURNVALUE_NOTENOUGHROOM;
+ return RETURNVALUE_CANNOTTHROW;
}
if (caster) {
@@ -1965,7 +1961,9 @@ AreaCombat::~AreaCombat() {
clear();
}
-void AreaCombat::getList(const Position ¢erPos, const Position &targetPos, std::vector> &list) const {
+void AreaCombat::getList(const Position ¢erPos, const Position &targetPos, std::vector> &list, const Direction dir) const {
+ auto casterPos = getNextPosition(dir, targetPos);
+
const std::unique_ptr &area = getArea(centerPos, targetPos);
if (!area) {
return;
@@ -1977,17 +1975,19 @@ void AreaCombat::getList(const Position ¢erPos, const Position &targetPos, s
const uint32_t rows = area->getRows();
const uint32_t cols = area->getCols();
- list.reserve(rows * cols);
+ list.reserve(rows * cols);
Position tmpPos(targetPos.x - centerX, targetPos.y - centerY, targetPos.z);
- for (uint32_t y = 0; y < rows; ++y, ++tmpPos.y, tmpPos.x -= cols) {
- for (uint32_t x = 0; x < cols; ++x, ++tmpPos.x) {
- if (area->getValue(y, x) != 0) {
- if (g_game().isSightClear(targetPos, tmpPos, true)) {
- list.emplace_back(g_game().map.getOrCreateTile(tmpPos));
- }
+
+ for (uint32_t y = 0; y < rows; ++y) {
+ for (uint32_t x = 0; x < cols; ++x) {
+ if (area->getValue(y, x) != 0 && g_game().isSightClear(casterPos, tmpPos, true)) {
+ list.emplace_back(g_game().map.getOrCreateTile(tmpPos));
}
+ ++tmpPos.x;
}
+ ++tmpPos.y;
+ tmpPos.x -= cols;
}
}
diff --git a/src/creatures/combat/combat.hpp b/src/creatures/combat/combat.hpp
index 2a7f42a7..ae9e4260 100644
--- a/src/creatures/combat/combat.hpp
+++ b/src/creatures/combat/combat.hpp
@@ -164,7 +164,7 @@ class AreaCombat {
// non-assignable
AreaCombat &operator=(const AreaCombat &) = delete;
- void getList(const Position ¢erPos, const Position &targetPos, std::vector> &list) const;
+ void getList(const Position ¢erPos, const Position &targetPos, std::vector> &list, const Direction dir) const;
void setupArea(const std::list &list, uint32_t rows);
void setupArea(int32_t length, int32_t spread);
diff --git a/src/creatures/combat/spells.cpp b/src/creatures/combat/spells.cpp
index 2bb4889c..642a2e46 100644
--- a/src/creatures/combat/spells.cpp
+++ b/src/creatures/combat/spells.cpp
@@ -562,13 +562,6 @@ bool Spell::playerInstantSpellCheck(const std::shared_ptr &player, const
const auto &tile = g_game().map.getOrCreateTile(toPos);
- ReturnValue ret = Combat::canDoCombat(player, tile, aggressive);
- if (ret != RETURNVALUE_NOERROR) {
- player->sendCancelMessage(ret);
- g_game().addMagicEffect(player->getPosition(), CONST_ME_POFF);
- return false;
- }
-
if (blockingCreature && tile->getBottomVisibleCreature(player) != nullptr) {
player->sendCancelMessage(RETURNVALUE_NOTENOUGHROOM);
g_game().addMagicEffect(player->getPosition(), CONST_ME_POFF);
diff --git a/src/creatures/monsters/monster.cpp b/src/creatures/monsters/monster.cpp
index 6b3ea8c5..ea08577e 100644
--- a/src/creatures/monsters/monster.cpp
+++ b/src/creatures/monsters/monster.cpp
@@ -612,7 +612,11 @@ bool Monster::removeTarget(const std::shared_ptr &creature) {
totalPlayersOnScreen--;
}
- targetList.erase(it);
+ if (auto shared = it->lock()) {
+ targetList.erase(it);
+ } else {
+ return false;
+ }
return true;
}
@@ -1529,6 +1533,11 @@ void Monster::doRandomStep(Direction &nextDirection, bool &result) {
}
void Monster::doWalkBack(uint32_t &flags, Direction &nextDirection, bool &result) {
+ if (totalPlayersOnScreen > 0) {
+ isWalkingBack = false;
+ return;
+ }
+
result = Creature::getNextStep(nextDirection, flags);
if (result) {
flags |= FLAG_PATHFINDING;
diff --git a/src/creatures/npcs/npc.cpp b/src/creatures/npcs/npc.cpp
index 2d848ada..f9060b00 100644
--- a/src/creatures/npcs/npc.cpp
+++ b/src/creatures/npcs/npc.cpp
@@ -828,13 +828,13 @@ void Npc::removeShopPlayer(uint32_t playerGUID) {
}
void Npc::closeAllShopWindows() {
- for (const auto &playerGUID : shopPlayers | std::views::keys) {
- const auto &player = g_game().getPlayerByGUID(playerGUID);
+ for (auto it = shopPlayers.begin(); it != shopPlayers.end();) {
+ const auto &player = g_game().getPlayerByGUID(it->first);
if (player) {
player->closeShopWindow();
}
+ it = shopPlayers.erase(it);
}
- shopPlayers.clear();
}
void Npc::handlePlayerMove(const std::shared_ptr &player, const Position &newPos) {
diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp
index 37e14c97..3e0b04db 100644
--- a/src/creatures/players/player.cpp
+++ b/src/creatures/players/player.cpp
@@ -166,6 +166,8 @@ std::string Player::getDescription(int32_t lookDistance) {
if (group->access) {
s << " You are " << group->name << '.';
+ } else if (hasFlag(PlayerFlags_t::IsGameTester)) {
+ s << " You are a " << group->name << '.';
} else if (vocation->getId() != VOCATION_NONE) {
s << " You are " << vocation->getVocDescription() << '.';
} else {
@@ -189,6 +191,8 @@ std::string Player::getDescription(int32_t lookDistance) {
if (group->access) {
s << " " << getSubjectVerb() << " " << group->name << '.';
+ } else if (hasFlag(PlayerFlags_t::IsGameTester)) {
+ s << " " << getSubjectVerb() << " a " << group->name << '.';
} else if (vocation->getId() != VOCATION_NONE) {
s << " " << getSubjectVerb() << " " << vocation->getVocDescription() << '.';
} else {
@@ -6184,7 +6188,7 @@ bool Player::lastHitIsPlayer(const std::shared_ptr &lastHitCreature) {
}
void Player::changeHealth(int32_t healthChange, bool sendHealthChange /* = true*/) {
- if (g_configManager().getBoolean(TOGGLE_TEST_MODE)) {
+ if (hasFlag(PlayerFlags_t::IsGameTester)) {
return;
}
@@ -6193,7 +6197,7 @@ void Player::changeHealth(int32_t healthChange, bool sendHealthChange /* = true*
}
void Player::changeMana(int32_t manaChange) {
- if (g_configManager().getBoolean(TOGGLE_TEST_MODE)) {
+ if (hasFlag(PlayerFlags_t::IsGameTester)) {
return;
}
@@ -6205,7 +6209,7 @@ void Player::changeMana(int32_t manaChange) {
}
void Player::changeSoul(int32_t soulChange) {
- if (g_configManager().getBoolean(TOGGLE_TEST_MODE)) {
+ if (hasFlag(PlayerFlags_t::IsGameTester)) {
return;
}
@@ -7325,9 +7329,16 @@ uint8_t Player::getRandomMountId() const {
}
}
- const auto playerMountsSize = static_cast(playerMounts.size() - 1);
- const auto randomIndex = uniform_random(0, std::max(0, playerMountsSize));
- return playerMounts.at(randomIndex);
+ if (playerMounts.empty()) {
+ return 0;
+ }
+
+ const auto randomIndex = uniform_random(0, static_cast(playerMounts.size() - 1));
+ if (randomIndex >= 0 && static_cast(randomIndex) < playerMounts.size()) {
+ return playerMounts[randomIndex];
+ }
+
+ return 0;
}
bool Player::toggleMount(bool mount) {
diff --git a/src/game/game.cpp b/src/game/game.cpp
index 30936e20..cd281732 100644
--- a/src/game/game.cpp
+++ b/src/game/game.cpp
@@ -9130,12 +9130,13 @@ void Game::playerCreateMarketOffer(uint32_t playerId, uint8_t type, uint16_t ite
}
uint64_t totalPrice = price * amount;
- uint64_t totalFee = totalPrice * 0.02;
- uint64_t maxFee = std::min(1000000, totalFee);
- uint64_t fee = std::max(20, totalFee);
+ uint64_t totalFee = totalPrice * 0.02; // 2% fee
+ uint64_t maxFee = std::min(1000000, totalFee); // Max fee is 1kk
+ uint64_t fee = std::clamp(totalFee, uint64_t(20), maxFee); // Limit between 20 and maxFee
if (type == MARKETACTION_SELL) {
- if (fee > (player->getBankBalance() + player->getMoney())) {
+ uint64_t totalPriceWithFee = totalPrice + fee;
+ if (totalPriceWithFee > (player->getMoney() + player->getBankBalance())) {
offerStatus << "Fee is greater than player money";
return;
}
diff --git a/src/items/tile.cpp b/src/items/tile.cpp
index 6c980345..7be7a3ed 100644
--- a/src/items/tile.cpp
+++ b/src/items/tile.cpp
@@ -1888,11 +1888,25 @@ std::shared_ptr- Tile::getUseItem(int32_t index) const {
return ground;
}
- if (const auto &thing = getThing(index)) {
- return thing->getItem();
+ if (index >= 0 && index < static_cast(items->size())) {
+ if (const auto &thing = getThing(index)) {
+ if (auto thingItem = thing->getItem()) {
+ return thingItem;
+ }
+ }
}
- return nullptr;
+ if (auto topDownItem = getTopDownItem()) {
+ return topDownItem;
+ }
+
+ for (auto it = items->rbegin(), end = items->rend(); it != end; ++it) {
+ if ((*it)->getDoor()) {
+ return (*it)->getItem();
+ }
+ }
+
+ return !items->empty() ? *items->begin() : nullptr;
}
std::shared_ptr
- Tile::getDoorItem() const {
diff --git a/src/map/spectators.cpp b/src/map/spectators.cpp
index e286023b..097272a4 100644
--- a/src/map/spectators.cpp
+++ b/src/map/spectators.cpp
@@ -67,14 +67,14 @@ bool Spectators::checkCache(const SpectatorsCache::FloorData &specData, bool onl
for (const auto &creature : *list) {
const auto &specPos = creature->getPosition();
if ((centerPos.x - specPos.x >= minRangeX
- && centerPos.y - specPos.y >= minRangeY
- && centerPos.x - specPos.x <= maxRangeX
- && centerPos.y - specPos.y <= maxRangeY
- && (multifloor || specPos.z == centerPos.z)
- && ((onlyPlayers && creature->getPlayer())
- || (onlyMonsters && creature->getMonster())
- || (onlyNpcs && creature->getNpc()))
- || (!onlyPlayers && !onlyMonsters && !onlyNpcs))) {
+ && centerPos.y - specPos.y >= minRangeY
+ && centerPos.x - specPos.x <= maxRangeX
+ && centerPos.y - specPos.y <= maxRangeY
+ && (multifloor || specPos.z == centerPos.z)
+ && ((onlyPlayers && creature->getPlayer())
+ || (onlyMonsters && creature->getMonster())
+ || (onlyNpcs && creature->getNpc())))
+ || (!onlyPlayers && !onlyMonsters && !onlyNpcs)) {
spectators.emplace_back(creature);
}
}
@@ -265,7 +265,7 @@ Spectators Spectators::excludePlayerMaster() const {
specs.creatures.reserve(creatures.size());
for (const auto &c : creatures) {
- if ((c->getMonster() != nullptr && !c->getMaster() || !c->getMaster()->getPlayer())) {
+ if ((c->getMonster() != nullptr && !c->getMaster()) || (!c->getMaster() || !c->getMaster()->getPlayer())) {
specs.insert(c);
}
}
diff --git a/src/utils/utils_definitions.hpp b/src/utils/utils_definitions.hpp
index 5518a484..f5e90d8a 100644
--- a/src/utils/utils_definitions.hpp
+++ b/src/utils/utils_definitions.hpp
@@ -699,6 +699,7 @@ enum class PlayerFlags_t : uint8_t {
IsAlwaysPremium,
CanMapClickTeleport,
IgnoredByNpcs,
+ IsGameTester,
// Must always be the last
FlagLast