From 3e02cedeaae8a06c96df149a257af68da07aba49 Mon Sep 17 00:00:00 2001 From: Matt Gomez <martin_jojo@hotmail.com> Date: Thu, 11 Jul 2024 20:34:36 -0600 Subject: [PATCH] 11072014 --- .../creaturescripts/others/login_events.lua | 3 - data/libs/functions/revscriptsys.lua | 4 + .../creaturescripts/familiar/on_login.lua | 1 + data/scripts/creaturescripts/player/login.lua | 1 + src/creatures/combat/combat.cpp | 29 +-- src/creatures/combat/combat.hpp | 8 +- src/creatures/creature.cpp | 4 + .../monsters/spawns/spawn_monster.cpp | 3 +- .../monsters/spawns/spawn_monster.hpp | 27 ++- src/creatures/npcs/spawns/spawn_npc.cpp | 2 +- src/creatures/npcs/spawns/spawn_npc.hpp | 4 +- src/creatures/players/player.cpp | 20 ++- src/creatures/players/player.hpp | 10 +- src/game/game.cpp | 30 ++-- src/io/functions/iologindata_load_player.cpp | 4 +- src/io/iologindata.cpp | 25 +-- src/io/iologindata.hpp | 4 +- src/io/iomapserialize.cpp | 8 +- .../functions/core/game/global_functions.cpp | 6 +- .../creatures/monster/monster_functions.cpp | 3 +- .../events/global_event_functions.cpp | 2 + .../events/global_event_functions.hpp | 1 + src/lua/global/globalevent.cpp | 13 +- src/lua/global/globalevent.hpp | 2 + src/lua/lua_definitions.hpp | 1 + src/map/map.cpp | 169 ++++++++++++++---- src/map/map.hpp | 2 +- src/server/network/protocol/protocolgame.cpp | 2 +- src/server/signals.cpp | 2 + 29 files changed, 266 insertions(+), 124 deletions(-) diff --git a/data-otxserver/scripts/creaturescripts/others/login_events.lua b/data-otxserver/scripts/creaturescripts/others/login_events.lua index f88095e47..2223f095f 100644 --- a/data-otxserver/scripts/creaturescripts/others/login_events.lua +++ b/data-otxserver/scripts/creaturescripts/others/login_events.lua @@ -1,10 +1,7 @@ local loginEvents = CreatureEvent("LoginEvents") function loginEvents.onLogin(player) local events = { - "AdvanceSave", "RookgaardAdvance", - "FamiliarLogin", - "FamiliarAdvance", --Quests --Cults Of Tibia Quest "HealthPillar", diff --git a/data/libs/functions/revscriptsys.lua b/data/libs/functions/revscriptsys.lua index 84b8275e3..515522d64 100644 --- a/data/libs/functions/revscriptsys.lua +++ b/data/libs/functions/revscriptsys.lua @@ -233,6 +233,10 @@ do self:type("periodchange") self:onPeriodChange(value) return + elseif key == "onSave" then + self:type("save") + self:onSave(value) + return end rawset(self, key, value) end diff --git a/data/scripts/creaturescripts/familiar/on_login.lua b/data/scripts/creaturescripts/familiar/on_login.lua index e264040db..dd4a1928e 100644 --- a/data/scripts/creaturescripts/familiar/on_login.lua +++ b/data/scripts/creaturescripts/familiar/on_login.lua @@ -5,6 +5,7 @@ function familiarOnLogin.onLogin(player) return false end + player:registerEvent("FamiliarAdvance") local vocation = FAMILIAR_ID[player:getVocation():getBaseId()] local familiarName diff --git a/data/scripts/creaturescripts/player/login.lua b/data/scripts/creaturescripts/player/login.lua index fbe4f9e63..416760669 100644 --- a/data/scripts/creaturescripts/player/login.lua +++ b/data/scripts/creaturescripts/player/login.lua @@ -173,6 +173,7 @@ function playerLoginGlobal.onLogin(player) player:registerEvent("PlayerDeath") player:registerEvent("DropLoot") player:registerEvent("BossParticipation") + player:registerEvent("UpdatePlayerOnAdvancedLevel") return true end diff --git a/src/creatures/combat/combat.cpp b/src/creatures/combat/combat.cpp index 87f7500d0..91ff712de 100644 --- a/src/creatures/combat/combat.cpp +++ b/src/creatures/combat/combat.cpp @@ -108,7 +108,7 @@ CombatDamage Combat::getCombatDamage(std::shared_ptr<Creature> creature, std::sh return damage; } -void Combat::getCombatArea(const Position ¢erPos, const Position &targetPos, const std::unique_ptr<AreaCombat> &area, std::forward_list<std::shared_ptr<Tile>> &list) { +void Combat::getCombatArea(const Position ¢erPos, const Position &targetPos, const std::unique_ptr<AreaCombat> &area, std::vector<std::shared_ptr<Tile>> &list) { if (targetPos.z >= MAP_MAX_LAYERS) { return; } @@ -116,7 +116,7 @@ void Combat::getCombatArea(const Position ¢erPos, const Position &targetPos, if (area) { area->getList(centerPos, targetPos, list); } else { - list.push_front(g_game().map.getOrCreateTile(targetPos)); + list.emplace_back(g_game().map.getOrCreateTile(targetPos)); } } @@ -1119,7 +1119,7 @@ bool Combat::doCombat(std::shared_ptr<Creature> caster, const Position &position } void Combat::CombatFunc(std::shared_ptr<Creature> caster, const Position &origin, const Position &pos, const std::unique_ptr<AreaCombat> &area, const CombatParams ¶ms, CombatFunction func, CombatDamage* data) { - std::forward_list<std::shared_ptr<Tile>> tileList; + std::vector<std::shared_ptr<Tile>> tileList; if (caster) { getCombatArea(caster->getPosition(), pos, area, tileList); @@ -1827,26 +1827,29 @@ AreaCombat::AreaCombat(const AreaCombat &rhs) { } } -void AreaCombat::getList(const Position ¢erPos, const Position &targetPos, std::forward_list<std::shared_ptr<Tile>> &list) const { +void AreaCombat::getList(const Position ¢erPos, const Position &targetPos, std::vector<std::shared_ptr<Tile>> &list) const { const std::unique_ptr<MatrixArea> &area = getArea(centerPos, targetPos); if (!area) { return; } - uint32_t centerY, centerX; + uint32_t centerY; + uint32_t centerX; area->getCenter(centerY, centerX); + const uint32_t rows = area->getRows(); + const uint32_t cols = area->getCols(); + list.reserve(rows * cols); + Position tmpPos(targetPos.x - centerX, targetPos.y - centerY, targetPos.z); - uint32_t cols = area->getCols(); - for (uint32_t y = 0, rows = area->getRows(); y < rows; ++y) { - for (uint32_t x = 0; x < cols; ++x) { - if (area->getValue(y, x) != 0 && g_game().isSightClear(targetPos, tmpPos, true)) { - list.push_front(g_game().map.getOrCreateTile(tmpPos)); + 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)); + } } - tmpPos.x++; } - tmpPos.x -= cols; - tmpPos.y++; } } diff --git a/src/creatures/combat/combat.hpp b/src/creatures/combat/combat.hpp index 93b02502f..304a70913 100644 --- a/src/creatures/combat/combat.hpp +++ b/src/creatures/combat/combat.hpp @@ -84,7 +84,7 @@ class ChainPickerCallback final : public CallBack { }; struct CombatParams { - std::forward_list<std::shared_ptr<Condition>> conditionList; + std::vector<std::shared_ptr<Condition>> conditionList; std::unique_ptr<ValueCallback> valueCallback; std::unique_ptr<TileCallback> tileCallback; @@ -218,7 +218,7 @@ class AreaCombat { // non-assignable AreaCombat &operator=(const AreaCombat &) = delete; - void getList(const Position ¢erPos, const Position &targetPos, std::forward_list<std::shared_ptr<Tile>> &list) const; + void getList(const Position ¢erPos, const Position &targetPos, std::vector<std::shared_ptr<Tile>> &list) const; void setupArea(const std::list<uint32_t> &list, uint32_t rows); void setupArea(int32_t length, int32_t spread); @@ -290,7 +290,7 @@ class Combat { static void doCombatDispel(std::shared_ptr<Creature> caster, std::shared_ptr<Creature> target, const CombatParams ¶ms); static void doCombatDispel(std::shared_ptr<Creature> caster, const Position &position, const std::unique_ptr<AreaCombat> &area, const CombatParams ¶ms); - static void getCombatArea(const Position ¢erPos, const Position &targetPos, const std::unique_ptr<AreaCombat> &area, std::forward_list<std::shared_ptr<Tile>> &list); + static void getCombatArea(const Position ¢erPos, const Position &targetPos, const std::unique_ptr<AreaCombat> &area, std::vector<std::shared_ptr<Tile>> &list); static bool isInPvpZone(std::shared_ptr<Creature> attacker, std::shared_ptr<Creature> target); static bool isProtected(std::shared_ptr<Player> attacker, std::shared_ptr<Player> target); @@ -320,7 +320,7 @@ class Combat { return area != nullptr; } void addCondition(const std::shared_ptr<Condition> condition) { - params.conditionList.emplace_front(condition); + params.conditionList.emplace_back(condition); } void setPlayerCombatValues(formulaType_t formulaType, double mina, double minb, double maxa, double maxb); void postCombatEffects(std::shared_ptr<Creature> caster, const Position &origin, const Position &pos) const { diff --git a/src/creatures/creature.cpp b/src/creatures/creature.cpp index c68ab4f35..e3474ab24 100644 --- a/src/creatures/creature.cpp +++ b/src/creatures/creature.cpp @@ -1050,6 +1050,10 @@ void Creature::goToFollowCreature_async(std::function<void()> &&onComplete) { pathfinderRunning.store(true); g_dispatcher().asyncEvent([self = getCreature()] { + if (!self || self->isRemoved()) { + return; + } + self->goToFollowCreature(); self->pathfinderRunning.store(false); }); diff --git a/src/creatures/monsters/spawns/spawn_monster.cpp b/src/creatures/monsters/spawns/spawn_monster.cpp index 968b90d8c..ca8f45847 100644 --- a/src/creatures/monsters/spawns/spawn_monster.cpp +++ b/src/creatures/monsters/spawns/spawn_monster.cpp @@ -61,8 +61,7 @@ bool SpawnsMonster::loadFromXML(const std::string &filemonstername) { continue; } - spawnMonsterList.emplace_front(centerPos, radius); - SpawnMonster &spawnMonster = spawnMonsterList.front(); + SpawnMonster &spawnMonster = spawnMonsterList.emplace_back(centerPos, radius); for (auto childMonsterNode : spawnMonsterNode.children()) { if (strcasecmp(childMonsterNode.name(), "monster") == 0) { diff --git a/src/creatures/monsters/spawns/spawn_monster.hpp b/src/creatures/monsters/spawns/spawn_monster.hpp index 35b856d23..81a08973c 100644 --- a/src/creatures/monsters/spawns/spawn_monster.hpp +++ b/src/creatures/monsters/spawns/spawn_monster.hpp @@ -36,6 +36,25 @@ class SpawnMonster { SpawnMonster(const SpawnMonster &) = delete; SpawnMonster &operator=(const SpawnMonster &) = delete; + // moveable + SpawnMonster(SpawnMonster &&rhs) noexcept : + spawnMonsterMap(std::move(rhs.spawnMonsterMap)), + spawnedMonsterMap(std::move(rhs.spawnedMonsterMap)), + checkSpawnMonsterEvent(rhs.checkSpawnMonsterEvent), centerPos(rhs.centerPos), radius(rhs.radius), interval(rhs.interval) { } + + SpawnMonster &operator=(SpawnMonster &&rhs) noexcept { + if (this != &rhs) { + spawnMonsterMap = std::move(rhs.spawnMonsterMap); + spawnedMonsterMap = std::move(rhs.spawnedMonsterMap); + + checkSpawnMonsterEvent = rhs.checkSpawnMonsterEvent; + centerPos = rhs.centerPos; + radius = rhs.radius; + interval = rhs.interval; + } + return *this; + } + bool addMonster(const std::string &name, const Position &pos, Direction dir, uint32_t interval, uint32_t weight = 1); void removeMonster(std::shared_ptr<Monster> monster); void removeMonsters(); @@ -83,10 +102,6 @@ class SpawnsMonster { bool loadFromXML(const std::string &filemonstername); void startup(); void clear(); - SpawnMonster &addSpawnMonster(const Position &pos, int32_t radius) { - spawnMonsterList.emplace_front(pos, radius); - return spawnMonsterList.front(); - } bool isStarted() const { return started; @@ -94,12 +109,12 @@ class SpawnsMonster { bool isLoaded() const { return loaded; } - std::forward_list<SpawnMonster> &getspawnMonsterList() { + std::vector<SpawnMonster> &getspawnMonsterList() { return spawnMonsterList; } private: - std::forward_list<SpawnMonster> spawnMonsterList; + std::vector<SpawnMonster> spawnMonsterList; std::string filemonstername; bool loaded = false; bool started = false; diff --git a/src/creatures/npcs/spawns/spawn_npc.cpp b/src/creatures/npcs/spawns/spawn_npc.cpp index a8be411dc..694822c23 100644 --- a/src/creatures/npcs/spawns/spawn_npc.cpp +++ b/src/creatures/npcs/spawns/spawn_npc.cpp @@ -57,7 +57,7 @@ bool SpawnsNpc::loadFromXml(const std::string &fileNpcName) { continue; } - const auto &spawnNpc = spawnNpcList.emplace_front(std::make_shared<SpawnNpc>(centerPos, radius)); + const auto &spawnNpc = spawnNpcList.emplace_back(std::make_shared<SpawnNpc>(centerPos, radius)); for (auto childNode : spawnNode.children()) { if (strcasecmp(childNode.name(), "npc") == 0) { diff --git a/src/creatures/npcs/spawns/spawn_npc.hpp b/src/creatures/npcs/spawns/spawn_npc.hpp index 49eb3bc6f..b0d0ab862 100644 --- a/src/creatures/npcs/spawns/spawn_npc.hpp +++ b/src/creatures/npcs/spawns/spawn_npc.hpp @@ -94,12 +94,12 @@ class SpawnsNpc { return fileName = std::move(setName); } - std::forward_list<std::shared_ptr<SpawnNpc>> &getSpawnNpcList() { + std::vector<std::shared_ptr<SpawnNpc>> &getSpawnNpcList() { return spawnNpcList; } private: - std::forward_list<std::shared_ptr<SpawnNpc>> spawnNpcList; + std::vector<std::shared_ptr<SpawnNpc>> spawnNpcList; std::string fileName; bool loaded = false; bool started = false; diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index 273079873..2660c0391 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -5289,12 +5289,12 @@ double Player::getLostPercent() const { void Player::learnInstantSpell(const std::string &spellName) { if (!hasLearnedInstantSpell(spellName)) { - learnedInstantSpellList.push_front(spellName); + learnedInstantSpellList.emplace_back(spellName); } } void Player::forgetInstantSpell(const std::string &spellName) { - learnedInstantSpellList.remove(spellName); + std::erase(learnedInstantSpellList, spellName); } bool Player::hasLearnedInstantSpell(const std::string &spellName) const { @@ -5700,12 +5700,12 @@ bool Player::addPartyInvitation(std::shared_ptr<Party> newParty) { return false; } - invitePartyList.push_front(newParty); + invitePartyList.emplace_back(newParty); return true; } void Player::removePartyInvitation(std::shared_ptr<Party> remParty) { - invitePartyList.remove(remParty); + std::erase(invitePartyList, remParty); } void Player::clearPartyInvitations() { @@ -6124,7 +6124,7 @@ bool Player::hasModalWindowOpen(uint32_t modalWindowId) const { } void Player::onModalWindowHandled(uint32_t modalWindowId) { - modalWindows.remove(modalWindowId); + std::erase(modalWindows, modalWindowId); } void Player::sendModalWindow(const ModalWindow &modalWindow) { @@ -6132,7 +6132,7 @@ void Player::sendModalWindow(const ModalWindow &modalWindow) { return; } - modalWindows.push_front(modalWindow.id); + modalWindows.emplace_back(modalWindow.id); client->sendModalWindow(modalWindow); } @@ -6251,8 +6251,10 @@ size_t Player::getMaxDepotItems() const { return g_configManager().getNumber(FREE_DEPOT_LIMIT, __FUNCTION__); } -std::forward_list<std::shared_ptr<Condition>> Player::getMuteConditions() const { - std::forward_list<std::shared_ptr<Condition>> muteConditions; +std::vector<std::shared_ptr<Condition>> Player::getMuteConditions() const { + std::vector<std::shared_ptr<Condition>> muteConditions; + muteConditions.reserve(conditions.size()); + for (const std::shared_ptr<Condition> &condition : conditions) { if (condition->getTicks() <= 0) { continue; @@ -6263,7 +6265,7 @@ std::forward_list<std::shared_ptr<Condition>> Player::getMuteConditions() const continue; } - muteConditions.push_front(condition); + muteConditions.emplace_back(condition); } return muteConditions; } diff --git a/src/creatures/players/player.hpp b/src/creatures/players/player.hpp index 2c765850f..bfaad14cf 100644 --- a/src/creatures/players/player.hpp +++ b/src/creatures/players/player.hpp @@ -2664,7 +2664,7 @@ class Player final : public Creature, public Cylinder, public Bankable { static uint32_t playerFirstID; static uint32_t playerLastID; - std::forward_list<std::shared_ptr<Condition>> getMuteConditions() const; + std::vector<std::shared_ptr<Condition>> getMuteConditions() const; void checkTradeState(std::shared_ptr<Item> item); bool hasCapacity(std::shared_ptr<Item> item, uint32_t count) const; @@ -2760,11 +2760,11 @@ class Player final : public Creature, public Cylinder, public Bankable { GuildWarVector guildWarVector; - std::forward_list<std::shared_ptr<Party>> invitePartyList; - std::forward_list<uint32_t> modalWindows; - std::forward_list<std::string> learnedInstantSpellList; + std::vector<std::shared_ptr<Party>> invitePartyList; + std::vector<uint32_t> modalWindows; + std::vector<std::string> learnedInstantSpellList; // TODO: This variable is only temporarily used when logging in, get rid of it somehow. - std::forward_list<std::shared_ptr<Condition>> storedConditionList; + std::vector<std::shared_ptr<Condition>> storedConditionList; std::unordered_set<std::shared_ptr<MonsterType>> m_bestiaryMonsterTracker; std::unordered_set<std::shared_ptr<MonsterType>> m_bosstiaryMonsterTracker; diff --git a/src/game/game.cpp b/src/game/game.cpp index 7ef6b7a90..2348d3845 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -593,7 +593,8 @@ void Game::setGameState(GameState_t newState) { } case GAME_STATE_SHUTDOWN: { - g_globalEvents().execute(GLOBALEVENT_SHUTDOWN); + g_globalEvents().save(); + g_globalEvents().shutdown(); // kick all players that are still online auto it = players.begin(); @@ -611,6 +612,8 @@ void Game::setGameState(GameState_t newState) { } case GAME_STATE_CLOSED: { + g_globalEvents().save(); + /* kick all players without the CanAlwaysLogin flag */ auto it = players.begin(); while (it != players.end()) { @@ -6935,14 +6938,6 @@ int32_t Game::applyHealthChange(CombatDamage &damage, std::shared_ptr<Creature> } } } - - if (damage.primary.value >= targetHealth) { - damage.primary.value = targetHealth; - damage.secondary.value = 0; - } else if (damage.secondary.value) { - damage.secondary.value = std::min<int32_t>(damage.secondary.value, targetHealth - damage.primary.value); - } - return targetHealth; } @@ -7300,19 +7295,12 @@ bool Game::combatChangeHealth(std::shared_ptr<Creature> attacker, std::shared_pt } } - auto targetHealth = applyHealthChange(damage, target); - if (damage.primary.value >= targetHealth) { - damage.primary.value = targetHealth; - damage.secondary.value = 0; - } else if (damage.secondary.value) { - damage.secondary.value = std::min<int32_t>(damage.secondary.value, targetHealth - damage.primary.value); - } - // Apply Custom PvP Damage (must be placed here to avoid recursive calls) if (attackerPlayer && targetPlayer) { applyPvPDamage(damage, attackerPlayer, targetPlayer); } + auto targetHealth = target->getHealth(); realDamage = damage.primary.value + damage.secondary.value; if (realDamage == 0) { return true; @@ -7324,6 +7312,14 @@ bool Game::combatChangeHealth(std::shared_ptr<Creature> attacker, std::shared_pt } } + targetHealth = applyHealthChange(damage, target); + if (damage.primary.value >= targetHealth) { + damage.primary.value = targetHealth; + damage.secondary.value = 0; + } else if (damage.secondary.value) { + damage.secondary.value = std::min<int32_t>(damage.secondary.value, targetHealth - damage.primary.value); + } + target->drainHealth(attacker, realDamage); if (realDamage > 0 && targetMonster) { if (attackerPlayer && attackerPlayer->getPlayer()) { diff --git a/src/io/functions/iologindata_load_player.cpp b/src/io/functions/iologindata_load_player.cpp index 8406cf110..764d46414 100644 --- a/src/io/functions/iologindata_load_player.cpp +++ b/src/io/functions/iologindata_load_player.cpp @@ -235,7 +235,7 @@ void IOLoginDataLoad::loadPlayerConditions(std::shared_ptr<Player> player, DBRes auto condition = Condition::createCondition(propStream); while (condition) { if (condition->unserialize(propStream)) { - player->storedConditionList.push_front(condition); + player->storedConditionList.emplace_back(condition); } condition = Condition::createCondition(propStream); } @@ -465,7 +465,7 @@ void IOLoginDataLoad::loadPlayerInstantSpellList(std::shared_ptr<Player> player, query << "SELECT `player_id`, `name` FROM `player_spells` WHERE `player_id` = " << player->getGUID(); if ((result = db.storeQuery(query.str()))) { do { - player->learnedInstantSpellList.emplace_front(result->getString("name")); + player->learnedInstantSpellList.emplace_back(result->getString("name")); } while (result->next()); } } diff --git a/src/io/iologindata.cpp b/src/io/iologindata.cpp index 122ade42c..132cead64 100644 --- a/src/io/iologindata.cpp +++ b/src/io/iologindata.cpp @@ -349,15 +349,14 @@ bool IOLoginData::hasBiddedOnHouse(uint32_t guid) { return db.storeQuery(query.str()).get() != nullptr; } -std::forward_list<VIPEntry> IOLoginData::getVIPEntries(uint32_t accountId) { - std::forward_list<VIPEntry> entries; - +std::vector<VIPEntry> IOLoginData::getVIPEntries(uint32_t accountId) { std::string query = fmt::format("SELECT `player_id`, (SELECT `name` FROM `players` WHERE `id` = `player_id`) AS `name`, `description`, `icon`, `notify` FROM `account_viplist` WHERE `account_id` = {}", accountId); + std::vector<VIPEntry> entries; - DBResult_ptr result = Database::getInstance().storeQuery(query); - if (result) { + if (const auto &result = Database::getInstance().storeQuery(query)) { + entries.reserve(result->countResults()); do { - entries.emplace_front( + entries.emplace_back( result->getNumber<uint32_t>("player_id"), result->getString("name"), result->getString("description"), @@ -366,6 +365,7 @@ std::forward_list<VIPEntry> IOLoginData::getVIPEntries(uint32_t accountId) { ); } while (result->next()); } + return entries; } @@ -388,15 +388,16 @@ void IOLoginData::removeVIPEntry(uint32_t accountId, uint32_t guid) { g_database().executeQuery(query); } -std::forward_list<VIPGroupEntry> IOLoginData::getVIPGroupEntries(uint32_t accountId, uint32_t guid) { - std::forward_list<VIPGroupEntry> entries; - +std::vector<VIPGroupEntry> IOLoginData::getVIPGroupEntries(uint32_t accountId, uint32_t guid) { std::string query = fmt::format("SELECT `id`, `name`, `customizable` FROM `account_vipgroups` WHERE `account_id` = {}", accountId); - DBResult_ptr result = g_database().storeQuery(query); - if (result) { + std::vector<VIPGroupEntry> entries; + + if (const auto &result = g_database().storeQuery(query)) { + entries.reserve(result->countResults()); + do { - entries.emplace_front( + entries.emplace_back( result->getNumber<uint8_t>("id"), result->getString("name"), result->getNumber<uint8_t>("customizable") == 0 ? false : true diff --git a/src/io/iologindata.hpp b/src/io/iologindata.hpp index be4147398..1451cf897 100644 --- a/src/io/iologindata.hpp +++ b/src/io/iologindata.hpp @@ -31,12 +31,12 @@ class IOLoginData { static void increaseBankBalance(uint32_t guid, uint64_t bankBalance); static bool hasBiddedOnHouse(uint32_t guid); - static std::forward_list<VIPEntry> getVIPEntries(uint32_t accountId); + static std::vector<VIPEntry> getVIPEntries(uint32_t accountId); static void addVIPEntry(uint32_t accountId, uint32_t guid, const std::string &description, uint32_t icon, bool notify); static void editVIPEntry(uint32_t accountId, uint32_t guid, const std::string &description, uint32_t icon, bool notify); static void removeVIPEntry(uint32_t accountId, uint32_t guid); - static std::forward_list<VIPGroupEntry> getVIPGroupEntries(uint32_t accountId, uint32_t guid); + static std::vector<VIPGroupEntry> getVIPGroupEntries(uint32_t accountId, uint32_t guid); static void addVIPGroupEntry(uint8_t groupId, uint32_t accountId, const std::string &groupName, bool customizable); static void editVIPGroupEntry(uint8_t groupId, uint32_t accountId, const std::string &groupName, bool customizable); static void removeVIPGroupEntry(uint8_t groupId, uint32_t accountId); diff --git a/src/io/iomapserialize.cpp b/src/io/iomapserialize.cpp index 475a210a7..e2a367c4e 100644 --- a/src/io/iomapserialize.cpp +++ b/src/io/iomapserialize.cpp @@ -241,19 +241,21 @@ void IOMapSerialize::saveTile(PropWriteStream &stream, std::shared_ptr<Tile> til return; } - std::forward_list<std::shared_ptr<Item>> items; + std::vector<std::shared_ptr<Item>> items; + items.reserve(32); + uint16_t count = 0; for (auto &item : *tileItems) { if (item->getID() == ITEM_BATHTUB_FILLED_NOTMOVABLE) { std::shared_ptr<Item> tub = Item::CreateItem(ITEM_BATHTUB_FILLED); - items.push_front(tub); + items.emplace_back(tub); ++count; continue; } else if (!item->isSavedToHouses()) { continue; } - items.push_front(item); + items.emplace_back(item); ++count; } diff --git a/src/lua/functions/core/game/global_functions.cpp b/src/lua/functions/core/game/global_functions.cpp index c8a857cff..6b5175b06 100644 --- a/src/lua/functions/core/game/global_functions.cpp +++ b/src/lua/functions/core/game/global_functions.cpp @@ -16,6 +16,7 @@ #include "lua/functions/core/game/global_functions.hpp" #include "lua/scripts/lua_environment.hpp" #include "lua/scripts/script_environment.hpp" +#include "lua/global/globalevent.hpp" #include "server/network/protocol/protocolstatus.hpp" #include "creatures/players/wheel/player_wheel.hpp" #include "lua/global/lua_timer_event_descr.hpp" @@ -443,7 +444,7 @@ int GlobalFunctions::luaDoAreaCombatCondition(lua_State* L) { if (area || areaId == 0) { CombatParams params; params.impactEffect = getNumber<uint16_t>(L, 5); - params.conditionList.emplace_front(condition); + params.conditionList.emplace_back(condition); Combat::doCombatCondition(creature, getPosition(L, 2), area, params); pushBoolean(L, true); } else { @@ -478,7 +479,7 @@ int GlobalFunctions::luaDoTargetCombatCondition(lua_State* L) { CombatParams params; params.impactEffect = getNumber<uint16_t>(L, 4); - params.conditionList.emplace_front(condition->clone()); + params.conditionList.emplace_back(condition->clone()); Combat::doCombatCondition(creature, target, params); pushBoolean(L, true); return 1; @@ -706,6 +707,7 @@ int GlobalFunctions::luaStopEvent(lua_State* L) { } int GlobalFunctions::luaSaveServer(lua_State* L) { + g_globalEvents().save(); g_saveManager().scheduleAll(); pushBoolean(L, true); return 1; diff --git a/src/lua/functions/creatures/monster/monster_functions.cpp b/src/lua/functions/creatures/monster/monster_functions.cpp index 273f6af5c..5578477d6 100644 --- a/src/lua/functions/creatures/monster/monster_functions.cpp +++ b/src/lua/functions/creatures/monster/monster_functions.cpp @@ -363,8 +363,7 @@ int MonsterFunctions::luaMonsterSetSpawnPosition(lua_State* L) { const Position &pos = monster->getPosition(); monster->setMasterPos(pos); - g_game().map.spawnsMonster.getspawnMonsterList().emplace_front(pos, 5); - SpawnMonster &spawnMonster = g_game().map.spawnsMonster.getspawnMonsterList().front(); + SpawnMonster &spawnMonster = g_game().map.spawnsMonster.getspawnMonsterList().emplace_back(pos, 5); uint32_t interval = getNumber<uint32_t>(L, 2, 90) * 1000 * 100 / std::max((uint32_t)1, (g_configManager().getNumber(RATE_SPAWN, __FUNCTION__) * eventschedule)); spawnMonster.addMonster(monster->mType->typeName, pos, DIRECTION_NORTH, static_cast<uint32_t>(interval)); spawnMonster.startSpawnMonsterCheck(); diff --git a/src/lua/functions/events/global_event_functions.cpp b/src/lua/functions/events/global_event_functions.cpp index ea6a5f7df..129a466e9 100644 --- a/src/lua/functions/events/global_event_functions.cpp +++ b/src/lua/functions/events/global_event_functions.cpp @@ -40,6 +40,8 @@ int GlobalEventFunctions::luaGlobalEventType(lua_State* L) { global->setEventType(GLOBALEVENT_PERIODCHANGE); } else if (tmpStr == "onthink") { global->setEventType(GLOBALEVENT_ON_THINK); + } else if (tmpStr == "save") { + global->setEventType(GLOBALEVENT_SAVE); } else { g_logger().error("[GlobalEventFunctions::luaGlobalEventType] - " "Invalid type for global event: {}"); diff --git a/src/lua/functions/events/global_event_functions.hpp b/src/lua/functions/events/global_event_functions.hpp index 6c988a61c..e04072a24 100644 --- a/src/lua/functions/events/global_event_functions.hpp +++ b/src/lua/functions/events/global_event_functions.hpp @@ -25,6 +25,7 @@ class GlobalEventFunctions final : LuaScriptInterface { registerMethod(L, "GlobalEvent", "onShutdown", GlobalEventFunctions::luaGlobalEventOnCallback); registerMethod(L, "GlobalEvent", "onRecord", GlobalEventFunctions::luaGlobalEventOnCallback); registerMethod(L, "GlobalEvent", "onPeriodChange", GlobalEventFunctions::luaGlobalEventOnCallback); + registerMethod(L, "GlobalEvent", "onSave", GlobalEventFunctions::luaGlobalEventOnCallback); } private: diff --git a/src/lua/global/globalevent.cpp b/src/lua/global/globalevent.cpp index 04420431d..32fa1a01a 100644 --- a/src/lua/global/globalevent.cpp +++ b/src/lua/global/globalevent.cpp @@ -66,6 +66,14 @@ void GlobalEvents::startup() const { execute(GLOBALEVENT_STARTUP); } +void GlobalEvents::shutdown() const { + execute(GLOBALEVENT_SHUTDOWN); +} + +void GlobalEvents::save() const { + execute(GLOBALEVENT_SAVE); +} + void GlobalEvents::timer() { time_t now = time(nullptr); @@ -165,7 +173,8 @@ GlobalEventMap GlobalEvents::getEventMap(GlobalEvent_t type) { case GLOBALEVENT_PERIODCHANGE: case GLOBALEVENT_STARTUP: case GLOBALEVENT_SHUTDOWN: - case GLOBALEVENT_RECORD: { + case GLOBALEVENT_RECORD: + case GLOBALEVENT_SAVE: { GlobalEventMap retMap; for (const auto &it : serverMap) { if (it.second->getEventType() == type) { @@ -196,6 +205,8 @@ std::string GlobalEvent::getScriptTypeName() const { return "onPeriodChange"; case GLOBALEVENT_ON_THINK: return "onThink"; + case GLOBALEVENT_SAVE: + return "onSave"; default: g_logger().error("[GlobalEvent::getScriptTypeName] - Invalid event type"); return std::string(); diff --git a/src/lua/global/globalevent.hpp b/src/lua/global/globalevent.hpp index 004c6cb5f..128a743b3 100644 --- a/src/lua/global/globalevent.hpp +++ b/src/lua/global/globalevent.hpp @@ -29,6 +29,8 @@ class GlobalEvents final : public Scripts { } void startup() const; + void shutdown() const; + void save() const; void timer(); void think(); diff --git a/src/lua/lua_definitions.hpp b/src/lua/lua_definitions.hpp index 083871e23..81add18e5 100644 --- a/src/lua/lua_definitions.hpp +++ b/src/lua/lua_definitions.hpp @@ -108,6 +108,7 @@ enum GlobalEvent_t { GLOBALEVENT_RECORD, GLOBALEVENT_PERIODCHANGE, GLOBALEVENT_ON_THINK, + GLOBALEVENT_SAVE, }; enum ModuleType_t { diff --git a/src/map/map.cpp b/src/map/map.cpp index 82629aeae..7406ecffb 100644 --- a/src/map/map.cpp +++ b/src/map/map.cpp @@ -457,62 +457,159 @@ bool Map::canThrowObjectTo(const Position &fromPos, const Position &toPos, bool return isSightClear(fromPos, toPos, false); } -bool Map::checkSightLine(const Position &fromPos, const Position &toPos) { - if (fromPos == toPos) { +bool Map::checkSightLine(Position start, Position destination) { + if (start.x == destination.x && start.y == destination.y) { return true; } - Position start(fromPos.z > toPos.z ? toPos : fromPos); - Position destination(fromPos.z > toPos.z ? fromPos : toPos); + int32_t distanceX = Position::getDistanceX(start, destination); + int32_t distanceY = Position::getDistanceY(start, destination); - const int8_t mx = start.x < destination.x ? 1 : start.x == destination.x ? 0 - : -1; - const int8_t my = start.y < destination.y ? 1 : start.y == destination.y ? 0 - : -1; + if (start.y == destination.y) { + // Horizontal line + const uint16_t delta = start.x < destination.x ? 0x0001 : 0xFFFF; + while (--distanceX > 0) { + start.x += delta; - int32_t A = Position::getOffsetY(destination, start); - int32_t B = Position::getOffsetX(start, destination); - int32_t C = -(A * destination.x + B * destination.y); + const auto &tile = getTile(start.x, start.y, start.z); + if (tile && tile->hasFlag(TILESTATE_BLOCKPROJECTILE)) { + return false; + } + } + } else if (start.x == destination.x) { + // Vertical line + const uint16_t delta = start.y < destination.y ? 0x0001 : 0xFFFF; + while (--distanceY > 0) { + start.y += delta; + + const auto &tile = getTile(start.x, start.y, start.z); + if (tile && tile->hasFlag(TILESTATE_BLOCKPROJECTILE)) { + return false; + } + } + } else { + // Xiaolin Wu's line algorithm - https://en.wikipedia.org/wiki/Xiaolin_Wu%27s_line_algorithm + // based on Michael Abrash's implementation - https://www.amazon.com/gp/product/1576101746/102-5103244-8168911 + uint16_t eAdj; + uint16_t eAcc = 0; + uint16_t deltaX = 0x0001; + uint16_t deltaY = 0x0001; + + if (distanceY > distanceX) { + eAdj = (static_cast<uint32_t>(distanceX) << 16) / static_cast<uint32_t>(distanceY); + + if (start.y > destination.y) { + std::swap(start.x, destination.x); + std::swap(start.y, destination.y); + } + if (start.x > destination.x) { + deltaX = 0xFFFF; + eAcc -= eAdj; + } - while (start.x != destination.x || start.y != destination.y) { - int32_t move_hor = std::abs(A * (start.x + mx) + B * (start.y) + C); - int32_t move_ver = std::abs(A * (start.x) + B * (start.y + my) + C); - int32_t move_cross = std::abs(A * (start.x + mx) + B * (start.y + my) + C); + while (--distanceY > 0) { + uint16_t xIncrease = 0; + const uint16_t eAccTemp = eAcc; + eAcc += eAdj; + if (eAcc <= eAccTemp) { + xIncrease = deltaX; + } - if (start.y != destination.y && (start.x == destination.x || move_hor > move_ver || move_hor > move_cross)) { - start.y += my; - } + const auto &tile = getTile(start.x + xIncrease, start.y + deltaY, start.z); + if (tile && tile->hasFlag(TILESTATE_BLOCKPROJECTILE)) { + if (Position::areInRange<1, 1>(start, destination)) { + return true; + } + return false; + } - if (start.x != destination.x && (start.y == destination.y || move_ver > move_hor || move_ver > move_cross)) { - start.x += mx; - } + start.x += xIncrease; + start.y += deltaY; + } + } else { + eAdj = (static_cast<uint32_t>(distanceY) << 16) / static_cast<uint32_t>(distanceX); - const std::shared_ptr<Tile> tile = getTile(start.x, start.y, start.z); - if (tile && tile->hasProperty(CONST_PROP_BLOCKPROJECTILE)) { - return false; - } - } + if (start.x > destination.x) { + std::swap(start.x, destination.x); + std::swap(start.y, destination.y); + } + if (start.y > destination.y) { + deltaY = 0xFFFF; + eAcc -= eAdj; + } - // now we need to perform a jump between floors to see if everything is clear (literally) - while (start.z != destination.z) { - const std::shared_ptr<Tile> tile = getTile(start.x, start.y, start.z); - if (tile && tile->getThingCount() > 0) { - return false; - } + while (--distanceX > 0) { + uint16_t yIncrease = 0; + const uint16_t eAccTemp = eAcc; + eAcc += eAdj; + if (eAcc <= eAccTemp) { + yIncrease = deltaY; + } - start.z++; - } + const auto &tile = getTile(start.x + deltaX, start.y + yIncrease, start.z); + if (tile && tile->hasFlag(TILESTATE_BLOCKPROJECTILE)) { + if (Position::areInRange<1, 1>(start, destination)) { + return true; + } + return false; + } + start.x += deltaX; + start.y += yIncrease; + } + } + } return true; } bool Map::isSightClear(const Position &fromPos, const Position &toPos, bool floorCheck) { + // Check if this sight line should be even possible if (floorCheck && fromPos.z != toPos.z) { return false; } - // Cast two converging rays and see if either yields a result. - return checkSightLine(fromPos, toPos) || checkSightLine(toPos, fromPos); + // Check if we even need to perform line checking + if (fromPos.z == toPos.z && (Position::areInRange<1, 1>(fromPos, toPos) || (!floorCheck && fromPos.z == 0))) { + return true; + } + + // We can only throw one floor up + if (fromPos.z > toPos.z && Position::getDistanceZ(fromPos, toPos) > 1) { + return false; + } + + // Perform check for current floor + const bool sightClear = checkSightLine(fromPos, toPos); + if (floorCheck || (fromPos.z == toPos.z && sightClear)) { + return sightClear; + } + + uint8_t startZ; + if (sightClear && (fromPos.z < toPos.z || fromPos.z == toPos.z)) { + startZ = fromPos.z; + } else { + // Check if we can throw above obstacle + const auto &tile = getTile(fromPos.x, fromPos.y, fromPos.z - 1); + if ((tile && (tile->getGround() || tile->hasFlag(TILESTATE_BLOCKPROJECTILE))) || !checkSightLine(Position(fromPos.x, fromPos.y, fromPos.z - 1), Position(toPos.x, toPos.y, toPos.z - 1))) { + return false; + } + + // We can throw above obstacle + if (fromPos.z > toPos.z) { + return true; + } + + startZ = fromPos.z - 1; + } + + // now we need to perform a jump between floors to see if everything is clear (literally) + for (; startZ != toPos.z; ++startZ) { + const auto &tile = getTile(toPos.x, toPos.y, startZ); + if (tile && (tile->getGround() || tile->hasFlag(TILESTATE_BLOCKPROJECTILE))) { + return false; + } + } + return true; } std::shared_ptr<Tile> Map::canWalkTo(const std::shared_ptr<Creature> &creature, const Position &pos) { diff --git a/src/map/map.hpp b/src/map/map.hpp index e57328e12..bae074451 100644 --- a/src/map/map.hpp +++ b/src/map/map.hpp @@ -119,7 +119,7 @@ class Map : public MapCache { * \returns The result if there is no obstacles */ bool isSightClear(const Position &fromPos, const Position &toPos, bool floorCheck); - bool checkSightLine(const Position &fromPos, const Position &toPos); + bool checkSightLine(Position start, Position destination); std::shared_ptr<Tile> canWalkTo(const std::shared_ptr<Creature> &creature, const Position &pos); diff --git a/src/server/network/protocol/protocolgame.cpp b/src/server/network/protocol/protocolgame.cpp index 3ac22ac6b..f3a4bcef3 100644 --- a/src/server/network/protocol/protocolgame.cpp +++ b/src/server/network/protocol/protocolgame.cpp @@ -6695,7 +6695,7 @@ void ProtocolGame::sendAddCreature(std::shared_ptr<Creature> creature, const Pos sendVIPGroups(); - const std::forward_list<VIPEntry> &vipEntries = IOLoginData::getVIPEntries(player->getAccountId()); + const auto &vipEntries = IOLoginData::getVIPEntries(player->getAccountId()); if (player->isAccessPlayer()) { for (const VIPEntry &entry : vipEntries) { diff --git a/src/server/signals.cpp b/src/server/signals.cpp index c85b21312..626a918f6 100644 --- a/src/server/signals.cpp +++ b/src/server/signals.cpp @@ -15,6 +15,7 @@ #include "lib/thread/thread_pool.hpp" #include "lua/creature/events.hpp" #include "lua/scripts/lua_environment.hpp" +#include "lua/global/globalevent.hpp" #include "server/signals.hpp" Signals::Signals(asio::io_service &service) : @@ -92,6 +93,7 @@ void Signals::sigtermHandler() { void Signals::sigusr1Handler() { // Dispatcher thread g_logger().info("SIGUSR1 received, saving the game state..."); + g_globalEvents().save(); g_saveManager().scheduleAll(); }