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