From e54b9c7ef75ba7bcf982a1227684a8e20b2db567 Mon Sep 17 00:00:00 2001 From: Luan Colombo <94877887+luancolombo@users.noreply.github.com> Date: Mon, 30 Oct 2023 20:12:41 +0000 Subject: [PATCH 1/7] feat: The Paleworm loot and mount (#1758) Fix paleworm loot items, and add the haze mount script. --- .../quests/feaster_of_souls/the_pale_worm.lua | 2 ++ .../scripts/actions/mounts/haze_mount.lua | 25 +++++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 data-otservbr-global/scripts/actions/mounts/haze_mount.lua 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 8f1258b6e70..41bf63ad767 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 @@ -95,6 +95,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/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() From 2a3a028da7f94200353e6022dbb3184ea024ee20 Mon Sep 17 00:00:00 2001 From: Luan Santos Date: Tue, 31 Oct 2023 21:20:01 -0700 Subject: [PATCH 2/7] fix: stamina boost overflow (#1763) This is a big one, players would stop saving correctly after getting exp boosts from daily rewards totally more than max tiny int. Fixes: - Change DB column to uint16 (same as the c++ variable type) - Programatically set the max value to 12 hours (or 43,200 seconds) --- data-otservbr-global/migrations/41.lua | 10 +++++++++- data-otservbr-global/migrations/42.lua | 3 +++ schema.sql | 4 ++-- src/creatures/players/player.hpp | 5 +++++ 4 files changed, 19 insertions(+), 3 deletions(-) create mode 100644 data-otservbr-global/migrations/42.lua 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/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/players/player.hpp b/src/creatures/players/player.hpp index 791f696148c..840c257b4c1 100644 --- a/src/creatures/players/player.hpp +++ b/src/creatures/players/player.hpp @@ -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; } From 77d9c775c17b55613abaf22204fd0a8ac8a5cc9f Mon Sep 17 00:00:00 2001 From: Luan Santos Date: Wed, 1 Nov 2023 06:01:47 -0700 Subject: [PATCH 3/7] fix: save offline counterparty on market offer acceptance (#1764) Players were getting loaded with online = true which made it so we tried to save players asynchronously even when they were offline, causing market transactions to not always save correctly. --- src/game/game.cpp | 21 +++++++------------ .../functions/core/game/game_functions.cpp | 2 -- 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/src/game/game.cpp b/src/game/game.cpp index b3706b7bf38..76c83737f60 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; } @@ -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()) { 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); } From 5b26dad8f3835d040869012fb9022068ae9ea8a1 Mon Sep 17 00:00:00 2001 From: sebbesiren <35768829+sebbesiren@users.noreply.github.com> Date: Wed, 1 Nov 2023 14:59:10 +0100 Subject: [PATCH 4/7] feat: raid addBroadcast now also announces to webhook (#1760) Added a stage to encounter to broadcast to the entire server instead of only to online players. --- data/libs/raids_lib.lua | 14 ++++++++++++++ 1 file changed, 14 insertions(+) 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 From 74ea157bd08aae53fc4bccdd298d72196210f277 Mon Sep 17 00:00:00 2001 From: Luan Santos Date: Wed, 1 Nov 2023 07:08:08 -0700 Subject: [PATCH 5/7] fix: bestiary double counting in party (#1757) This addresses two issues: - Bestiary janky implementation in Lua which causes performance issues due to its usage of onKill - Bestiary/bosstiary double counting kills when in a party and the lasthit/most damage players aren't the same Also moved concoction IDs to C++ so it can be easily used in C++ systems (such as Bestiary) --- data/libs/concoctions_lib.lua | 40 ++--- data/scripts/creatureevents/bestiary_kill.lua | 35 ----- .../scripts/creatureevents/bosstiary_kill.lua | 33 ---- src/creatures/creature.cpp | 22 +-- src/creatures/monsters/monsters.hpp | 10 +- src/creatures/players/grouping/party.hpp | 8 + src/creatures/players/player.cpp | 143 +++++++++++------- src/creatures/players/player.hpp | 14 ++ src/lua/functions/core/game/lua_enums.cpp | 25 +++ src/lua/functions/core/game/lua_enums.hpp | 1 + src/utils/utils_definitions.hpp | 23 +++ 11 files changed, 192 insertions(+), 162 deletions(-) delete mode 100644 data/scripts/creatureevents/bestiary_kill.lua delete mode 100644 data/scripts/creatureevents/bosstiary_kill.lua 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/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/src/creatures/creature.cpp b/src/creatures/creature.cpp index 2f855c6ac15..980bab34155 100644 --- a/src/creatures/creature.cpp +++ b/src/creatures/creature.cpp @@ -625,14 +625,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 +666,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); 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/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..aba77e44c60 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -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) { diff --git a/src/creatures/players/player.hpp b/src/creatures/players/player.hpp index 840c257b4c1..83b28e88364 100644 --- a/src/creatures/players/player.hpp +++ b/src/creatures/players/player.hpp @@ -2492,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; @@ -2594,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; 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/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, +}; From fe07368729668312aa8968a1b78d252a617c27dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bruno=20Lu=C3=ADs=20Lucarelo=20Lamonato?= Date: Wed, 1 Nov 2023 11:13:49 -0300 Subject: [PATCH 6/7] fix: npc lokur selling wagon tickets (#1762) Added the option to buy wagon tickets from NPC Lokur. Fixes #1755 --- data-otservbr-global/npc/lokur.lua | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) 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 From 06683a6f2e3285e68491c84feee450fc91e793f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Renato=20Foot=20Guimar=C3=A3es=20Costallat?= Date: Wed, 1 Nov 2023 11:21:16 -0300 Subject: [PATCH 7/7] ci/cl: clear GitHub actions cache on PR closure (#1751) Clean GHA cache related to the PR being closed. --- .github/workflows/clean-cache.yaml | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 .github/workflows/clean-cache.yaml 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