diff --git a/.github/workflows/clean-cache.yaml b/.github/workflows/clean-cache.yaml new file mode 100644 index 00000000000..ed298937a51 --- /dev/null +++ b/.github/workflows/clean-cache.yaml @@ -0,0 +1,30 @@ +--- +name: Cleanup caches by a branch +on: + pull_request: + types: + - closed + +jobs: + cleanup: + runs-on: ubuntu-latest + steps: + - name: Cleanup + run: | + gh extension install actions/gh-actions-cache + + echo "Fetching list of cache key" + cacheKeysForPR=$(gh actions-cache list -R "$REPO" -B "$BRANCH" -L 100 | cut -f 1 ) + + ## Setting this to not fail the workflow while deleting cache keys. + set +e + echo "Deleting caches..." + for cacheKey in $cacheKeysForPR + do + gh actions-cache delete "$cacheKey" -R "$REPO" -B "$BRANCH" --confirm + done + echo "Done" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + REPO: ${{ github.repository }} + BRANCH: refs/pull/${{ github.event.pull_request.number }}/merge diff --git a/data-otservbr-global/migrations/41.lua b/data-otservbr-global/migrations/41.lua index 86a6d8ffec1..179ac18b574 100644 --- a/data-otservbr-global/migrations/41.lua +++ b/data-otservbr-global/migrations/41.lua @@ -1,3 +1,11 @@ function onUpdateDatabase() - return false -- true = There are others migrations file | false = this is the last migration file + logger.info("Updating database to version 42 (fix xpboost types)") + + db.query([[ + ALTER TABLE `players` + MODIFY `xpboost_stamina` smallint(5) UNSIGNED DEFAULT NULL, + MODIFY `xpboost_value` tinyint(4) UNSIGNED DEFAULT NULL + ]]) + + return true end diff --git a/data-otservbr-global/migrations/42.lua b/data-otservbr-global/migrations/42.lua new file mode 100644 index 00000000000..86a6d8ffec1 --- /dev/null +++ b/data-otservbr-global/migrations/42.lua @@ -0,0 +1,3 @@ +function onUpdateDatabase() + return false -- true = There are others migrations file | false = this is the last migration file +end diff --git a/data-otservbr-global/monster/quests/feaster_of_souls/the_pale_worm.lua b/data-otservbr-global/monster/quests/feaster_of_souls/the_pale_worm.lua index d9459ff0821..bde610ee680 100644 --- a/data-otservbr-global/monster/quests/feaster_of_souls/the_pale_worm.lua +++ b/data-otservbr-global/monster/quests/feaster_of_souls/the_pale_worm.lua @@ -96,6 +96,8 @@ monster.loot = { { name = "bloody tears", chance = 1500 }, { name = "ghost chestplate", chance = 150 }, { name = "spooky hood", chance = 150 }, + { name = "pale worm's scalp", chance = 1200 }, + { name = "spectral scrap of cloth", chance = 250 }, { name = "fabulous legs", chance = 150 }, { name = "phantasmal axe", chance = 150 }, { name = "ghost backpack", chance = 150 }, diff --git a/data-otservbr-global/npc/lokur.lua b/data-otservbr-global/npc/lokur.lua index 6b9bbc271e0..0da622e8ba8 100644 --- a/data-otservbr-global/npc/lokur.lua +++ b/data-otservbr-global/npc/lokur.lua @@ -63,6 +63,32 @@ local function creatureSayCallback(npc, creature, type, message) return false end + if MsgContains(message, "ticket") then + if Player(creature):getStorageValue(Storage.WagonTicket) >= os.time() then + npcHandler:say("Your weekly ticket is still valid. Would be a waste of money to purchase a second one", npc, creature) + return true + end + + npcHandler:say("Do you want to purchase a weekly ticket for the ore wagons? With it you can travel freely and swiftly through Kazordoon for one week. 250 gold only. Deal?", npc, creature) + npcHandler:setTopic(playerId, 9) + elseif MsgContains(message, "yes") and npcHandler:getTopic(playerId) > 0 then + local player = Player(creature) + if npcHandler:getTopic(playerId) == 9 then + if not player:removeMoneyBank(250) then + npcHandler:say("You don't have enough money.", npc, creature) + npcHandler:setTopic(playerId, 0) + return true + end + + player:setStorageValue(Storage.WagonTicket, os.time() + 7 * 24 * 60 * 60) + npcHandler:say("Here is your stamp. It can't be transferred to another person and will last one week from now. You'll get notified upon using an ore wagon when it isn't valid anymore.", npc, creature) + end + npcHandler:setTopic(playerId, 0) + elseif MsgContains(message, "no") and npcHandler:getTopic(playerId) > 0 then + npcHandler:say("No then.", npc, creature) + npcHandler:setTopic(playerId, 0) + end + -- Parse bank npc:parseBank(message, npc, creature, npcHandler) -- Parse guild bank diff --git a/data-otservbr-global/scripts/actions/mounts/haze_mount.lua b/data-otservbr-global/scripts/actions/mounts/haze_mount.lua new file mode 100644 index 00000000000..bd289b57df9 --- /dev/null +++ b/data-otservbr-global/scripts/actions/mounts/haze_mount.lua @@ -0,0 +1,25 @@ +local config = { + [32629] = { mountId = 162, message = "You are now versed to ride the haze!" }, +} + +local hazemount = Action() + +function hazemount.onUse(player, item, fromPosition, target, toPosition, isHotkey) + local mount = config[item.itemid] + + if not mount then + return true + end + + if not player:hasMount(mount.mountId) then + player:addMount(mount.mountId) + player:say(mount.message, TALKTYPE_MONSTER_SAY) + item:remove(1) + else + player:sendTextMessage(19, "You already have this mount") + end + return true +end + +hazemount:id(32629) +hazemount:register() diff --git a/data/libs/concoctions_lib.lua b/data/libs/concoctions_lib.lua index 0d8a6e6574a..72547a8a1ab 100644 --- a/data/libs/concoctions_lib.lua +++ b/data/libs/concoctions_lib.lua @@ -12,26 +12,26 @@ ConcoctionTickType = { Concoction = {} Concoction.__index = Concoction Concoction.Ids = { - KooldownAid = 36723, - StaminaExtension = 36725, - StrikeEnhancement = 36724, - CharmUpgrade = 36726, - WealthDuplex = 36727, - BestiaryBetterment = 36728, - FireResilience = 36729, - IceResilience = 36730, - EarthResilience = 36731, - EnergyResilience = 36732, - HolyResilience = 36733, - DeathResilience = 36734, - PhysicalResilience = 36735, - FireAmplification = 36736, - IceAmplification = 36737, - EarthAmplification = 36738, - EnergyAmplification = 36739, - HolyAmplification = 36740, - DeathAmplification = 36741, - PhysicalAmplification = 36742, + KooldownAid = Concoction_KooldownAid, + StaminaExtension = Concoction_StaminaExtension, + StrikeEnhancement = Concoction_StrikeEnhancement, + CharmUpgrade = Concoction_CharmUpgrade, + WealthDuplex = Concoction_WealthDuplex, + BestiaryBetterment = Concoction_BestiaryBetterment, + FireResilience = Concoction_FireResilience, + IceResilience = Concoction_IceResilience, + EarthResilience = Concoction_EarthResilience, + EnergyResilience = Concoction_EnergyResilience, + HolyResilience = Concoction_HolyResilience, + DeathResilience = Concoction_DeathResilience, + PhysicalResilience = Concoction_PhysicalResilience, + FireAmplification = Concoction_FireAmplification, + IceAmplification = Concoction_IceAmplification, + EarthAmplification = Concoction_EarthAmplification, + EnergyAmplification = Concoction_EnergyAmplification, + HolyAmplification = Concoction_HolyAmplification, + DeathAmplification = Concoction_DeathAmplification, + PhysicalAmplification = Concoction_PhysicalAmplification, } function Concoction.find(identifier) diff --git a/data/libs/raids_lib.lua b/data/libs/raids_lib.lua index 4f1ea9b8cdf..9b0b9d105a7 100644 --- a/data/libs/raids_lib.lua +++ b/data/libs/raids_lib.lua @@ -143,3 +143,17 @@ function Raid:getActivePlayerCount() end return count end + +--Overrides Encounter:addBroadcast +--Adds a stage that broadcasts raid information globally +--@param message string The message to send +--@return boolean True if the message stage is added successfully, false otherwise +function Raid:addBroadcast(message, type) + type = type or MESSAGE_EVENT_ADVANCE + return self:addStage({ + start = function() + self:broadcast(type, message) + Webhook.sendMessage("Incoming raid", message, WEBHOOK_COLOR_RAID) + end, + }) +end diff --git a/data/scripts/creatureevents/bestiary_kill.lua b/data/scripts/creatureevents/bestiary_kill.lua deleted file mode 100644 index a238124eacf..00000000000 --- a/data/scripts/creatureevents/bestiary_kill.lua +++ /dev/null @@ -1,35 +0,0 @@ -local bestiaryOnKill = CreatureEvent("BestiaryOnKill") -function bestiaryOnKill.onKill(player, creature, lastHit) - if not player:isPlayer() or not creature:isMonster() or creature:hasBeenSummoned() or creature:isPlayer() then - return true - end - - local mType = MonsterType(creature:getName()) - if not mType then - logger.error("[bestiaryOnKill.onKill] monster with name {} have wrong MonsterType", creature:getName()) - return true - end - - if mType:Bestiaryrace() == 0 then - return true - end - - local bestiaryBetterment = Concoction.find(Concoction.Ids.BestiaryBetterment) - if not bestiaryBetterment then - logger.warn("[BestiaryOnKill] - Could not find BestiaryBetterment concoction.") - end - for cid, damage in pairs(creature:getDamageMap()) do - local participant = Player(cid) - if participant and participant:isPlayer() then - local bestiaryMultiplier = (configManager.getNumber(configKeys.BESTIARY_KILL_MULTIPLIER) or 1) - if bestiaryBetterment and bestiaryBetterment:active(participant) then - bestiaryMultiplier = bestiaryMultiplier * bestiaryBetterment.config.multiplier - end - participant:addBestiaryKill(creature:getName(), bestiaryMultiplier) - end - end - - return true -end - -bestiaryOnKill:register() diff --git a/data/scripts/creatureevents/bosstiary_kill.lua b/data/scripts/creatureevents/bosstiary_kill.lua deleted file mode 100644 index 2a1139cd339..00000000000 --- a/data/scripts/creatureevents/bosstiary_kill.lua +++ /dev/null @@ -1,33 +0,0 @@ -local bosstiaryOnKill = CreatureEvent("BosstiaryOnKill") -function bosstiaryOnKill.onKill(player, creature, lastHit) - if not player:isPlayer() or not creature:isMonster() or creature:hasBeenSummoned() or creature:isPlayer() then - return true - end - - local mType = MonsterType(creature:getName()) - if not mType then - logger.error("[bosstiaryOnKill.onKill] monster with name {} have wrong MonsterType", creature:getName()) - return true - end - - if mType:bossRace() == nil or mType:bossRace() == "" then - return true - end - - local bosstiaryMultiplier = (configManager.getNumber(configKeys.BOSSTIARY_KILL_MULTIPLIER) or 1) - local killBonus = (configManager.getNumber(configKeys.BOOSTED_BOSS_KILL_BONUS) or 3) - for cid, damage in pairs(creature:getDamageMap()) do - local participant = Player(cid) - if participant and participant:isPlayer() then - if creature:getName():lower() == (Game.getBoostedBoss()):lower() then - participant:addBosstiaryKill(creature:getName(), bosstiaryMultiplier * killBonus) - else - participant:addBosstiaryKill(creature:getName(), bosstiaryMultiplier) - end - end - end - - return true -end - -bosstiaryOnKill:register() diff --git a/schema.sql b/schema.sql index dc58d3a71ac..3ba4e35effc 100644 --- a/schema.sql +++ b/schema.sql @@ -129,8 +129,8 @@ CREATE TABLE IF NOT EXISTS `players` ( `skill_manaleech_amount` bigint(20) UNSIGNED NOT NULL DEFAULT '0', `manashield` SMALLINT UNSIGNED NOT NULL DEFAULT '0', `max_manashield` SMALLINT UNSIGNED NOT NULL DEFAULT '0', - `xpboost_stamina` smallint(5) DEFAULT NULL, - `xpboost_value` tinyint(4) DEFAULT NULL, + `xpboost_stamina` smallint(5) UNSIGNED DEFAULT NULL, + `xpboost_value` tinyint(4) UNSIGNED DEFAULT NULL, `marriage_status` bigint(20) UNSIGNED NOT NULL DEFAULT '0', `marriage_spouse` int(11) NOT NULL DEFAULT '-1', `bonus_rerolls` bigint(21) NOT NULL DEFAULT '0', diff --git a/src/creatures/combat/combat.cpp b/src/creatures/combat/combat.cpp index c398f07a2b2..38f2c5de217 100644 --- a/src/creatures/combat/combat.cpp +++ b/src/creatures/combat/combat.cpp @@ -898,7 +898,7 @@ void Combat::addDistanceEffect(std::shared_ptr caster, const Position void Combat::doChainEffect(const Position &origin, const Position &dest, uint8_t effect) { if (effect > 0) { - std::forward_list dirList; + stdext::arraylist dirList(128); FindPathParams fpp; fpp.minTargetDist = 0; fpp.maxTargetDist = 1; diff --git a/src/creatures/combat/condition.cpp b/src/creatures/combat/condition.cpp index f69488175bd..4cf1834e1a4 100644 --- a/src/creatures/combat/condition.cpp +++ b/src/creatures/combat/condition.cpp @@ -1929,7 +1929,7 @@ bool ConditionFeared::getFleeDirection(std::shared_ptr creature) { return false; } -bool ConditionFeared::getFleePath(std::shared_ptr creature, const Position &pos, std::forward_list &dirList) { +bool ConditionFeared::getFleePath(std::shared_ptr creature, const Position &pos, stdext::arraylist &dirList) { const std::vector walkSize { 15, 9, 3, 1 }; bool found = false; std::ptrdiff_t found_size = 0; @@ -2030,7 +2030,7 @@ bool ConditionFeared::startCondition(std::shared_ptr creature) { bool ConditionFeared::executeCondition(std::shared_ptr creature, int32_t interval) { Position currentPos = creature->getPosition(); - std::forward_list listDir; + stdext::arraylist listDir(128); g_logger().debug("[ConditionFeared::executeCondition] Executing condition, current position is {}", currentPos.toString()); @@ -2040,7 +2040,7 @@ bool ConditionFeared::executeCondition(std::shared_ptr creature, int32 } if (getFleePath(creature, currentPos, listDir)) { - g_dispatcher().addEvent(std::bind(&Game::forcePlayerAutoWalk, &g_game(), creature->getID(), listDir), "ConditionFeared::executeCondition"); + g_dispatcher().addEvent(std::bind(&Game::forcePlayerAutoWalk, &g_game(), creature->getID(), listDir.data()), "ConditionFeared::executeCondition"); g_logger().debug("[ConditionFeared::executeCondition] Walking Scheduled"); } } diff --git a/src/creatures/combat/condition.hpp b/src/creatures/combat/condition.hpp index f8228e074ca..28d637b8b87 100644 --- a/src/creatures/combat/condition.hpp +++ b/src/creatures/combat/condition.hpp @@ -333,7 +333,7 @@ class ConditionFeared final : public Condition { private: bool canWalkTo(std::shared_ptr creature, Position pos, Direction moveDirection) const; bool getFleeDirection(std::shared_ptr creature); - bool getFleePath(std::shared_ptr creature, const Position &pos, std::forward_list &dirList); + bool getFleePath(std::shared_ptr creature, const Position &pos, stdext::arraylist &dirList); bool getRandomDirection(std::shared_ptr creature, Position pos); bool isStuck(std::shared_ptr creature, Position pos) const; diff --git a/src/creatures/creature.cpp b/src/creatures/creature.cpp index 2f855c6ac15..7058753acf1 100644 --- a/src/creatures/creature.cpp +++ b/src/creatures/creature.cpp @@ -133,16 +133,23 @@ void Creature::onThink(uint32_t interval) { } } + auto onThink = [self = getCreature(), interval] { + // scripting event - onThink + const auto &thinkEvents = self->getCreatureEvents(CREATURE_EVENT_THINK); + for (const auto creatureEventPtr : thinkEvents) { + creatureEventPtr->executeOnThink(self->static_self_cast(), interval); + } + }; + if (isUpdatingPath) { - isUpdatingPath = false; - goToFollowCreature(); + g_dispatcher().asyncEvent([self = getCreature(), onThink = std::move(onThink)] { + self->isUpdatingPath = false; + self->goToFollowCreature_async(onThink); + }); + return; } - // scripting event - onThink - const CreatureEventList &thinkEvents = getCreatureEvents(CREATURE_EVENT_THINK); - for (const auto creatureEventPtr : thinkEvents) { - creatureEventPtr->executeOnThink(static_self_cast(), interval); - } + onThink(); } void Creature::onAttacking(uint32_t interval) { @@ -224,18 +231,20 @@ bool Creature::getNextStep(Direction &dir, uint32_t &) { return true; } -void Creature::startAutoWalk(const std::forward_list &listDir, bool ignoreConditions /* = false*/) { +void Creature::startAutoWalk(const std::vector &listDir, bool ignoreConditions /* = false*/) { + listWalkDir.clear(); + if (!ignoreConditions && (hasCondition(CONDITION_ROOTED) || hasCondition(CONDITION_FEARED))) { return; } - listWalkDir = listDir; + listWalkDir = { listDir.begin(), listDir.end() }; - size_t size = 0; - for (auto it = listDir.begin(); it != listDir.end() && size <= 1; ++it) { - size++; + if (listWalkDir.empty()) { + return; } - addEventWalk(size == 1); + + addEventWalk(listWalkDir.size() == 1); } void Creature::addEventWalk(bool firstStep) { @@ -249,20 +258,22 @@ void Creature::addEventWalk(bool firstStep) { return; } - int64_t ticks = getEventStepTicks(firstStep); + const int64_t ticks = getEventStepTicks(firstStep); if (ticks <= 0) { return; } - // Take first step right away, but still queue the next - if (ticks == 1) { - g_game().checkCreatureWalk(getID()); - } + g_dispatcher().context().tryAddEvent([ticks, self = getCreature()]() { + // Take first step right away, but still queue the next + if (ticks == 1) { + g_game().checkCreatureWalk(self->getID()); + } - eventWalk = g_dispatcher().scheduleEvent( - static_cast(ticks), std::bind(&Game::checkCreatureWalk, &g_game(), getID()), - "Creature::checkCreatureWalk" - ); + self->eventWalk = g_dispatcher().scheduleEvent( + static_cast(ticks), std::bind(&Game::checkCreatureWalk, &g_game(), self->getID()), + "Creature::checkCreatureWalk" + ); + }); } void Creature::stopEventWalk() { @@ -320,11 +331,7 @@ int32_t Creature::getWalkCache(const Position &pos) { if (std::abs(dx) <= maxWalkCacheWidth) { int32_t dy = Position::getOffsetY(pos, myPos); if (std::abs(dy) <= maxWalkCacheHeight) { - if (localMapCache[maxWalkCacheHeight + dy][maxWalkCacheWidth + dx]) { - return 1; - } else { - return 0; - } + return localMapCache[maxWalkCacheHeight + dy][maxWalkCacheWidth + dx]; } } @@ -464,7 +471,7 @@ void Creature::checkSummonMove(const Position &newPos, bool teleportSummon) { } } -void Creature::onCreatureMove(std::shared_ptr creature, std::shared_ptr newTile, const Position &newPos, std::shared_ptr oldTile, const Position &oldPos, bool teleport) { +void Creature::onCreatureMove(const std::shared_ptr &creature, const std::shared_ptr &newTile, const Position &newPos, const std::shared_ptr &oldTile, const Position &oldPos, bool teleport) { if (creature == getCreature()) { lastStep = OTSYS_TIME(); lastStepCost = 1; @@ -512,7 +519,7 @@ void Creature::onCreatureMove(std::shared_ptr creature, std::shared_pt // update 0 for (int32_t x = -maxWalkCacheWidth; x <= maxWalkCacheWidth; ++x) { - std::shared_ptr cacheTile = g_game().map.getTile(static_cast(myPos.getX() + x), static_cast(myPos.getY() - maxWalkCacheHeight), myPos.z); + const auto &cacheTile = g_game().map.getTile(static_cast(myPos.getX() + x), static_cast(myPos.getY() - maxWalkCacheHeight), myPos.z); updateTileCache(cacheTile, x, -maxWalkCacheHeight); } } else if (oldPos.y < newPos.y) { // south @@ -523,7 +530,7 @@ void Creature::onCreatureMove(std::shared_ptr creature, std::shared_pt // update mapWalkHeight - 1 for (int32_t x = -maxWalkCacheWidth; x <= maxWalkCacheWidth; ++x) { - std::shared_ptr cacheTile = g_game().map.getTile(static_cast(myPos.getX() + x), static_cast(myPos.getY() + maxWalkCacheHeight), myPos.z); + const auto &cacheTile = g_game().map.getTile(static_cast(myPos.getX() + x), static_cast(myPos.getY() + maxWalkCacheHeight), myPos.z); updateTileCache(cacheTile, x, maxWalkCacheHeight); } } @@ -548,7 +555,7 @@ void Creature::onCreatureMove(std::shared_ptr creature, std::shared_pt // update mapWalkWidth - 1 for (int32_t y = -maxWalkCacheHeight; y <= maxWalkCacheHeight; ++y) { - std::shared_ptr cacheTile = g_game().map.getTile(myPos.x + maxWalkCacheWidth, static_cast(myPos.y + y), myPos.z); + const auto &cacheTile = g_game().map.getTile(myPos.x + maxWalkCacheWidth, static_cast(myPos.y + y), myPos.z); updateTileCache(cacheTile, maxWalkCacheWidth, y); } } else if (oldPos.x > newPos.x) { // west @@ -593,7 +600,7 @@ void Creature::onCreatureMove(std::shared_ptr creature, std::shared_pt } } - auto followCreature = getFollowCreature(); + const auto &followCreature = getFollowCreature(); if (followCreature && (creature == getCreature() || creature == followCreature)) { if (hasFollowPath) { isUpdatingPath = true; @@ -605,7 +612,7 @@ void Creature::onCreatureMove(std::shared_ptr creature, std::shared_pt } } - auto attackedCreature = getAttackedCreature(); + const auto &attackedCreature = getAttackedCreature(); if (attackedCreature && (creature == attackedCreature || creature == getCreature())) { if (newPos.z != oldPos.z || !canSee(attackedCreature->getPosition())) { onCreatureDisappear(attackedCreature, false); @@ -625,14 +632,6 @@ void Creature::onCreatureMove(std::shared_ptr creature, std::shared_pt void Creature::onDeath() { bool lastHitUnjustified = false; bool mostDamageUnjustified = false; - std::shared_ptr lastHitCreature = g_game().getCreatureByID(lastHitCreatureId); - std::shared_ptr lastHitCreatureMaster; - if (lastHitCreature) { - lastHitUnjustified = lastHitCreature->onKilledCreature(static_self_cast(), true); - lastHitCreatureMaster = lastHitCreature->getMaster(); - } else { - lastHitCreatureMaster = nullptr; - } std::shared_ptr mostDamageCreature = nullptr; @@ -674,13 +673,15 @@ void Creature::onDeath() { it.first->onGainExperience(it.second, getCreature()); } + std::shared_ptr mostDamageCreatureMaster = nullptr; if (mostDamageCreature) { - if (mostDamageCreature != lastHitCreature && mostDamageCreature != lastHitCreatureMaster) { - auto mostDamageCreatureMaster = mostDamageCreature->getMaster(); - if (lastHitCreature != mostDamageCreatureMaster && (lastHitCreatureMaster == nullptr || mostDamageCreatureMaster != lastHitCreatureMaster)) { - mostDamageUnjustified = mostDamageCreature->onKilledCreature(static_self_cast(), false); - } - } + mostDamageCreatureMaster = mostDamageCreature->getMaster(); + mostDamageUnjustified = mostDamageCreature->onKilledCreature(getCreature(), false); + } + + std::shared_ptr lastHitCreature = g_game().getCreatureByID(lastHitCreatureId); + if (lastHitCreature && lastHitCreature != mostDamageCreature && lastHitCreature != mostDamageCreatureMaster) { + lastHitUnjustified = lastHitCreature->onKilledCreature(getCreature(), true); } bool droppedCorpse = dropCorpse(lastHitCreature, mostDamageCreature, lastHitUnjustified, mostDamageUnjustified); @@ -954,7 +955,7 @@ bool Creature::setAttackedCreature(std::shared_ptr creature) { return true; } -void Creature::getPathSearchParams(std::shared_ptr, FindPathParams &fpp) { +void Creature::getPathSearchParams(const std::shared_ptr &, FindPathParams &fpp) { fpp.fullPathSearch = !hasFollowPath; fpp.clearSight = true; fpp.maxSearchDist = 12; @@ -962,55 +963,64 @@ void Creature::getPathSearchParams(std::shared_ptr, FindPathParams &fp fpp.maxTargetDist = 1; } +void Creature::goToFollowCreature_async(std::function &&onComplete) { + if (pathfinderRunning.load()) { + return; + } + + pathfinderRunning.store(true); + g_dispatcher().asyncEvent([self = getCreature()] { + self->goToFollowCreature(); + self->pathfinderRunning.store(false); + }); + + if (onComplete) { + g_dispatcher().context().addEvent(std::move(onComplete)); + } +} + void Creature::goToFollowCreature() { - auto followCreature = getFollowCreature(); - if (followCreature) { - if (isSummon() && !getMonster()->isFamiliar() && !canFollowMaster()) { - hasFollowPath = false; - return; - } + const auto &followCreature = getFollowCreature(); + if (!followCreature) { + return; + } - FindPathParams fpp; - getPathSearchParams(followCreature, fpp); - std::shared_ptr monster = getMonster(); - if (monster && !monster->getMaster() && (monster->isFleeing() || fpp.maxTargetDist > 1)) { - Direction dir = DIRECTION_NONE; - - if (monster->isFleeing()) { - monster->getDistanceStep(followCreature->getPosition(), dir, true); - } else { // maxTargetDist > 1 - if (!monster->getDistanceStep(followCreature->getPosition(), dir)) { - // if we can't get anything then let the A* calculate - listWalkDir.clear(); - if (getPathTo(followCreature->getPosition(), listWalkDir, fpp)) { - hasFollowPath = true; - startAutoWalk(listWalkDir); - } else { - hasFollowPath = false; - } - return; - } - } + const auto &monster = getMonster(); + + if (isSummon() && !monster->isFamiliar() && !canFollowMaster()) { + listWalkDir.clear(); + return; + } - if (dir != DIRECTION_NONE) { - listWalkDir.clear(); - listWalkDir.push_front(dir); + bool executeOnFollow = true; + stdext::arraylist listDir(128); - hasFollowPath = true; - startAutoWalk(listWalkDir); - } - } else { - listWalkDir.clear(); - if (getPathTo(followCreature->getPosition(), listWalkDir, fpp)) { - hasFollowPath = true; - startAutoWalk(listWalkDir); - } else { - hasFollowPath = false; - } + FindPathParams fpp; + getPathSearchParams(followCreature, fpp); + + if (monster && !monster->getMaster() && (monster->isFleeing() || fpp.maxTargetDist > 1)) { + Direction dir = DIRECTION_NONE; + + if (monster->isFleeing()) { + monster->getDistanceStep(followCreature->getPosition(), dir, true); + } else if (!monster->getDistanceStep(followCreature->getPosition(), dir)) { // maxTargetDist > 1 + // if we can't get anything then let the A* calculate + executeOnFollow = false; + } else if (dir != DIRECTION_NONE) { + listDir.push_back(dir); + hasFollowPath = true; } } - onFollowCreatureComplete(followCreature); + if (listDir.empty()) { + hasFollowPath = getPathTo(getFollowCreature()->getPosition(), listDir, fpp); + } + + startAutoWalk(listDir.data()); + + if (executeOnFollow) { + onFollowCreatureComplete(getFollowCreature()); + } } bool Creature::canFollowMaster() { @@ -1254,7 +1264,6 @@ bool Creature::addCondition(std::shared_ptr condition) { std::shared_ptr prevCond = getCondition(condition->getType(), condition->getId(), condition->getSubId()); if (prevCond) { prevCond->addCondition(getCreature(), condition); - return true; } @@ -1644,11 +1653,11 @@ bool Creature::isInvisible() const { != conditions.end(); } -bool Creature::getPathTo(const Position &targetPos, std::forward_list &dirList, const FindPathParams &fpp) { +bool Creature::getPathTo(const Position &targetPos, stdext::arraylist &dirList, const FindPathParams &fpp) { return g_game().map.getPathMatching(getCreature(), dirList, FrozenPathingConditionCall(targetPos), fpp); } -bool Creature::getPathTo(const Position &targetPos, std::forward_list &dirList, int32_t minTargetDist, int32_t maxTargetDist, bool fullPathSearch /*= true*/, bool clearSight /*= true*/, int32_t maxSearchDist /*= 7*/) { +bool Creature::getPathTo(const Position &targetPos, stdext::arraylist &dirList, int32_t minTargetDist, int32_t maxTargetDist, bool fullPathSearch /*= true*/, bool clearSight /*= true*/, int32_t maxSearchDist /*= 7*/) { FindPathParams fpp; fpp.fullPathSearch = fullPathSearch; fpp.maxSearchDist = maxSearchDist; diff --git a/src/creatures/creature.hpp b/src/creatures/creature.hpp index 0195ec17c30..5e03e33d703 100644 --- a/src/creatures/creature.hpp +++ b/src/creatures/creature.hpp @@ -43,6 +43,10 @@ class FrozenPathingConditionCall { bool isInRange(const Position &startPos, const Position &testPos, const FindPathParams &fpp) const; + Position getTargetPos() const { + return targetPos; + } + private: Position targetPos; }; @@ -266,9 +270,11 @@ class Creature : virtual public Thing, public SharedObject { phmap::flat_hash_set> getZones(); // walk functions - void startAutoWalk(const std::forward_list &listDir, bool ignoreConditions = false); + void startAutoWalk(const std::vector &listDir, bool ignoreConditions = false); void addEventWalk(bool firstStep = false); void stopEventWalk(); + + void goToFollowCreature_async(std::function &&onComplete = nullptr); virtual void goToFollowCreature(); // walk events @@ -283,8 +289,12 @@ class Creature : virtual public Thing, public SharedObject { virtual bool setFollowCreature(std::shared_ptr creature); // follow events - virtual void onFollowCreature(std::shared_ptr) { } - virtual void onFollowCreatureComplete(std::shared_ptr) { } + virtual void onFollowCreature(const std::shared_ptr &) { + /* empty */ + } + virtual void onFollowCreatureComplete(const std::shared_ptr &) { + /* empty */ + } // combat functions std::shared_ptr getAttackedCreature() { @@ -446,7 +456,7 @@ class Creature : virtual public Thing, public SharedObject { * @return false */ void checkSummonMove(const Position &newPos, bool teleportSummon = false); - virtual void onCreatureMove(std::shared_ptr creature, std::shared_ptr newTile, const Position &newPos, std::shared_ptr oldTile, const Position &oldPos, bool teleport); + virtual void onCreatureMove(const std::shared_ptr &creature, const std::shared_ptr &newTile, const Position &newPos, const std::shared_ptr &oldTile, const Position &oldPos, bool teleport); virtual void onAttackedCreatureDisappear(bool) { } virtual void onFollowCreatureDisappear(bool) { } @@ -524,8 +534,8 @@ class Creature : virtual public Thing, public SharedObject { double getDamageRatio(std::shared_ptr attacker) const; - bool getPathTo(const Position &targetPos, std::forward_list &dirList, const FindPathParams &fpp); - bool getPathTo(const Position &targetPos, std::forward_list &dirList, int32_t minTargetDist, int32_t maxTargetDist, bool fullPathSearch = true, bool clearSight = true, int32_t maxSearchDist = 7); + bool getPathTo(const Position &targetPos, stdext::arraylist &dirList, const FindPathParams &fpp); + bool getPathTo(const Position &targetPos, stdext::arraylist &dirList, int32_t minTargetDist, int32_t maxTargetDist, bool fullPathSearch = true, bool clearSight = true, int32_t maxSearchDist = 7); struct CountBlock_t { int32_t total; @@ -660,7 +670,7 @@ class Creature : virtual public Thing, public SharedObject { CreatureEventList eventsList; ConditionList conditions; - std::forward_list listWalkDir; + std::deque listWalkDir; std::weak_ptr m_tile; std::weak_ptr m_attackedCreature; @@ -721,16 +731,18 @@ class Creature : virtual public Thing, public SharedObject { bool skillLoss = true; bool lootDrop = true; bool cancelNextWalk = false; - bool hasFollowPath = false; bool forceUpdateFollowPath = false; bool hiddenHealth = false; bool floorChange = false; bool canUseDefense = true; bool moveLocked = false; + bool hasFollowPath = false; int8_t charmChanceModifier = 0; uint8_t wheelOfDestinyDrainBodyDebuff = 0; + std::atomic_bool pathfinderRunning = false; + // use map here instead of phmap to keep the keys in a predictable order std::map creatureIcons = {}; @@ -756,7 +768,7 @@ class Creature : virtual public Thing, public SharedObject { virtual uint16_t getLookCorpse() const { return 0; } - virtual void getPathSearchParams(std::shared_ptr creature, FindPathParams &fpp); + virtual void getPathSearchParams(const std::shared_ptr &, FindPathParams &fpp); virtual void death(std::shared_ptr) { } virtual bool dropCorpse(std::shared_ptr lastHitCreature, std::shared_ptr mostDamageCreature, bool lastHitUnjustified, bool mostDamageUnjustified); virtual std::shared_ptr getCorpse(std::shared_ptr lastHitCreature, std::shared_ptr mostDamageCreature); @@ -769,4 +781,5 @@ class Creature : virtual public Thing, public SharedObject { bool canFollowMaster(); bool isLostSummon(); void handleLostSummon(bool teleportSummons); + void executeAsyncPathTo(bool executeOnFollow, FindPathParams &fpp, std::function &&onComplete); }; diff --git a/src/creatures/monsters/monster.cpp b/src/creatures/monsters/monster.cpp index 9bef75a5ecb..97c18e6d855 100644 --- a/src/creatures/monsters/monster.cpp +++ b/src/creatures/monsters/monster.cpp @@ -178,7 +178,7 @@ void Monster::onRemoveCreature(std::shared_ptr creature, bool isLogout } } -void Monster::onCreatureMove(std::shared_ptr creature, std::shared_ptr newTile, const Position &newPos, std::shared_ptr oldTile, const Position &oldPos, bool teleport) { +void Monster::onCreatureMove(const std::shared_ptr &creature, const std::shared_ptr &newTile, const Position &newPos, const std::shared_ptr &oldTile, const Position &oldPos, bool teleport) { Creature::onCreatureMove(creature, newTile, newPos, oldTile, oldPos, teleport); if (mType->info.creatureMoveEvent != -1) { @@ -596,16 +596,17 @@ bool Monster::searchTarget(TargetSearchType_t searchType /*= TARGETSEARCH_DEFAUL return false; } -void Monster::onFollowCreatureComplete(std::shared_ptr creature) { +void Monster::onFollowCreatureComplete(const std::shared_ptr &creature) { if (!creature) { return; } + auto it = std::find(targetIDList.begin(), targetIDList.end(), creature->getID()); - if (it != targetIDList.end()) { - auto target = targetListMap[*it].lock(); - if (!target) { - return; - } + if (it == targetIDList.end()) { + return; + } + + if (const auto &target = targetListMap[*it].lock()) { targetIDList.erase(it); if (hasFollowPath) { @@ -870,7 +871,7 @@ void Monster::doAttacking(uint32_t interval) { } } -bool Monster::canUseAttack(const Position &pos, std::shared_ptr target) const { +bool Monster::canUseAttack(const Position &pos, const std::shared_ptr &target) const { if (isHostile()) { const Position &targetPos = target->getPosition(); uint32_t distance = std::max(Position::getDistanceX(pos, targetPos), Position::getDistanceY(pos, targetPos)); @@ -2066,7 +2067,7 @@ bool Monster::isImmune(CombatType_t combatType) const { return mType->info.m_damageImmunities[combatTypeToIndex(combatType)]; } -void Monster::getPathSearchParams(std::shared_ptr creature, FindPathParams &fpp) { +void Monster::getPathSearchParams(const std::shared_ptr &creature, FindPathParams &fpp) { Creature::getPathSearchParams(creature, fpp); fpp.minTargetDist = 1; diff --git a/src/creatures/monsters/monster.hpp b/src/creatures/monsters/monster.hpp index adcb0258503..e6178a4f77c 100644 --- a/src/creatures/monsters/monster.hpp +++ b/src/creatures/monsters/monster.hpp @@ -147,13 +147,13 @@ class Monster final : public Creature { void onCreatureAppear(std::shared_ptr creature, bool isLogin) override; void onRemoveCreature(std::shared_ptr creature, bool isLogout) override; - void onCreatureMove(std::shared_ptr creature, std::shared_ptr newTile, const Position &newPos, std::shared_ptr oldTile, const Position &oldPos, bool teleport) override; + void onCreatureMove(const std::shared_ptr &creature, const std::shared_ptr &newTile, const Position &newPos, const std::shared_ptr &oldTile, const Position &oldPos, bool teleport) override; void onCreatureSay(std::shared_ptr creature, SpeakClasses type, const std::string &text) override; void drainHealth(std::shared_ptr attacker, int32_t damage) override; void changeHealth(int32_t healthChange, bool sendHealthChange = true) override; bool getNextStep(Direction &direction, uint32_t &flags) override; - void onFollowCreatureComplete(std::shared_ptr creature) override; + void onFollowCreatureComplete(const std::shared_ptr &creature) override; void onThink(uint32_t interval) override; @@ -407,7 +407,7 @@ class Monster final : public Creature { void onAddCondition(ConditionType_t type) override; void onEndCondition(ConditionType_t type) override; - bool canUseAttack(const Position &pos, std::shared_ptr target) const; + bool canUseAttack(const Position &pos, const std::shared_ptr &target) const; bool canUseSpell(const Position &pos, const Position &targetPos, const spellBlock_t &sb, uint32_t interval, bool &inRange, bool &resetTicks); bool getRandomStep(const Position &creaturePos, Direction &direction); bool getDanceStep(const Position &creaturePos, Direction &direction, bool keepAttack = true, bool keepDistance = true); @@ -434,7 +434,7 @@ class Monster final : public Creature { return mType->info.lookcorpse; } void dropLoot(std::shared_ptr corpse, std::shared_ptr lastHitCreature) override; - void getPathSearchParams(std::shared_ptr creature, FindPathParams &fpp) override; + void getPathSearchParams(const std::shared_ptr &creature, FindPathParams &fpp) override; bool useCacheMap() const override { return !randomStepping; } diff --git a/src/creatures/monsters/monsters.hpp b/src/creatures/monsters/monsters.hpp index fd911c661f9..e0cc79b88e9 100644 --- a/src/creatures/monsters/monsters.hpp +++ b/src/creatures/monsters/monsters.hpp @@ -185,15 +185,19 @@ class MonsterType { } float getHealthMultiplier() const { - return info.bosstiaryClass.empty() ? g_configManager().getFloat(RATE_MONSTER_HEALTH) : g_configManager().getFloat(RATE_BOSS_HEALTH); + return isBoss() ? g_configManager().getFloat(RATE_BOSS_HEALTH) : g_configManager().getFloat(RATE_MONSTER_HEALTH); } float getAttackMultiplier() const { - return info.bosstiaryClass.empty() ? g_configManager().getFloat(RATE_MONSTER_ATTACK) : g_configManager().getFloat(RATE_BOSS_ATTACK); + return isBoss() ? g_configManager().getFloat(RATE_BOSS_ATTACK) : g_configManager().getFloat(RATE_MONSTER_ATTACK); } float getDefenseMultiplier() const { - return info.bosstiaryClass.empty() ? g_configManager().getFloat(RATE_MONSTER_DEFENSE) : g_configManager().getFloat(RATE_BOSS_DEFENSE); + return isBoss() ? g_configManager().getFloat(RATE_BOSS_DEFENSE) : g_configManager().getFloat(RATE_MONSTER_DEFENSE); + } + + bool isBoss() const { + return !info.bosstiaryClass.empty(); } void loadLoot(const std::shared_ptr monsterType, LootBlock lootblock); diff --git a/src/creatures/npcs/npc.cpp b/src/creatures/npcs/npc.cpp index 89eabdae28b..2df46c285cb 100644 --- a/src/creatures/npcs/npc.cpp +++ b/src/creatures/npcs/npc.cpp @@ -113,7 +113,7 @@ void Npc::onRemoveCreature(std::shared_ptr creature, bool isLogout) { shopPlayerMap.clear(); } -void Npc::onCreatureMove(std::shared_ptr creature, std::shared_ptr newTile, const Position &newPos, std::shared_ptr oldTile, const Position &oldPos, bool teleport) { +void Npc::onCreatureMove(const std::shared_ptr &creature, const std::shared_ptr &newTile, const Position &newPos, const std::shared_ptr &oldTile, const Position &oldPos, bool teleport) { Creature::onCreatureMove(creature, newTile, newPos, oldTile, oldPos, teleport); // onCreatureMove(self, creature, oldPosition, newPosition) @@ -134,7 +134,7 @@ void Npc::onCreatureMove(std::shared_ptr creature, std::shared_ptrgetPlayer()) { + if (const auto &player = creature->getPlayer()) { handlePlayerMove(player, newPos); } } diff --git a/src/creatures/npcs/npc.hpp b/src/creatures/npcs/npc.hpp index 27f46cbeec6..38cd9840a17 100644 --- a/src/creatures/npcs/npc.hpp +++ b/src/creatures/npcs/npc.hpp @@ -137,7 +137,7 @@ class Npc final : public Creature { void onCreatureAppear(std::shared_ptr creature, bool isLogin) override; void onRemoveCreature(std::shared_ptr creature, bool isLogout) override; - void onCreatureMove(std::shared_ptr creature, std::shared_ptr newTile, const Position &newPos, std::shared_ptr oldTile, const Position &oldPos, bool teleport) override; + void onCreatureMove(const std::shared_ptr &creature, const std::shared_ptr &newTile, const Position &newPos, const std::shared_ptr &oldTile, const Position &oldPos, bool teleport) override; void onCreatureSay(std::shared_ptr creature, SpeakClasses type, const std::string &text) override; void onThink(uint32_t interval) override; void onPlayerBuyItem(std::shared_ptr player, uint16_t itemid, uint8_t count, uint16_t amount, bool ignore, bool inBackpacks); diff --git a/src/creatures/players/grouping/party.hpp b/src/creatures/players/grouping/party.hpp index e7bc2c2fc55..d4c0cea0b35 100644 --- a/src/creatures/players/grouping/party.hpp +++ b/src/creatures/players/grouping/party.hpp @@ -35,6 +35,14 @@ class Party : public SharedObject { std::shared_ptr getLeader() const { return m_leader.lock(); } + std::vector> getPlayers() const { + std::vector> players; + for (auto &member : memberList) { + players.push_back(member); + } + players.push_back(getLeader()); + return players; + } std::vector> getMembers() { return memberList; } diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index 1f7f9f088b0..1178a666e1c 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -1830,10 +1830,10 @@ void Player::onWalk(Direction &dir) { setNextAction(OTSYS_TIME() + getStepDuration(dir)); } -void Player::onCreatureMove(std::shared_ptr creature, std::shared_ptr newTile, const Position &newPos, std::shared_ptr oldTile, const Position &oldPos, bool teleport) { +void Player::onCreatureMove(const std::shared_ptr &creature, const std::shared_ptr &newTile, const Position &newPos, const std::shared_ptr &oldTile, const Position &oldPos, bool teleport) { Creature::onCreatureMove(creature, newTile, newPos, oldTile, oldPos, teleport); - auto followCreature = getFollowCreature(); + const auto &followCreature = getFollowCreature(); if (hasFollowPath && (creature == followCreature || (creature.get() == this && followCreature))) { isUpdatingPath = false; g_dispatcher().addEvent(std::bind(&Game::updateCreatureWalk, &g_game(), getID()), "Game::updateCreatureWalk"); @@ -1850,7 +1850,7 @@ void Player::onCreatureMove(std::shared_ptr creature, std::shared_ptr< } if (tradePartner && !Position::areInRange<2, 2, 0>(tradePartner->getPosition(), getPosition())) { - g_game().internalCloseTrade(static_self_cast()); + g_game().internalCloseTrade(getPlayer()); } } @@ -1873,13 +1873,13 @@ void Player::onCreatureMove(std::shared_ptr creature, std::shared_ptr< if (party) { party->updateSharedExperience(); - party->updatePlayerStatus(static_self_cast(), oldPos, newPos); + party->updatePlayerStatus(getPlayer(), oldPos, newPos); } if (teleport || oldPos.z != newPos.z) { int32_t ticks = g_configManager().getNumber(STAIRHOP_DELAY); if (ticks > 0) { - if (std::shared_ptr condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_PACIFIED, ticks, 0)) { + if (const auto &condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_PACIFIED, ticks, 0)) { addCondition(condition); } } @@ -4223,7 +4223,7 @@ void Player::goToFollowCreature() { } } -void Player::getPathSearchParams(std::shared_ptr creature, FindPathParams &fpp) { +void Player::getPathSearchParams(const std::shared_ptr &creature, FindPathParams &fpp) { Creature::getPathSearchParams(creature, fpp); fpp.fullPathSearch = true; } @@ -4287,7 +4287,7 @@ uint64_t Player::getGainedExperience(std::shared_ptr attacker) const { return 0; } -void Player::onFollowCreature(std::shared_ptr creature) { +void Player::onFollowCreature(const std::shared_ptr &creature) { if (!creature) { stopWalk(); } @@ -4583,75 +4583,104 @@ void Player::onTargetCreatureGainHealth(std::shared_ptr target, int32_ } } -bool Player::onKilledCreature(std::shared_ptr target, bool lastHit /* = true*/) { +bool Player::onKilledPlayer(const std::shared_ptr &target, bool lastHit) { bool unjustified = false; - - if (hasFlag(PlayerFlags_t::NotGenerateLoot)) { + if (target->getZoneType() == ZONE_PVP) { target->setDropLoot(false); - } - - Creature::onKilledCreature(target, lastHit); - - if (auto targetPlayer = target->getPlayer()) { - if (targetPlayer && targetPlayer->getZoneType() == ZONE_PVP) { - targetPlayer->setDropLoot(false); - targetPlayer->setSkillLoss(false); - } else if (!hasFlag(PlayerFlags_t::NotGainInFight) && !isPartner(targetPlayer)) { - if (!Combat::isInPvpZone(getPlayer(), targetPlayer) && hasAttacked(targetPlayer) && !targetPlayer->hasAttacked(getPlayer()) && !isGuildMate(targetPlayer) && targetPlayer != getPlayer()) { - if (targetPlayer->hasKilled(getPlayer())) { - for (auto &kill : targetPlayer->unjustifiedKills) { - if (kill.target == getGUID() && kill.unavenged) { - kill.unavenged = false; - auto it = attackedSet.find(targetPlayer->guid); - attackedSet.erase(it); - break; - } + target->setSkillLoss(false); + } else if (!hasFlag(PlayerFlags_t::NotGainInFight) && !isPartner(target)) { + if (!Combat::isInPvpZone(getPlayer(), target) && hasAttacked(target) && !target->hasAttacked(getPlayer()) && !isGuildMate(target) && target != getPlayer()) { + if (target->hasKilled(getPlayer())) { + for (auto &kill : target->unjustifiedKills) { + if (kill.target == getGUID() && kill.unavenged) { + kill.unavenged = false; + auto it = attackedSet.find(target->guid); + attackedSet.erase(it); + break; } - } else if (targetPlayer->getSkull() == SKULL_NONE && !isInWar(targetPlayer)) { - unjustified = true; - addUnjustifiedDead(targetPlayer); - } - - if (lastHit && hasCondition(CONDITION_INFIGHT)) { - pzLocked = true; - std::shared_ptr condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_INFIGHT, g_configManager().getNumber(WHITE_SKULL_TIME), 0); - addCondition(condition); } - } - } - } else if (std::shared_ptr monster = target->getMonster()) { - // Access to the monster's map damage to check if the player attacked it - for (auto [playerId, damage] : monster->getDamageMap()) { - auto damagePlayer = g_game().getPlayerByID(playerId); - if (!damagePlayer) { - continue; + } else if (target->getSkull() == SKULL_NONE && !isInWar(target)) { + unjustified = true; + addUnjustifiedDead(target); } - // If the player is not in a party and sharing exp active and enabled - // And it's not the player killing the creature, then we ignore everything else - auto damageParty = damagePlayer->getParty(); - if (static_self_cast()->getID() != damagePlayer->getID() && (!damageParty || !damageParty->isSharedExperienceActive() || !damageParty->isSharedExperienceEnabled())) { - continue; + if (lastHit && hasCondition(CONDITION_INFIGHT)) { + pzLocked = true; + std::shared_ptr condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_INFIGHT, g_configManager().getNumber(WHITE_SKULL_TIME), 0); + addCondition(condition); } + } + } + return unjustified; +} - const auto &taskSlot = damagePlayer->getTaskHuntingWithCreature(monster->getRaceId()); - if (!taskSlot || monster->isSummon()) { - continue; - } +void Player::addHuntingTaskKill(const std::shared_ptr &mType) { + const auto &taskSlot = getTaskHuntingWithCreature(mType->info.raceid); + if (!taskSlot) { + return; + } - if (const auto &option = g_ioprey().getTaskRewardOption(taskSlot)) { - taskSlot->currentKills += 1; - if ((taskSlot->upgrade && taskSlot->currentKills >= option->secondKills) || (!taskSlot->upgrade && taskSlot->currentKills >= option->firstKills)) { - taskSlot->state = PreyTaskDataState_Completed; - std::string message = "You succesfully finished your hunting task. Your reward is ready to be claimed!"; - damagePlayer->sendTextMessage(MESSAGE_STATUS, message); - } - damagePlayer->reloadTaskSlot(taskSlot->id); - } + if (const auto &option = g_ioprey().getTaskRewardOption(taskSlot)) { + taskSlot->currentKills += 1; + if ((taskSlot->upgrade && taskSlot->currentKills >= option->secondKills) || (!taskSlot->upgrade && taskSlot->currentKills >= option->firstKills)) { + taskSlot->state = PreyTaskDataState_Completed; + std::string message = "You succesfully finished your hunting task. Your reward is ready to be claimed!"; + sendTextMessage(MESSAGE_STATUS, message); } + reloadTaskSlot(taskSlot->id); } +} - return unjustified; +void Player::addBestiaryKill(const std::shared_ptr &mType) { + if (mType->isBoss()) { + return; + } + uint32_t kills = g_configManager().getNumber(BESTIARY_KILL_MULTIPLIER); + if (isConcoctionActive(Concoction_t::BestiaryBetterment)) { + kills *= 2; + } + g_iobestiary().addBestiaryKill(getPlayer(), mType, kills); +} + +void Player::addBosstiaryKill(const std::shared_ptr &mType) { + if (!mType->isBoss()) { + return; + } + uint32_t kills = g_configManager().getNumber(BOSSTIARY_KILL_MULTIPLIER); + + g_ioBosstiary().addBosstiaryKill(getPlayer(), mType, kills); +} + +bool Player::onKilledMonster(const std::shared_ptr &monster, bool lastHit) { + if (lastHit || monster->isSummon()) { + return false; + } + auto party = getParty(); + auto participants = party && party->isSharedExperienceEnabled() && party->isSharedExperienceActive() ? party->getPlayers() : std::vector> { getPlayer() }; + auto mType = monster->getMonsterType(); + for (const auto &player : participants) { + player->addHuntingTaskKill(mType); + player->addBestiaryKill(mType); + player->addBosstiaryKill(mType); + } + + return false; +} + +bool Player::onKilledCreature(std::shared_ptr target, bool lastHit /* = true*/) { + if (hasFlag(PlayerFlags_t::NotGenerateLoot)) { + target->setDropLoot(false); + } + + Creature::onKilledCreature(target, lastHit); + + if (auto targetPlayer = target->getPlayer()) { + return onKilledPlayer(targetPlayer, lastHit); + } else if (auto targetMonster = target->getMonster()) { + return onKilledMonster(targetMonster, lastHit); + } + + return false; } void Player::gainExperience(uint64_t gainExp, std::shared_ptr target) { @@ -7529,9 +7558,9 @@ SoundEffect_t Player::getAttackSoundEffect() const { bool Player::canAutoWalk(const Position &toPosition, const std::function &function, uint32_t delay /* = 500*/) { if (!Position::areInRange<1, 1>(getPosition(), toPosition)) { // Check if can walk to the toPosition and send event to use function - std::forward_list listDir; + stdext::arraylist listDir(128); if (getPathTo(toPosition, listDir, 0, 1, true, true)) { - g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, &g_game(), getID(), listDir), __FUNCTION__); + g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, &g_game(), getID(), listDir.data()), __FUNCTION__); std::shared_ptr task = createPlayerTask(delay, function, __FUNCTION__); setNextWalkActionTask(task); diff --git a/src/creatures/players/player.hpp b/src/creatures/players/player.hpp index 791f696148c..2a32a838c36 100644 --- a/src/creatures/players/player.hpp +++ b/src/creatures/players/player.hpp @@ -815,7 +815,7 @@ class Player final : public Creature, public Cylinder, public Bankable { void goToFollowCreature() override; // follow events - void onFollowCreature(std::shared_ptr creature) override; + void onFollowCreature(const std::shared_ptr &) override; // walk events void onWalk(Direction &dir) override; @@ -1230,7 +1230,7 @@ class Player final : public Creature, public Cylinder, public Bankable { void onCreatureAppear(std::shared_ptr creature, bool isLogin) override; void onRemoveCreature(std::shared_ptr creature, bool isLogout) override; - void onCreatureMove(std::shared_ptr creature, std::shared_ptr newTile, const Position &newPos, std::shared_ptr oldTile, const Position &oldPos, bool teleport) override; + void onCreatureMove(const std::shared_ptr &creature, const std::shared_ptr &newTile, const Position &newPos, const std::shared_ptr &oldTile, const Position &oldPos, bool teleport) override; void onAttackedCreatureDisappear(bool isLogout) override; void onFollowCreatureDisappear(bool isLogout) override; @@ -1760,6 +1760,11 @@ class Player final : public Creature, public Cylinder, public Bankable { } void setExpBoostStamina(uint16_t stamina) { + // only allow stamina boosts of 12 hours or less + if (stamina > 12 * 3600) { + expBoostStamina = 12 * 3600; + return; + } expBoostStamina = stamina; } @@ -2487,6 +2492,14 @@ class Player final : public Creature, public Cylinder, public Bankable { std::map getActiveConcoctions() const { return activeConcoctions; } + bool isConcoctionActive(Concoction_t concotion) const { + uint16_t itemId = static_cast(concotion); + if (!activeConcoctions.contains(itemId)) { + return false; + } + auto timeLeft = activeConcoctions.at(itemId); + return timeLeft > 0; + } bool checkAutoLoot() const { const bool autoLoot = g_configManager().getBoolean(AUTOLOOT) && getStorageValue(STORAGEVALUE_AUTO_LOOT) != 0; @@ -2589,6 +2602,12 @@ class Player final : public Creature, public Cylinder, public Bankable { void internalAddThing(std::shared_ptr thing) override; void internalAddThing(uint32_t index, std::shared_ptr thing) override; + void addHuntingTaskKill(const std::shared_ptr &mType); + void addBestiaryKill(const std::shared_ptr &mType); + void addBosstiaryKill(const std::shared_ptr &mType); + bool onKilledPlayer(const std::shared_ptr &target, bool lastHit); + bool onKilledMonster(const std::shared_ptr &target, bool lastHit); + phmap::flat_hash_set attackedSet; phmap::flat_hash_set VIPList; @@ -2869,7 +2888,7 @@ class Player final : public Creature, public Cylinder, public Bankable { void addConditionSuppression(const std::array &addConditions); uint16_t getLookCorpse() const override; - void getPathSearchParams(std::shared_ptr creature, FindPathParams &fpp) override; + void getPathSearchParams(const std::shared_ptr &creature, FindPathParams &fpp) override; void setDead(bool isDead) { dead = isDead; diff --git a/src/game/game.cpp b/src/game/game.cpp index b3706b7bf38..417c89a8497 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -704,6 +704,7 @@ std::shared_ptr Game::getPlayerByID(uint32_t id, bool loadTmp /* = false if (!IOLoginData::loadPlayerById(tmpPlayer, id)) { return nullptr; } + tmpPlayer->setOnline(false); return tmpPlayer; } @@ -784,6 +785,7 @@ std::shared_ptr Game::getPlayerByGUID(const uint32_t &guid, bool loadTmp if (!IOLoginData::loadPlayerById(tmpPlayer, guid)) { return nullptr; } + tmpPlayer->setOnline(false); return tmpPlayer; } @@ -1142,9 +1144,9 @@ void Game::playerMoveCreature(std::shared_ptr player, std::shared_ptr(movingCreatureOrigPos, player->getPosition())) { // need to walk to the creature first before moving it - std::forward_list listDir; + stdext::arraylist listDir(128); if (player->getPathTo(movingCreatureOrigPos, listDir, 0, 1, true, true)) { - g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir), "Game::playerAutoWalk"); + g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir.data()), "Game::playerAutoWalk"); std::shared_ptr task = createPlayerTask(600, std::bind(&Game::playerMoveCreatureByID, this, player->getID(), movingCreature->getID(), movingCreatureOrigPos, toTile->getPosition()), "Game::playerMoveCreatureByID"); @@ -1439,9 +1441,9 @@ void Game::playerMoveItem(std::shared_ptr player, const Position &fromPo if (!Position::areInRange<1, 1>(playerPos, mapFromPos)) { // need to walk to the item first before using it - std::forward_list listDir; + stdext::arraylist listDir(128); if (player->getPathTo(item->getPosition(), listDir, 0, 1, true, true)) { - g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir), "Game::playerAutoWalk"); + g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir.data()), "Game::playerAutoWalk"); std::shared_ptr task = createPlayerTask(400, std::bind(&Game::playerMoveItemByPlayerID, this, player->getID(), fromPos, itemId, fromStackPos, toPos, count), "Game::playerMoveItemByPlayerID"); player->setNextWalkActionTask(task); @@ -1496,9 +1498,9 @@ void Game::playerMoveItem(std::shared_ptr player, const Position &fromPo internalGetPosition(moveItem, itemPos, itemStackPos); } - std::forward_list listDir; + stdext::arraylist listDir(128); if (player->getPathTo(walkPos, listDir, 0, 0, true, true)) { - g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir), "Game::playerAutoWalk"); + g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir.data()), "Game::playerAutoWalk"); std::shared_ptr task = createPlayerTask(400, std::bind(&Game::playerMoveItemByPlayerID, this, player->getID(), itemPos, itemId, itemStackPos, toPos, count), "Game::playerMoveItemByPlayerID"); player->setNextWalkActionTask(task); @@ -3018,7 +3020,7 @@ void Game::playerMove(uint32_t playerId, Direction direction) { player->setNextWalkActionTask(nullptr); player->cancelPush(); - player->startAutoWalk(std::forward_list { direction }, false); + player->startAutoWalk(std::vector { direction }, false); } void Game::forcePlayerMove(uint32_t playerId, Direction direction) { @@ -3031,7 +3033,7 @@ void Game::forcePlayerMove(uint32_t playerId, Direction direction) { player->setNextWalkActionTask(nullptr); player->cancelPush(); - player->startAutoWalk(std::forward_list { direction }, true); + player->startAutoWalk(std::vector { direction }, true); } bool Game::playerBroadcastMessage(std::shared_ptr player, const std::string &text) const { @@ -3196,7 +3198,7 @@ void Game::playerReceivePingBack(uint32_t playerId) { player->sendPingBack(); } -void Game::playerAutoWalk(uint32_t playerId, const std::forward_list &listDir) { +void Game::playerAutoWalk(uint32_t playerId, const std::vector &listDir) { std::shared_ptr player = getPlayerByID(playerId); if (!player) { return; @@ -3207,7 +3209,7 @@ void Game::playerAutoWalk(uint32_t playerId, const std::forward_list player->startAutoWalk(listDir, false); } -void Game::forcePlayerAutoWalk(uint32_t playerId, const std::forward_list &listDir) { +void Game::forcePlayerAutoWalk(uint32_t playerId, const std::vector &listDir) { std::shared_ptr player = getPlayerByID(playerId); if (!player) { return; @@ -3296,9 +3298,9 @@ void Game::playerUseItemEx(uint32_t playerId, const Position &fromPos, uint8_t f internalGetPosition(moveItem, itemPos, itemStackPos); } - std::forward_list listDir; + stdext::arraylist listDir(128); if (player->getPathTo(walkToPos, listDir, 0, 1, true, true)) { - g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir), "Game::playerAutoWalk"); + g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir.data()), "Game::playerAutoWalk"); std::shared_ptr task = createPlayerTask(400, std::bind(&Game::playerUseItemEx, this, playerId, itemPos, itemStackPos, fromItemId, toPos, toStackPos, toItemId), "Game::playerUseItemEx"); if (it.isRune() || it.type == ITEM_TYPE_POTION) { @@ -3401,9 +3403,9 @@ void Game::playerUseItem(uint32_t playerId, const Position &pos, uint8_t stackPo ReturnValue ret = g_actions().canUse(player, pos); if (ret != RETURNVALUE_NOERROR) { if (ret == RETURNVALUE_TOOFARAWAY) { - std::forward_list listDir; + stdext::arraylist listDir(128); if (player->getPathTo(pos, listDir, 0, 1, true, true)) { - g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir), "Game::playerAutoWalk"); + g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir.data()), "Game::playerAutoWalk"); std::shared_ptr task = createPlayerTask(400, std::bind(&Game::playerUseItem, this, playerId, pos, stackPos, index, itemId), "Game::playerUseItem"); if (it.isRune() || it.type == ITEM_TYPE_POTION) { @@ -3535,9 +3537,9 @@ void Game::playerUseWithCreature(uint32_t playerId, const Position &fromPos, uin internalGetPosition(moveItem, itemPos, itemStackPos); } - std::forward_list listDir; + stdext::arraylist listDir(128); if (player->getPathTo(walkToPos, listDir, 0, 1, true, true)) { - g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir), "Game::playerAutoWalk"); + g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir.data()), "Game::playerAutoWalk"); std::shared_ptr task = createPlayerTask(400, std::bind(&Game::playerUseWithCreature, this, playerId, itemPos, itemStackPos, creatureId, itemId), "Game::playerUseWithCreature"); if (it.isRune() || it.type == ITEM_TYPE_POTION) { @@ -3674,9 +3676,9 @@ void Game::playerRotateItem(uint32_t playerId, const Position &pos, uint8_t stac } if (pos.x != 0xFFFF && !Position::areInRange<1, 1, 0>(pos, player->getPosition())) { - std::forward_list listDir; + stdext::arraylist listDir(128); if (player->getPathTo(pos, listDir, 0, 1, true, true)) { - g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir), "Game::playerAutoWalk"); + g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir.data()), "Game::playerAutoWalk"); std::shared_ptr task = createPlayerTask(400, std::bind(&Game::playerRotateItem, this, playerId, pos, stackPos, itemId), "Game::playerRotateItem"); player->setNextWalkActionTask(task); @@ -3715,9 +3717,9 @@ void Game::playerConfigureShowOffSocket(uint32_t playerId, const Position &pos, bool isPodiumOfRenown = itemId == ITEM_PODIUM_OF_RENOWN1 || itemId == ITEM_PODIUM_OF_RENOWN2; if (!Position::areInRange<1, 1, 0>(pos, player->getPosition())) { - std::forward_list listDir; + stdext::arraylist listDir(128); if (player->getPathTo(pos, listDir, 0, 1, true, false)) { - g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir), "Game::playerAutoWalk"); + g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir.data()), "Game::playerAutoWalk"); std::shared_ptr task; if (isPodiumOfRenown) { task = createPlayerTask(400, std::bind_front(&Player::sendPodiumWindow, player, item, pos, itemId, stackPos), "Game::playerConfigureShowOffSocket"); @@ -3762,9 +3764,9 @@ void Game::playerSetShowOffSocket(uint32_t playerId, Outfit_t &outfit, const Pos } if (!Position::areInRange<1, 1, 0>(pos, player->getPosition())) { - std::forward_list listDir; + stdext::arraylist listDir(128); if (player->getPathTo(pos, listDir, 0, 1, true, false)) { - g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir), "Game::playerAutoWalk"); + g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir.data()), "Game::playerAutoWalk"); std::shared_ptr task = createPlayerTask(400, std::bind(&Game::playerBrowseField, this, playerId, pos), "Game::playerBrowseField"); player->setNextWalkActionTask(task); } else { @@ -3891,9 +3893,9 @@ void Game::playerWrapableItem(uint32_t playerId, const Position &pos, uint8_t st } if (pos.x != 0xFFFF && !Position::areInRange<1, 1, 0>(pos, player->getPosition())) { - std::forward_list listDir; + stdext::arraylist listDir(128); if (player->getPathTo(pos, listDir, 0, 1, true, true)) { - g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir), "Game::playerAutoWalk"); + g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir.data()), "Game::playerAutoWalk"); std::shared_ptr task = createPlayerTask(400, std::bind(&Game::playerWrapableItem, this, playerId, pos, stackPos, itemId), "Game::playerWrapableItem"); player->setNextWalkActionTask(task); @@ -4061,9 +4063,9 @@ void Game::playerBrowseField(uint32_t playerId, const Position &pos) { } if (!Position::areInRange<1, 1>(playerPos, pos)) { - std::forward_list listDir; + stdext::arraylist listDir(128); if (player->getPathTo(pos, listDir, 0, 1, true, true)) { - g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir), "Game::playerAutoWalk"); + g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir.data()), "Game::playerAutoWalk"); std::shared_ptr task = createPlayerTask(400, std::bind(&Game::playerBrowseField, this, playerId, pos), "Game::playerBrowseField"); player->setNextWalkActionTask(task); } else { @@ -4311,9 +4313,9 @@ void Game::playerRequestTrade(uint32_t playerId, const Position &pos, uint8_t st } if (!Position::areInRange<1, 1>(tradeItemPosition, playerPosition)) { - std::forward_list listDir; + stdext::arraylist listDir(128); if (player->getPathTo(pos, listDir, 0, 1, true, true)) { - g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir), "Game::playerAutoWalk"); + g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir.data()), "Game::playerAutoWalk"); std::shared_ptr task = createPlayerTask(400, std::bind(&Game::playerRequestTrade, this, playerId, pos, stackPos, tradePlayerId, itemId), "Game::playerRequestTrade"); player->setNextWalkActionTask(task); @@ -4860,9 +4862,9 @@ void Game::playerQuickLoot(uint32_t playerId, const Position &pos, uint16_t item if (!autoLoot && pos.x != 0xffff) { if (!Position::areInRange<1, 1, 0>(pos, player->getPosition())) { // need to walk to the corpse first before looting it - std::forward_list listDir; + stdext::arraylist listDir(128); if (player->getPathTo(pos, listDir, 0, 1, true, true)) { - g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir), "Game::playerAutoWalk"); + g_dispatcher().addEvent(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir.data()), "Game::playerAutoWalk"); std::shared_ptr task = createPlayerTask(0, std::bind(&Game::playerQuickLoot, this, player->getID(), pos, itemId, stackPos, defaultItem, lootAllCorpses, autoLoot), "Game::playerQuickLoot"); player->setNextWalkActionTask(task); } else { @@ -5745,7 +5747,7 @@ bool Game::internalCreatureSay(std::shared_ptr creature, SpeakClasses } void Game::checkCreatureWalk(uint32_t creatureId) { - std::shared_ptr creature = getCreatureByID(creatureId); + const auto &creature = getCreatureByID(creatureId); if (creature && creature->getHealth() > 0) { creature->onCreatureWalk(); cleanup(); @@ -5753,20 +5755,20 @@ void Game::checkCreatureWalk(uint32_t creatureId) { } void Game::updateCreatureWalk(uint32_t creatureId) { - std::shared_ptr creature = getCreatureByID(creatureId); + const auto &creature = getCreatureByID(creatureId); if (creature && creature->getHealth() > 0) { - creature->goToFollowCreature(); + creature->goToFollowCreature_async(); } } void Game::checkCreatureAttack(uint32_t creatureId) { - std::shared_ptr creature = getCreatureByID(creatureId); + const auto &creature = getCreatureByID(creatureId); if (creature && creature->getHealth() > 0) { creature->onAttacking(0); } } -void Game::addCreatureCheck(std::shared_ptr creature) { +void Game::addCreatureCheck(const std::shared_ptr &creature) { creature->creatureCheck = true; if (creature->inCheckCreaturesVector) { @@ -5775,10 +5777,10 @@ void Game::addCreatureCheck(std::shared_ptr creature) { } creature->inCheckCreaturesVector = true; - checkCreatureLists[uniform_random(0, EVENT_CREATURECOUNT - 1)].push_back(creature); + checkCreatureLists[uniform_random(0, EVENT_CREATURECOUNT - 1)].emplace_back(creature); } -void Game::removeCreatureCheck(std::shared_ptr creature) { +void Game::removeCreatureCheck(const std::shared_ptr &creature) { if (creature->inCheckCreaturesVector) { creature->creatureCheck = false; } @@ -5790,7 +5792,7 @@ void Game::checkCreatures() { auto &checkCreatureList = checkCreatureLists[index]; size_t it = 0, end = checkCreatureList.size(); while (it < end) { - std::shared_ptr creature = checkCreatureList[it]; + const auto &creature = checkCreatureList[it]; if (creature && creature->creatureCheck) { if (creature->getHealth() > 0) { creature->onThink(EVENT_CREATURE_THINK_INTERVAL); @@ -8593,13 +8595,10 @@ void Game::playerAcceptMarketOffer(uint32_t playerId, uint32_t timestamp, uint16 return; } - std::shared_ptr buyerPlayer = getPlayerByGUID(offer.playerId); + std::shared_ptr buyerPlayer = getPlayerByGUID(offer.playerId, true); if (!buyerPlayer) { - buyerPlayer = std::make_shared(nullptr); - if (!IOLoginData::loadPlayerById(buyerPlayer, offer.playerId)) { - offerStatus << "Failed to load buyer player " << player->getName(); - return; - } + offerStatus << "Failed to load buyer player " << player->getName(); + return; } if (!buyerPlayer->getAccount()) { @@ -8697,14 +8696,10 @@ void Game::playerAcceptMarketOffer(uint32_t playerId, uint32_t timestamp, uint16 g_saveManager().savePlayer(buyerPlayer); } } else if (offer.type == MARKETACTION_SELL) { - std::shared_ptr sellerPlayer = getPlayerByGUID(offer.playerId); + std::shared_ptr sellerPlayer = getPlayerByGUID(offer.playerId, true); if (!sellerPlayer) { - sellerPlayer = std::make_shared(nullptr); - if (!IOLoginData::loadPlayerById(sellerPlayer, offer.playerId)) { - offerStatus << "Failed to load seller player"; - - return; - } + offerStatus << "Failed to load seller player"; + return; } if (player == sellerPlayer || player->getAccount() == sellerPlayer->getAccount()) { @@ -9000,9 +8995,9 @@ void Game::playerSetMonsterPodium(uint32_t playerId, uint32_t monsterRaceId, con } if (!Position::areInRange<1, 1, 0>(pos, player->getPosition())) { - if (std::forward_list listDir; + if (stdext::arraylist listDir(128); player->getPathTo(pos, listDir, 0, 1, true, false)) { - g_dispatcher().addEvent(std::bind_front(&Game::playerAutoWalk, this, player->getID(), listDir), "Game::playerAutoWalk"); + g_dispatcher().addEvent(std::bind_front(&Game::playerAutoWalk, this, player->getID(), listDir.data()), "Game::playerAutoWalk"); std::shared_ptr task = createPlayerTask(400, std::bind_front(&Game::playerBrowseField, this, playerId, pos), "Game::playerBrowseField"); player->setNextWalkActionTask(task); } else { @@ -9091,9 +9086,9 @@ void Game::playerRotatePodium(uint32_t playerId, const Position &pos, uint8_t st } if (pos.x != 0xFFFF && !Position::areInRange<1, 1, 0>(pos, player->getPosition())) { - if (std::forward_list listDir; + if (stdext::arraylist listDir(128); player->getPathTo(pos, listDir, 0, 1, true, true)) { - g_dispatcher().addEvent(std::bind_front(&Game::playerAutoWalk, this, player->getID(), listDir), "Game::playerAutoWalk"); + g_dispatcher().addEvent(std::bind_front(&Game::playerAutoWalk, this, player->getID(), listDir.data()), "Game::playerAutoWalk"); std::shared_ptr task = createPlayerTask(400, std::bind_front(&Game::playerRotatePodium, this, playerId, pos, stackPos, itemId), "Game::playerRotatePodium"); player->setNextWalkActionTask(task); diff --git a/src/game/game.hpp b/src/game/game.hpp index a5c7cd116c0..2b30a31d2f4 100644 --- a/src/game/game.hpp +++ b/src/game/game.hpp @@ -168,8 +168,8 @@ class Game { bool removeCreature(std::shared_ptr creature, bool isLogout = true); void executeDeath(uint32_t creatureId); - void addCreatureCheck(std::shared_ptr creature); - static void removeCreatureCheck(std::shared_ptr creature); + void addCreatureCheck(const std::shared_ptr &creature); + static void removeCreatureCheck(const std::shared_ptr &creature); size_t getPlayersOnline() const { return players.size(); @@ -320,8 +320,8 @@ class Game { void playerCloseNpcChannel(uint32_t playerId); void playerReceivePing(uint32_t playerId); void playerReceivePingBack(uint32_t playerId); - void playerAutoWalk(uint32_t playerId, const std::forward_list &listDir); - void forcePlayerAutoWalk(uint32_t playerId, const std::forward_list &listDir); + void playerAutoWalk(uint32_t playerId, const std::vector &listDir); + void forcePlayerAutoWalk(uint32_t playerId, const std::vector &listDir); void playerStopAutoWalk(uint32_t playerId); void playerUseItemEx(uint32_t playerId, const Position &fromPos, uint8_t fromStackPos, uint16_t fromItemId, const Position &toPos, uint8_t toStackPos, uint16_t toItemId); void playerUseItem(uint32_t playerId, const Position &pos, uint8_t stackPos, uint8_t index, uint16_t itemId); diff --git a/src/game/scheduling/dispatcher.cpp b/src/game/scheduling/dispatcher.cpp index 1bb7512f11d..a24ef770466 100644 --- a/src/game/scheduling/dispatcher.cpp +++ b/src/game/scheduling/dispatcher.cpp @@ -14,7 +14,6 @@ #include "lib/di/container.hpp" #include "utils/tools.hpp" -constexpr static auto ASYNC_TIME_OUT = std::chrono::seconds(15); thread_local DispatcherContext Dispatcher::dispacherContext; Dispatcher &Dispatcher::getInstance() { @@ -30,7 +29,7 @@ void Dispatcher::init() { while (!threadPool.getIoContext().stopped()) { updateClock(); - executeEvents(asyncLock); + executeEvents(); executeScheduledEvents(); mergeEvents(); @@ -56,12 +55,12 @@ void Dispatcher::executeSerialEvents(std::vector &tasks) { dispacherContext.reset(); } -void Dispatcher::executeParallelEvents(std::vector &tasks, const uint8_t groupId, std::unique_lock &asyncLock) { - const size_t totalTaskSize = tasks.size(); - std::atomic_uint_fast64_t executedTasks = 0; +void Dispatcher::executeParallelEvents(std::vector &tasks, const uint8_t groupId) { + std::atomic_uint_fast64_t totalTaskSize = tasks.size(); + std::atomic_bool isTasksCompleted = false; for (const auto &task : tasks) { - threadPool.addLoad([this, &task, &executedTasks, groupId, totalTaskSize] { + threadPool.addLoad([groupId, &task, &isTasksCompleted, &totalTaskSize] { dispacherContext.type = DispatcherType::AsyncEvent; dispacherContext.group = static_cast(groupId); dispacherContext.taskName = task.getContext(); @@ -70,21 +69,21 @@ void Dispatcher::executeParallelEvents(std::vector &tasks, const uint8_t g dispacherContext.reset(); - executedTasks.fetch_add(1); - if (executedTasks.load() >= totalTaskSize) { - signalAsync.notify_one(); + totalTaskSize.fetch_sub(1); + if (totalTaskSize.load() == 0) { + isTasksCompleted.store(true); + isTasksCompleted.notify_one(); } }); } - if (signalAsync.wait_for(asyncLock, ASYNC_TIME_OUT) == std::cv_status::timeout) { - g_logger().warn("A timeout occurred when executing the async dispatch in the context({}). Executed Tasks: {}/{}.", groupId, executedTasks.load(), totalTaskSize); - } + isTasksCompleted.wait(false); + tasks.clear(); } -void Dispatcher::executeEvents(std::unique_lock &asyncLock) { - for (uint_fast8_t groupId = 0; groupId < static_cast(TaskGroup::Last); ++groupId) { +void Dispatcher::executeEvents(const TaskGroup startGroup) { + for (uint_fast8_t groupId = static_cast(startGroup); groupId < static_cast(TaskGroup::Last); ++groupId) { auto &tasks = m_tasks[groupId]; if (tasks.empty()) { return; @@ -92,9 +91,9 @@ void Dispatcher::executeEvents(std::unique_lock &asyncLock) { if (groupId == static_cast(TaskGroup::Serial)) { executeSerialEvents(tasks); - mergeEvents(); // merge request, as there may be async event requests + mergeAsyncEvents(); } else { - executeParallelEvents(tasks, groupId, asyncLock); + executeParallelEvents(tasks, groupId); } } } @@ -128,18 +127,37 @@ void Dispatcher::executeScheduledEvents() { } dispacherContext.reset(); + + mergeAsyncEvents(); // merge async events requested by scheduled events + executeEvents(TaskGroup::GenericParallel); // execute async events requested by scheduled events } -// Merge thread events with main dispatch events -void Dispatcher::mergeEvents() { +// Merge only async thread events with main dispatch events +void Dispatcher::mergeAsyncEvents() { + constexpr uint8_t start = static_cast(TaskGroup::GenericParallel); + constexpr uint8_t end = static_cast(TaskGroup::Last); + for (const auto &thread : threads) { std::scoped_lock lock(thread->mutex); - for (uint_fast8_t i = 0; i < static_cast(TaskGroup::Last); ++i) { + for (uint_fast8_t i = start; i < end; ++i) { if (!thread->tasks[i].empty()) { m_tasks[i].insert(m_tasks[i].end(), make_move_iterator(thread->tasks[i].begin()), make_move_iterator(thread->tasks[i].end())); thread->tasks[i].clear(); } } + } +} + +// Merge thread events with main dispatch events +void Dispatcher::mergeEvents() { + constexpr uint8_t serial = static_cast(TaskGroup::Serial); + + for (const auto &thread : threads) { + std::scoped_lock lock(thread->mutex); + if (!thread->tasks[serial].empty()) { + m_tasks[serial].insert(m_tasks[serial].end(), make_move_iterator(thread->tasks[serial].begin()), make_move_iterator(thread->tasks[serial].end())); + thread->tasks[serial].clear(); + } if (!thread->scheduledTasks.empty()) { scheduledTasks.insert(make_move_iterator(thread->scheduledTasks.begin()), make_move_iterator(thread->scheduledTasks.end())); @@ -196,3 +214,19 @@ void Dispatcher::stopEvent(uint64_t eventId) { scheduledTasksRef.erase(it); } } + +void DispatcherContext::addEvent(std::function &&f) const { + g_dispatcher().addEvent(std::move(f), taskName); +} + +void DispatcherContext::tryAddEvent(std::function &&f) const { + if (!f) { + return; + } + + if (isAsync()) { + g_dispatcher().addEvent(std::move(f), taskName); + } else { + f(); + } +} diff --git a/src/game/scheduling/dispatcher.hpp b/src/game/scheduling/dispatcher.hpp index 73ba38ed224..9d0a5c51750 100644 --- a/src/game/scheduling/dispatcher.hpp +++ b/src/game/scheduling/dispatcher.hpp @@ -55,6 +55,12 @@ struct DispatcherContext { return type; } + // postpone the event + void addEvent(std::function &&f) const; + + // if the context is async, the event will be postponed, if not, it will be executed immediately. + void tryAddEvent(std::function &&f) const; + private: void reset() { group = TaskGroup::ThreadPool; @@ -143,15 +149,16 @@ class Dispatcher { void init(); void shutdown() { - signalAsync.notify_all(); + signalSchedule.notify_all(); } + inline void mergeAsyncEvents(); inline void mergeEvents(); - inline void executeEvents(std::unique_lock &asyncLock); + inline void executeEvents(const TaskGroup startGroup = TaskGroup::Serial); inline void executeScheduledEvents(); inline void executeSerialEvents(std::vector &tasks); - inline void executeParallelEvents(std::vector &tasks, const uint8_t groupId, std::unique_lock &asyncLock); + inline void executeParallelEvents(std::vector &tasks, const uint8_t groupId); inline std::chrono::nanoseconds timeUntilNextScheduledTask() const; inline void checkPendingTasks() { @@ -174,7 +181,6 @@ class Dispatcher { uint_fast64_t dispatcherCycle = 0; ThreadPool &threadPool; - std::condition_variable signalAsync; std::condition_variable signalSchedule; std::atomic_bool hasPendingTasks = false; std::mutex dummyMutex; // This is only used for signaling the condition variable and not as an actual lock. diff --git a/src/items/items_definitions.hpp b/src/items/items_definitions.hpp index 8b54fa679b5..c6326acd28b 100644 --- a/src/items/items_definitions.hpp +++ b/src/items/items_definitions.hpp @@ -431,6 +431,11 @@ enum TileFlags_t : uint32_t { TILESTATE_IMMOVABLENOFIELDBLOCKPATH = 1 << 21, TILESTATE_NOFIELDBLOCKPATH = 1 << 22, TILESTATE_SUPPORTS_HANGABLE = 1 << 23, + TILESTATE_MOVEABLE = 1 << 24, + TILESTATE_ISHORIZONTAL = 1 << 25, + TILESTATE_ISVERTICAL = 1 << 26, + TILESTATE_BLOCKPROJECTILE = 1 << 27, + TILESTATE_HASHEIGHT = 1 << 28, TILESTATE_FLOORCHANGE = TILESTATE_FLOORCHANGE_DOWN | TILESTATE_FLOORCHANGE_NORTH | TILESTATE_FLOORCHANGE_SOUTH | TILESTATE_FLOORCHANGE_EAST | TILESTATE_FLOORCHANGE_WEST | TILESTATE_FLOORCHANGE_SOUTH_ALT | TILESTATE_FLOORCHANGE_EAST_ALT, }; diff --git a/src/items/tile.cpp b/src/items/tile.cpp index 92beb20d52f..44631406fda 100644 --- a/src/items/tile.cpp +++ b/src/items/tile.cpp @@ -26,18 +26,34 @@ auto real_nullptr_tile = std::make_shared(0xFFFF, 0xFFFF, 0xFF); const std::shared_ptr &Tile::nullptr_tile = real_nullptr_tile; bool Tile::hasProperty(ItemProperty prop) const { - if (ground && ground->hasProperty(prop)) { - return true; - } - - if (const TileItemVector* items = getItemList()) { - for (auto &item : *items) { - if (item->hasProperty(prop)) { - return true; - } - } + switch (prop) { + case CONST_PROP_BLOCKSOLID: + return hasFlag(TILESTATE_BLOCKSOLID); + case CONST_PROP_HASHEIGHT: + return hasFlag(TILESTATE_HASHEIGHT); + case CONST_PROP_BLOCKPROJECTILE: + return hasFlag(TILESTATE_BLOCKPROJECTILE); + case CONST_PROP_BLOCKPATH: + return hasFlag(TILESTATE_BLOCKPATH); + case CONST_PROP_ISVERTICAL: + return hasFlag(TILESTATE_ISVERTICAL); + case CONST_PROP_ISHORIZONTAL: + return hasFlag(TILESTATE_ISHORIZONTAL); + case CONST_PROP_MOVEABLE: + return hasFlag(TILESTATE_MOVEABLE); + case CONST_PROP_IMMOVABLEBLOCKSOLID: + return hasFlag(TILESTATE_IMMOVABLEBLOCKSOLID); + case CONST_PROP_IMMOVABLEBLOCKPATH: + return hasFlag(TILESTATE_IMMOVABLEBLOCKPATH); + case CONST_PROP_IMMOVABLENOFIELDBLOCKPATH: + return hasFlag(TILESTATE_IMMOVABLENOFIELDBLOCKPATH); + case CONST_PROP_NOFIELDBLOCKPATH: + return hasFlag(TILESTATE_NOFIELDBLOCKPATH); + case CONST_PROP_SUPPORTHANGABLE: + return hasFlag(TILESTATE_SUPPORTS_HANGABLE); + default: + return false; } - return false; } bool Tile::hasProperty(std::shared_ptr exclude, ItemProperty prop) const { @@ -943,6 +959,7 @@ void Tile::addThing(int32_t, std::shared_ptr thing) { if (creature) { Spectators::clearCache(); creature->setParent(static_self_cast()); + CreatureVector* creatures = makeCreatures(); creatures->insert(creatures->begin(), creature); } else { @@ -1530,6 +1547,7 @@ void Tile::internalAddThing(uint32_t, std::shared_ptr thing) { std::shared_ptr creature = thing->getCreature(); if (creature) { Spectators::clearCache(); + CreatureVector* creatures = makeCreatures(); creatures->insert(creatures->begin(), creature); } else { @@ -1574,14 +1592,14 @@ void Tile::internalAddThing(uint32_t, std::shared_ptr thing) { } } -void Tile::updateTileFlags(std::shared_ptr item) { +void Tile::updateTileFlags(const std::shared_ptr &item) { resetTileFlags(item); setTileFlags(item); } -void Tile::setTileFlags(std::shared_ptr item) { +void Tile::setTileFlags(const std::shared_ptr &item) { if (!hasFlag(TILESTATE_FLOORCHANGE)) { - const ItemType &it = Item::items[item->getID()]; + const auto &it = Item::items[item->getID()]; if (it.floorChange != 0) { setFlag(it.floorChange); } @@ -1603,6 +1621,10 @@ void Tile::setTileFlags(std::shared_ptr item) { setFlag(TILESTATE_IMMOVABLENOFIELDBLOCKPATH); } + if (item->hasProperty(CONST_PROP_SUPPORTHANGABLE)) { + setFlag(TILESTATE_SUPPORTS_HANGABLE); + } + if (item->getTeleport()) { setFlag(TILESTATE_TELEPORT); } @@ -1627,9 +1649,34 @@ void Tile::setTileFlags(std::shared_ptr item) { setFlag(TILESTATE_BED); } - std::shared_ptr container = item->getContainer(); - if (container && container->getDepotLocker()) { - setFlag(TILESTATE_DEPOT); + if (item->hasProperty(CONST_PROP_IMMOVABLEBLOCKPATH)) { + setFlag(TILESTATE_IMMOVABLEBLOCKPATH); + } + + if (item->hasProperty(CONST_PROP_MOVEABLE)) { + setFlag(TILESTATE_MOVEABLE); + } + + if (item->hasProperty(CONST_PROP_ISHORIZONTAL)) { + setFlag(TILESTATE_ISHORIZONTAL); + } + + if (item->hasProperty(CONST_PROP_ISVERTICAL)) { + setFlag(TILESTATE_ISVERTICAL); + } + + if (item->hasProperty(CONST_PROP_BLOCKPROJECTILE)) { + setFlag(TILESTATE_BLOCKPROJECTILE); + } + + if (item->hasProperty(CONST_PROP_HASHEIGHT)) { + setFlag(TILESTATE_HASHEIGHT); + } + + if (const auto &container = item->getContainer()) { + if (container->getDepotLocker()) { + setFlag(TILESTATE_DEPOT); + } } if (item->hasProperty(CONST_PROP_SUPPORTHANGABLE)) { @@ -1637,7 +1684,7 @@ void Tile::setTileFlags(std::shared_ptr item) { } } -void Tile::resetTileFlags(std::shared_ptr item) { +void Tile::resetTileFlags(const std::shared_ptr &item) { const ItemType &it = Item::items[item->getID()]; if (it.floorChange != 0) { resetFlag(TILESTATE_FLOORCHANGE); @@ -1667,6 +1714,26 @@ void Tile::resetTileFlags(std::shared_ptr item) { resetFlag(TILESTATE_IMMOVABLENOFIELDBLOCKPATH); } + if (item->hasProperty(CONST_PROP_MOVEABLE) && !hasProperty(item, CONST_PROP_MOVEABLE)) { + resetFlag(TILESTATE_MOVEABLE); + } + + if (item->hasProperty(CONST_PROP_ISHORIZONTAL) && !hasProperty(item, CONST_PROP_ISHORIZONTAL)) { + resetFlag(TILESTATE_ISHORIZONTAL); + } + + if (item->hasProperty(CONST_PROP_ISVERTICAL) && !hasProperty(item, CONST_PROP_ISVERTICAL)) { + resetFlag(TILESTATE_ISVERTICAL); + } + + if (item->hasProperty(CONST_PROP_BLOCKPROJECTILE) && !hasProperty(item, CONST_PROP_BLOCKPROJECTILE)) { + resetFlag(TILESTATE_BLOCKPROJECTILE); + } + + if (item->hasProperty(CONST_PROP_HASHEIGHT) && !hasProperty(item, CONST_PROP_HASHEIGHT)) { + resetFlag(TILESTATE_HASHEIGHT); + } + if (item->getTeleport()) { resetFlag(TILESTATE_TELEPORT); } @@ -1687,9 +1754,10 @@ void Tile::resetTileFlags(std::shared_ptr item) { resetFlag(TILESTATE_BED); } - std::shared_ptr container = item->getContainer(); - if (container && container->getDepotLocker()) { - resetFlag(TILESTATE_DEPOT); + if (const auto &container = item->getContainer()) { + if (container->getDepotLocker()) { + resetFlag(TILESTATE_DEPOT); + } } if (item->hasProperty(CONST_PROP_SUPPORTHANGABLE)) { diff --git a/src/items/tile.hpp b/src/items/tile.hpp index de5dd4f0e43..c5471756159 100644 --- a/src/items/tile.hpp +++ b/src/items/tile.hpp @@ -140,6 +140,7 @@ class Tile : public Cylinder, public SharedObject { std::shared_ptr getTopCreature() const; std::shared_ptr getBottomCreature() const; std::shared_ptr getTopVisibleCreature(std::shared_ptr creature) const; + std::shared_ptr getBottomVisibleCreature(std::shared_ptr creature) const; std::shared_ptr getTopTopItem() const; std::shared_ptr getTopDownItem() const; @@ -210,7 +211,7 @@ class Tile : public Cylinder, public SharedObject { void addThing(std::shared_ptr thing) override final; void addThing(int32_t index, std::shared_ptr thing) override; - void updateTileFlags(std::shared_ptr item); + void updateTileFlags(const std::shared_ptr &item); void updateThing(std::shared_ptr thing, uint16_t itemId, uint32_t count) override final; void replaceThing(uint32_t index, std::shared_ptr thing) override final; @@ -244,8 +245,14 @@ class Tile : public Cylinder, public SharedObject { std::shared_ptr getGround() const { return ground; } - void setGround(std::shared_ptr item) { - ground = item; + void setGround(const std::shared_ptr &item) { + if (ground) { + resetTileFlags(ground); + } + + if (ground = item) { + setTileFlags(item); + } } private: @@ -254,8 +261,8 @@ class Tile : public Cylinder, public SharedObject { void onRemoveTileItem(const CreatureVector &spectators, const std::vector &oldStackPosVector, std::shared_ptr item); void onUpdateTile(const CreatureVector &spectators); - void setTileFlags(std::shared_ptr item); - void resetTileFlags(std::shared_ptr item); + void setTileFlags(const std::shared_ptr &item); + void resetTileFlags(const std::shared_ptr &item); bool hasHarmfulField() const; ReturnValue checkNpcCanWalkIntoTile() const; diff --git a/src/lua/functions/core/game/game_functions.cpp b/src/lua/functions/core/game/game_functions.cpp index 99a277490ab..cab8c599a21 100644 --- a/src/lua/functions/core/game/game_functions.cpp +++ b/src/lua/functions/core/game/game_functions.cpp @@ -630,8 +630,6 @@ int GameFunctions::luaGameGetNormalizedPlayerName(lua_State* L) { std::shared_ptr player = g_game().getPlayerByName(name, true); if (player) { pushString(L, player->getName()); - if (!player->isOnline()) { - } } else { lua_pushnil(L); } diff --git a/src/lua/functions/core/game/lua_enums.cpp b/src/lua/functions/core/game/lua_enums.cpp index fc1586f3363..f462aaae26a 100644 --- a/src/lua/functions/core/game/lua_enums.cpp +++ b/src/lua/functions/core/game/lua_enums.cpp @@ -108,6 +108,7 @@ void LuaEnums::init(lua_State* L) { initSoundEnums(L); initWheelEnums(L); initAttributeConditionSubIdEnums(L); + initConcoctionsEnum(L); } void LuaEnums::initOthersEnums(lua_State* L) { @@ -446,6 +447,30 @@ void LuaEnums::initAttributeConditionSubIdEnums(lua_State* L) { } } +void LuaEnums::initConcoctionsEnum(lua_State* L) { + std::string luaNamespace = "Concoction_"; + registerEnumNamespace(L, luaNamespace, Concoction_t::KooldownAid); + registerEnumNamespace(L, luaNamespace, Concoction_t::StaminaExtension); + registerEnumNamespace(L, luaNamespace, Concoction_t::StrikeEnhancement); + registerEnumNamespace(L, luaNamespace, Concoction_t::CharmUpgrade); + registerEnumNamespace(L, luaNamespace, Concoction_t::WealthDuplex); + registerEnumNamespace(L, luaNamespace, Concoction_t::BestiaryBetterment); + registerEnumNamespace(L, luaNamespace, Concoction_t::FireResilience); + registerEnumNamespace(L, luaNamespace, Concoction_t::IceResilience); + registerEnumNamespace(L, luaNamespace, Concoction_t::EarthResilience); + registerEnumNamespace(L, luaNamespace, Concoction_t::EnergyResilience); + registerEnumNamespace(L, luaNamespace, Concoction_t::HolyResilience); + registerEnumNamespace(L, luaNamespace, Concoction_t::DeathResilience); + registerEnumNamespace(L, luaNamespace, Concoction_t::PhysicalResilience); + registerEnumNamespace(L, luaNamespace, Concoction_t::FireAmplification); + registerEnumNamespace(L, luaNamespace, Concoction_t::IceAmplification); + registerEnumNamespace(L, luaNamespace, Concoction_t::EarthAmplification); + registerEnumNamespace(L, luaNamespace, Concoction_t::EnergyAmplification); + registerEnumNamespace(L, luaNamespace, Concoction_t::HolyAmplification); + registerEnumNamespace(L, luaNamespace, Concoction_t::DeathAmplification); + registerEnumNamespace(L, luaNamespace, Concoction_t::PhysicalAmplification); +} + void LuaEnums::initConstMeEnums(lua_State* L) { registerEnum(L, CONST_ME_NONE); registerEnum(L, CONST_ME_DRAWBLOOD); diff --git a/src/lua/functions/core/game/lua_enums.hpp b/src/lua/functions/core/game/lua_enums.hpp index 055741b3f24..448e09fb647 100644 --- a/src/lua/functions/core/game/lua_enums.hpp +++ b/src/lua/functions/core/game/lua_enums.hpp @@ -35,6 +35,7 @@ class LuaEnums final : LuaScriptInterface { static void initConditionIdEnums(lua_State* L); static void initConditionParamEnums(lua_State* L); static void initAttributeConditionSubIdEnums(lua_State* L); + static void initConcoctionsEnum(lua_State* L); static void initConstMeEnums(lua_State* L); static void initConstAniEnums(lua_State* L); static void initConstPropEnums(lua_State* L); diff --git a/src/lua/functions/creatures/creature_functions.cpp b/src/lua/functions/creatures/creature_functions.cpp index 14d86f1e291..c5221a9c3ad 100644 --- a/src/lua/functions/creatures/creature_functions.cpp +++ b/src/lua/functions/creatures/creature_functions.cpp @@ -911,7 +911,7 @@ int CreatureFunctions::luaCreatureGetPathTo(lua_State* L) { fpp.clearSight = getBoolean(L, 6, fpp.clearSight); fpp.maxSearchDist = getNumber(L, 7, fpp.maxSearchDist); - std::forward_list dirList; + stdext::arraylist dirList(128); if (creature->getPathTo(position, dirList, fpp)) { lua_newtable(L); diff --git a/src/lua/functions/map/position_functions.cpp b/src/lua/functions/map/position_functions.cpp index 5cee3f2d847..3e5582a1cd2 100644 --- a/src/lua/functions/map/position_functions.cpp +++ b/src/lua/functions/map/position_functions.cpp @@ -97,7 +97,7 @@ int PositionFunctions::luaPositionGetPathTo(lua_State* L) { fpp.clearSight = getBoolean(L, 6, fpp.clearSight); fpp.maxSearchDist = getNumber(L, 7, fpp.maxSearchDist); - std::forward_list dirList; + stdext::arraylist dirList(128); if (g_game().map.getPathMatching(pos, dirList, FrozenPathingConditionCall(position), fpp)) { lua_newtable(L); diff --git a/src/map/map.cpp b/src/map/map.cpp index 53f7c98db38..72e867551be 100644 --- a/src/map/map.cpp +++ b/src/map/map.cpp @@ -17,6 +17,7 @@ #include "game/zones/zone.hpp" #include "io/iomap.hpp" #include "io/iomapserialize.hpp" +#include "game/scheduling/dispatcher.hpp" #include "map/spectators.hpp" void Map::load(const std::string &identifier, const Position &pos) { @@ -484,30 +485,39 @@ bool Map::isSightClear(const Position &fromPos, const Position &toPos, bool floo } std::shared_ptr Map::canWalkTo(const std::shared_ptr &creature, const Position &pos) { - int32_t walkCache = creature->getWalkCache(pos); + if (!creature || creature->isRemoved()) { + return nullptr; + } + + const int32_t walkCache = creature->getWalkCache(pos); + if (walkCache == 0) { return nullptr; - } else if (walkCache == 1) { + } + + if (walkCache == 1) { return getTile(pos.x, pos.y, pos.z); } // used for non-cached tiles - std::shared_ptr tile = getTile(pos.x, pos.y, pos.z); + const auto &tile = getTile(pos.x, pos.y, pos.z); if (creature->getTile() != tile) { if (!tile || tile->queryAdd(0, creature, 1, FLAG_PATHFINDING | FLAG_IGNOREFIELDDAMAGE) != RETURNVALUE_NOERROR) { return nullptr; } } + return tile; } -bool Map::getPathMatching(const std::shared_ptr &creature, std::forward_list &dirList, const FrozenPathingConditionCall &pathCondition, const FindPathParams &fpp) { - Position pos = creature->getPosition(); - Position endPos; - - AStarNodes nodes(pos.x, pos.y); +bool Map::getPathMatching(const std::shared_ptr &creature, stdext::arraylist &dirList, const FrozenPathingConditionCall &pathCondition, const FindPathParams &fpp) { + return getPathMatching(creature, creature->getPosition(), dirList, pathCondition, fpp); +} - int32_t bestMatch = 0; +bool Map::getPathMatching(const std::shared_ptr &creature, const Position &startPos, stdext::arraylist &dirList, const FrozenPathingConditionCall &pathCondition, const FindPathParams &fpp) { + static int_fast32_t allNeighbors[8][2] = { + { -1, 0 }, { 0, 1 }, { 1, 0 }, { 0, -1 }, { -1, -1 }, { 1, -1 }, { 1, 1 }, { -1, 1 } + }; static int_fast32_t dirNeighbors[8][5][2] = { { { -1, 0 }, { 0, 1 }, { 1, 0 }, { 1, 1 }, { -1, 1 } }, @@ -519,186 +529,14 @@ bool Map::getPathMatching(const std::shared_ptr &creature, std::forwar { { 0, 1 }, { 1, 0 }, { 1, -1 }, { 1, 1 }, { -1, 1 } }, { { -1, 0 }, { 0, 1 }, { -1, -1 }, { 1, 1 }, { -1, 1 } } }; - static int_fast32_t allNeighbors[8][2] = { - { -1, 0 }, { 0, 1 }, { 1, 0 }, { 0, -1 }, { -1, -1 }, { 1, -1 }, { 1, 1 }, { -1, 1 } - }; - const Position startPos = pos; - - AStarNode* found = nullptr; - while (fpp.maxSearchDist != 0 || nodes.getClosedNodes() < 100) { - AStarNode* n = nodes.getBestNode(); - if (!n) { - if (found) { - break; - } - return false; - } - - const int_fast32_t x = n->x; - const int_fast32_t y = n->y; - pos.x = x; - pos.y = y; - if (pathCondition(startPos, pos, fpp, bestMatch)) { - found = n; - endPos = pos; - if (bestMatch == 0) { - break; - } - } - - uint_fast32_t dirCount; - int_fast32_t* neighbors; - if (n->parent) { - const int_fast32_t offset_x = n->parent->x - x; - const int_fast32_t offset_y = n->parent->y - y; - if (offset_y == 0) { - if (offset_x == -1) { - neighbors = *dirNeighbors[DIRECTION_WEST]; - } else { - neighbors = *dirNeighbors[DIRECTION_EAST]; - } - } else if (!fpp.allowDiagonal || offset_x == 0) { - if (offset_y == -1) { - neighbors = *dirNeighbors[DIRECTION_NORTH]; - } else { - neighbors = *dirNeighbors[DIRECTION_SOUTH]; - } - } else if (offset_y == -1) { - if (offset_x == -1) { - neighbors = *dirNeighbors[DIRECTION_NORTHWEST]; - } else { - neighbors = *dirNeighbors[DIRECTION_NORTHEAST]; - } - } else if (offset_x == -1) { - neighbors = *dirNeighbors[DIRECTION_SOUTHWEST]; - } else { - neighbors = *dirNeighbors[DIRECTION_SOUTHEAST]; - } - dirCount = fpp.allowDiagonal ? 5 : 3; - } else { - dirCount = 8; - neighbors = *allNeighbors; - } - - const int_fast32_t f = n->f; - for (uint_fast32_t i = 0; i < dirCount; ++i) { - pos.x = x + *neighbors++; - pos.y = y + *neighbors++; - - if (fpp.maxSearchDist != 0 && (Position::getDistanceX(startPos, pos) > fpp.maxSearchDist || Position::getDistanceY(startPos, pos) > fpp.maxSearchDist)) { - continue; - } - - if (fpp.keepDistance && !pathCondition.isInRange(startPos, pos, fpp)) { - continue; - } - - std::shared_ptr tile; - AStarNode* neighborNode = nodes.getNodeByPosition(pos.x, pos.y); - if (neighborNode) { - tile = getTile(pos.x, pos.y, pos.z); - } else { - tile = canWalkTo(creature, pos); - if (!tile) { - continue; - } - } - - // The cost (g) for this neighbor - const int_fast32_t cost = AStarNodes::getMapWalkCost(n, pos); - const int_fast32_t extraCost = AStarNodes::getTileWalkCost(creature, tile); - const int_fast32_t newf = f + cost + extraCost; - - if (neighborNode) { - if (neighborNode->f <= newf) { - // The node on the closed/open list is cheaper than this one - continue; - } - - neighborNode->f = newf; - neighborNode->parent = n; - nodes.openNode(neighborNode); - } else { - // Does not exist in the open/closed list, create a std::make_shared - neighborNode = nodes.createOpenNode(n, pos.x, pos.y, newf); - if (!neighborNode) { - if (found) { - break; - } - return false; - } - } - } - - nodes.closeNode(n); - } - - if (!found) { - return false; - } - - int_fast32_t prevx = endPos.x; - int_fast32_t prevy = endPos.y; - - found = found->parent; - while (found) { - pos.x = found->x; - pos.y = found->y; - - int_fast32_t dx = pos.getX() - prevx; - int_fast32_t dy = pos.getY() - prevy; - - prevx = pos.x; - prevy = pos.y; - - if (dx == 1 && dy == 1) { - dirList.push_front(DIRECTION_NORTHWEST); - } else if (dx == -1 && dy == 1) { - dirList.push_front(DIRECTION_NORTHEAST); - } else if (dx == 1 && dy == -1) { - dirList.push_front(DIRECTION_SOUTHWEST); - } else if (dx == -1 && dy == -1) { - dirList.push_front(DIRECTION_SOUTHEAST); - } else if (dx == 1) { - dirList.push_front(DIRECTION_WEST); - } else if (dx == -1) { - dirList.push_front(DIRECTION_EAST); - } else if (dy == 1) { - dirList.push_front(DIRECTION_NORTH); - } else if (dy == -1) { - dirList.push_front(DIRECTION_SOUTH); - } - - found = found->parent; - } - return true; -} - -bool Map::getPathMatching(const Position &start, std::forward_list &dirList, const FrozenPathingConditionCall &pathCondition, const FindPathParams &fpp) { - Position pos = start; + Position pos = startPos; Position endPos; AStarNodes nodes(pos.x, pos.y); int32_t bestMatch = 0; - static int_fast32_t dirNeighbors[8][5][2] = { - { { -1, 0 }, { 0, 1 }, { 1, 0 }, { 1, 1 }, { -1, 1 } }, - { { -1, 0 }, { 0, 1 }, { 0, -1 }, { -1, -1 }, { -1, 1 } }, - { { -1, 0 }, { 1, 0 }, { 0, -1 }, { -1, -1 }, { 1, -1 } }, - { { 0, 1 }, { 1, 0 }, { 0, -1 }, { 1, -1 }, { 1, 1 } }, - { { 1, 0 }, { 0, -1 }, { -1, -1 }, { 1, -1 }, { 1, 1 } }, - { { -1, 0 }, { 0, -1 }, { -1, -1 }, { 1, -1 }, { -1, 1 } }, - { { 0, 1 }, { 1, 0 }, { 1, -1 }, { 1, 1 }, { -1, 1 } }, - { { -1, 0 }, { 0, 1 }, { -1, -1 }, { 1, 1 }, { -1, 1 } } - }; - static int_fast32_t allNeighbors[8][2] = { - { -1, 0 }, { 0, 1 }, { 1, 0 }, { 0, -1 }, { -1, -1 }, { 1, -1 }, { 1, 1 }, { -1, 1 } - }; - - const Position startPos = pos; - AStarNode* found = nullptr; while (fpp.maxSearchDist != 0 || nodes.getClosedNodes() < 100) { AStarNode* n = nodes.getBestNode(); @@ -768,20 +606,18 @@ bool Map::getPathMatching(const Position &start, std::forward_list &d continue; } - std::shared_ptr tile; AStarNode* neighborNode = nodes.getNodeByPosition(pos.x, pos.y); - if (neighborNode) { - tile = getTile(pos.x, pos.y, pos.z); - } else { - tile = getTile(pos.x, pos.y, pos.z); - if (!tile || tile->hasFlag(TILESTATE_BLOCKSOLID)) { - continue; - } + + const bool withoutCreature = creature == nullptr; + const auto &tile = neighborNode || withoutCreature ? getTile(pos.x, pos.y, pos.z) : canWalkTo(creature, pos); + + if (!tile || !neighborNode && withoutCreature && tile->hasFlag(TILESTATE_BLOCKSOLID)) { + continue; } // The cost (g) for this neighbor - const int_fast32_t cost = AStarNodes::getMapWalkCost(n, pos, true); - const int_fast32_t extraCost = 0; + const int_fast32_t cost = AStarNodes::getMapWalkCost(n, pos, withoutCreature); + const int_fast32_t extraCost = AStarNodes::getTileWalkCost(creature, tile); const int_fast32_t newf = f + cost + extraCost; if (neighborNode) { @@ -820,8 +656,8 @@ bool Map::getPathMatching(const Position &start, std::forward_list &d pos.x = found->x; pos.y = found->y; - int_fast32_t dx = pos.getX() - prevx; - int_fast32_t dy = pos.getY() - prevy; + const int_fast32_t dx = pos.getX() - prevx; + const int_fast32_t dy = pos.getY() - prevy; prevx = pos.x; prevy = pos.y; diff --git a/src/map/map.hpp b/src/map/map.hpp index ac0211c7daf..d5ca9b2af14 100644 --- a/src/map/map.hpp +++ b/src/map/map.hpp @@ -123,9 +123,11 @@ class Map : protected MapCache { std::shared_ptr canWalkTo(const std::shared_ptr &creature, const Position &pos); - bool getPathMatching(const std::shared_ptr &creature, std::forward_list &dirList, const FrozenPathingConditionCall &pathCondition, const FindPathParams &fpp); + bool getPathMatching(const std::shared_ptr &creature, stdext::arraylist &dirList, const FrozenPathingConditionCall &pathCondition, const FindPathParams &fpp); - bool getPathMatching(const Position &startPos, std::forward_list &dirList, const FrozenPathingConditionCall &pathCondition, const FindPathParams &fpp); + bool getPathMatching(const Position &startPos, stdext::arraylist &dirList, const FrozenPathingConditionCall &pathCondition, const FindPathParams &fpp) { + return getPathMatching(nullptr, startPos, dirList, pathCondition, fpp); + } std::map waypoints; @@ -145,6 +147,8 @@ class Map : protected MapCache { Houses housesCustomMaps[50]; private: + bool getPathMatching(const std::shared_ptr &creature, const Position &startPos, stdext::arraylist &dirList, const FrozenPathingConditionCall &pathCondition, const FindPathParams &fpp); + /** * Set a single tile. */ diff --git a/src/map/utils/astarnodes.cpp b/src/map/utils/astarnodes.cpp index 36265dd7f12..e4cccd4e694 100644 --- a/src/map/utils/astarnodes.cpp +++ b/src/map/utils/astarnodes.cpp @@ -104,23 +104,28 @@ int_fast32_t AStarNodes::getMapWalkCost(AStarNode* node, const Position &neighbo return MAP_NORMALWALKCOST; } -int_fast32_t AStarNodes::getTileWalkCost(const std::shared_ptr &creature, std::shared_ptr tile) { +int_fast32_t AStarNodes::getTileWalkCost(const std::shared_ptr &creature, const std::shared_ptr &tile) { + if (!creature || !tile) { + return 0; + } + int_fast32_t cost = 0; if (tile->getTopVisibleCreature(creature) != nullptr) { // destroy creature cost cost += MAP_NORMALWALKCOST * 3; } - if (std::shared_ptr field = tile->getFieldItem()) { - CombatType_t combatType = field->getCombatType(); - std::shared_ptr monster = creature->getMonster(); + if (const auto &field = tile->getFieldItem()) { + const CombatType_t combatType = field->getCombatType(); + const auto &monster = creature->getMonster(); + if (!creature->isImmune(combatType) && !creature->hasCondition(Combat::DamageToConditionType(combatType)) && (monster && !monster->canWalkOnFieldType(combatType))) { cost += MAP_NORMALWALKCOST * 18; } /** * Make player try to avoid magic fields, when calculating pathing */ - std::shared_ptr player = creature->getPlayer(); + const auto &player = creature->getPlayer(); if (player && !field->isBlocking() && field->getDamage() != 0) { cost += MAP_NORMALWALKCOST * 18; } diff --git a/src/map/utils/astarnodes.hpp b/src/map/utils/astarnodes.hpp index eba694de218..26f33cdfcc4 100644 --- a/src/map/utils/astarnodes.hpp +++ b/src/map/utils/astarnodes.hpp @@ -31,7 +31,7 @@ class AStarNodes { AStarNode* getNodeByPosition(uint32_t x, uint32_t y); static int_fast32_t getMapWalkCost(AStarNode* node, const Position &neighborPos, bool preferDiagonal = false); - static int_fast32_t getTileWalkCost(const std::shared_ptr &creature, std::shared_ptr tile); + static int_fast32_t getTileWalkCost(const std::shared_ptr &creature, const std::shared_ptr &tile); private: static constexpr int32_t MAX_NODES = 512; diff --git a/src/server/network/protocol/protocolgame.cpp b/src/server/network/protocol/protocolgame.cpp index 6da9e94180e..e81933610a8 100644 --- a/src/server/network/protocol/protocolgame.cpp +++ b/src/server/network/protocol/protocolgame.cpp @@ -1555,7 +1555,7 @@ void ProtocolGame::parseAutoWalk(NetworkMessage &msg) { msg.skipBytes(numdirs); - std::forward_list path; + stdext::arraylist path; for (uint8_t i = 0; i < numdirs; ++i) { uint8_t rawdir = msg.getPreviousByte(); switch (rawdir) { @@ -1592,7 +1592,7 @@ void ProtocolGame::parseAutoWalk(NetworkMessage &msg) { return; } - addGameTask(&Game::playerAutoWalk, player->getID(), path); + addGameTask(&Game::playerAutoWalk, player->getID(), path.data()); } void ProtocolGame::parseSetOutfit(NetworkMessage &msg) { diff --git a/src/utils/arraylist.hpp b/src/utils/arraylist.hpp index ea803ab8cf0..94e05e2eb76 100644 --- a/src/utils/arraylist.hpp +++ b/src/utils/arraylist.hpp @@ -21,6 +21,25 @@ namespace stdext { template class arraylist { public: + arraylist() = default; + + explicit arraylist(size_t reserveSize) { + reserve(reserveSize); + } + + explicit arraylist(std::initializer_list _Ilist) { + backContainer.assign(_Ilist); + } + + arraylist &operator=(std::initializer_list _Ilist) { + backContainer.assign(_Ilist); + return *this; + } + + void assign(std::initializer_list _Ilist) { + backContainer.assign(_Ilist); + } + bool contains(const T &v) { update(); return std::ranges::find(backContainer, v) != backContainer.end(); @@ -128,7 +147,7 @@ namespace stdext { const auto &data() noexcept { update(); - return backContainer.data(); + return backContainer; } T &operator[](const size_t i) { diff --git a/src/utils/utils_definitions.hpp b/src/utils/utils_definitions.hpp index e84dcf58a64..25fce70f47a 100644 --- a/src/utils/utils_definitions.hpp +++ b/src/utils/utils_definitions.hpp @@ -762,3 +762,26 @@ enum class AttrSubId_t { BloodRageProtector, Sharpshooter, }; + +enum Concoction_t : uint16_t { + KooldownAid = 36723, + StaminaExtension = 36725, + StrikeEnhancement = 36724, + CharmUpgrade = 36726, + WealthDuplex = 36727, + BestiaryBetterment = 36728, + FireResilience = 36729, + IceResilience = 36730, + EarthResilience = 36731, + EnergyResilience = 36732, + HolyResilience = 36733, + DeathResilience = 36734, + PhysicalResilience = 36735, + FireAmplification = 36736, + IceAmplification = 36737, + EarthAmplification = 36738, + EnergyAmplification = 36739, + HolyAmplification = 36740, + DeathAmplification = 36741, + PhysicalAmplification = 36742, +};