From 9422272b0bb1c78faffcdf090f1edfbceea019f6 Mon Sep 17 00:00:00 2001 From: Felipe Paluco Date: Thu, 28 Nov 2024 14:11:33 -0300 Subject: [PATCH 01/95] feat: add getMonstersByRace and getMonstersByBestiaryStars --- src/creatures/monsters/monsters.cpp | 28 +++++++++++++++++ src/creatures/monsters/monsters.hpp | 2 ++ .../monster/monster_type_functions.cpp | 30 +++++++++++++++++++ .../monster/monster_type_functions.hpp | 2 ++ 4 files changed, 62 insertions(+) diff --git a/src/creatures/monsters/monsters.cpp b/src/creatures/monsters/monsters.cpp index cd33e12e248..a9f5a8f3dbb 100644 --- a/src/creatures/monsters/monsters.cpp +++ b/src/creatures/monsters/monsters.cpp @@ -382,3 +382,31 @@ bool Monsters::tryAddMonsterType(const std::string &name, const std::shared_ptr< monsters[lowerName] = mType; return true; } + + +std::vector> Monsters::getMonstersByRace(BestiaryType_t race) const { + std::vector> monstersByRace; + const auto& bestiaryList = g_game().getBestiaryList(); + + for (const auto& it : bestiaryList) { + const auto monsterType = g_monsters().getMonsterType(it.second); + if (monsterType && monsterType->info.bestiaryRace == race) { + monstersByRace.emplace_back(monsterType); + } + } + return monstersByRace; +} + +std::vector> Monsters::getMonstersByBestiaryStars(uint8_t stars) const { + std::vector> monstersByStars; + const auto& bestiaryList = g_game().getBestiaryList(); + + for (const auto& it : bestiaryList) { + const auto monsterType = g_monsters().getMonsterType(it.second); + if (monsterType && monsterType->info.bestiaryStars == stars) { + monstersByStars.emplace_back(monsterType); + } + } + return monstersByStars; +} + diff --git a/src/creatures/monsters/monsters.hpp b/src/creatures/monsters/monsters.hpp index 35c4d73050d..d9a7b35975e 100644 --- a/src/creatures/monsters/monsters.hpp +++ b/src/creatures/monsters/monsters.hpp @@ -265,6 +265,8 @@ class Monsters { std::shared_ptr getMonsterTypeByRaceId(uint16_t raceId, bool isBoss = false) const; bool tryAddMonsterType(const std::string &name, const std::shared_ptr &mType); bool deserializeSpell(const std::shared_ptr &spell, spellBlock_t &sb, const std::string &description = "") const; + std::vector> Monsters::getMonstersByRace(BestiaryType_t race) const; + std::vector> Monsters::getMonstersByBestiaryStars(uint8_t stars) const; std::unique_ptr scriptInterface; std::map> monsters; diff --git a/src/lua/functions/creatures/monster/monster_type_functions.cpp b/src/lua/functions/creatures/monster/monster_type_functions.cpp index 07ce44b626c..3c33680f694 100644 --- a/src/lua/functions/creatures/monster/monster_type_functions.cpp +++ b/src/lua/functions/creatures/monster/monster_type_functions.cpp @@ -151,6 +151,8 @@ void MonsterTypeFunctions::init(lua_State* L) { Lua::registerMethod(L, "MonsterType", "deathSound", MonsterTypeFunctions::luaMonsterTypedeathSound); Lua::registerMethod(L, "MonsterType", "variant", MonsterTypeFunctions::luaMonsterTypeVariant); + Lua::registerMethod(L, "MonsterType", "getMonstersByRace", MonsterTypeFunctions::luaMonsterTypeGetMonstersByRace); + Lua::registerMethod(L, "MonsterType", "getMonstersByBestiaryStars", MonsterTypeFunctions::luaMonsterTypeGetMonstersByBestiaryStars); } void MonsterTypeFunctions::createMonsterTypeLootLuaTable(lua_State* L, const std::vector &lootList) { @@ -1853,3 +1855,31 @@ int MonsterTypeFunctions::luaMonsterTypeVariant(lua_State* L) { return 1; } + +int MonsterTypeFunctions::luaMonsterTypeGetMonstersByRace(lua_State* L) { + // monsterType:getMonstersByRace(race) + const BestiaryType_t race = Lua::getNumber(L, 1); + const auto monstersByRace = g_monsters().getMonstersByRace(race); + + lua_createtable(L, monstersByRace.size(), 0); + int index = 0; + for (const auto& monsterType : monstersByRace) { + Lua::pushUserdata(L, monsterType); + lua_rawseti(L, -2, ++index); + } + return 1; +} + +int MonsterTypeFunctions::luaMonsterTypeGetMonstersByBestiaryStars(lua_State* L) { + // monsterType:getMonstersByBestiaryStars(stars) + const uint8_t stars = Lua::getNumber(L, 1); + const auto monstersByStars = g_monsters().getMonstersByBestiaryStars(stars); + + lua_createtable(L, monstersByStars.size(), 0); + int index = 0; + for (const auto& monsterType : monstersByStars) { + Lua::pushUserdata(L, monsterType); + lua_rawseti(L, -2, ++index); + } + return 1; +} \ No newline at end of file diff --git a/src/lua/functions/creatures/monster/monster_type_functions.hpp b/src/lua/functions/creatures/monster/monster_type_functions.hpp index e272ea45cd7..2fb8640995d 100644 --- a/src/lua/functions/creatures/monster/monster_type_functions.hpp +++ b/src/lua/functions/creatures/monster/monster_type_functions.hpp @@ -139,4 +139,6 @@ class MonsterTypeFunctions { static int luaMonsterTypeCritChance(lua_State* L); static int luaMonsterTypeVariant(lua_State* L); + static int luaMonsterTypeGetMonstersByRace(lua_State* L); + static int luaMonsterTypeGetMonstersByBestiaryStars(lua_State* L); }; From 089b455410102aacccd34617663954b7fbfe5c21 Mon Sep 17 00:00:00 2001 From: Felipe Paluco Date: Thu, 28 Nov 2024 14:48:14 -0300 Subject: [PATCH 02/95] feat: add method to capture soulcore id from monster type --- src/creatures/monsters/monsters.hpp | 2 ++ .../creatures/monster/monster_type_functions.cpp | 16 ++++++++++++++++ .../creatures/monster/monster_type_functions.hpp | 2 ++ 3 files changed, 20 insertions(+) diff --git a/src/creatures/monsters/monsters.hpp b/src/creatures/monsters/monsters.hpp index d9a7b35975e..434046e6866 100644 --- a/src/creatures/monsters/monsters.hpp +++ b/src/creatures/monsters/monsters.hpp @@ -94,6 +94,8 @@ class MonsterType { uint32_t maxSummons = 0; uint32_t changeTargetSpeed = 0; + uint32_t soulCore = 0; + std::bitset m_conditionImmunities; std::bitset m_damageImmunities; diff --git a/src/lua/functions/creatures/monster/monster_type_functions.cpp b/src/lua/functions/creatures/monster/monster_type_functions.cpp index 3c33680f694..3a5d296bcfe 100644 --- a/src/lua/functions/creatures/monster/monster_type_functions.cpp +++ b/src/lua/functions/creatures/monster/monster_type_functions.cpp @@ -660,6 +660,22 @@ int MonsterTypeFunctions::luaMonsterTypeRaceid(lua_State* L) { return 1; } +int MonsterTypeFunctions::luaMonsterTypeSoulCore(lua_State* L) { + // get: monsterType:luaMonsterTypeSoulCore() set: monsterType:luaMonsterTypeSoulCore(id) + const auto &monsterType = Lua::getUserdataShared(L, 1); + if (monsterType) { + if (lua_gettop(L) == 1) { + lua_pushnumber(L, monsterType->info.soulCore); + } else { + monsterType->info.soulCore = Lua::getNumber(L, 2); + Lua::pushBoolean(L, true); + } + } else { + lua_pushnil(L); + } + return 1; +} + int MonsterTypeFunctions::luaMonsterTypeBestiarytoKill(lua_State* L) { // get: monsterType:BestiarytoKill() set: monsterType:BestiarytoKill(value) const auto &monsterType = Lua::getUserdataShared(L, 1); diff --git a/src/lua/functions/creatures/monster/monster_type_functions.hpp b/src/lua/functions/creatures/monster/monster_type_functions.hpp index 2fb8640995d..0994edaed23 100644 --- a/src/lua/functions/creatures/monster/monster_type_functions.hpp +++ b/src/lua/functions/creatures/monster/monster_type_functions.hpp @@ -131,6 +131,8 @@ class MonsterTypeFunctions { static int luaMonsterTypeBossRace(lua_State* L); static int luaMonsterTypeBossRaceId(lua_State* L); + static int luaMonsterTypeSoulCore(lua_State* L); + static int luaMonsterTypeSoundChance(lua_State* L); static int luaMonsterTypeSoundSpeedTicks(lua_State* L); static int luaMonsterTypeAddSound(lua_State* L); From 2dd1d025bc43736308d44e2979ff166f506d967f Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Thu, 28 Nov 2024 17:49:00 +0000 Subject: [PATCH 03/95] Code format - (Clang-format) --- src/creatures/monsters/monsters.cpp | 10 ++++------ .../creatures/monster/monster_type_functions.cpp | 6 +++--- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/creatures/monsters/monsters.cpp b/src/creatures/monsters/monsters.cpp index a9f5a8f3dbb..1155252bc7d 100644 --- a/src/creatures/monsters/monsters.cpp +++ b/src/creatures/monsters/monsters.cpp @@ -383,12 +383,11 @@ bool Monsters::tryAddMonsterType(const std::string &name, const std::shared_ptr< return true; } - std::vector> Monsters::getMonstersByRace(BestiaryType_t race) const { std::vector> monstersByRace; - const auto& bestiaryList = g_game().getBestiaryList(); + const auto &bestiaryList = g_game().getBestiaryList(); - for (const auto& it : bestiaryList) { + for (const auto &it : bestiaryList) { const auto monsterType = g_monsters().getMonsterType(it.second); if (monsterType && monsterType->info.bestiaryRace == race) { monstersByRace.emplace_back(monsterType); @@ -399,9 +398,9 @@ std::vector> Monsters::getMonstersByRace(BestiaryTy std::vector> Monsters::getMonstersByBestiaryStars(uint8_t stars) const { std::vector> monstersByStars; - const auto& bestiaryList = g_game().getBestiaryList(); + const auto &bestiaryList = g_game().getBestiaryList(); - for (const auto& it : bestiaryList) { + for (const auto &it : bestiaryList) { const auto monsterType = g_monsters().getMonsterType(it.second); if (monsterType && monsterType->info.bestiaryStars == stars) { monstersByStars.emplace_back(monsterType); @@ -409,4 +408,3 @@ std::vector> Monsters::getMonstersByBestiaryStars(u } return monstersByStars; } - diff --git a/src/lua/functions/creatures/monster/monster_type_functions.cpp b/src/lua/functions/creatures/monster/monster_type_functions.cpp index 3a5d296bcfe..d14d96b8c2a 100644 --- a/src/lua/functions/creatures/monster/monster_type_functions.cpp +++ b/src/lua/functions/creatures/monster/monster_type_functions.cpp @@ -1879,7 +1879,7 @@ int MonsterTypeFunctions::luaMonsterTypeGetMonstersByRace(lua_State* L) { lua_createtable(L, monstersByRace.size(), 0); int index = 0; - for (const auto& monsterType : monstersByRace) { + for (const auto &monsterType : monstersByRace) { Lua::pushUserdata(L, monsterType); lua_rawseti(L, -2, ++index); } @@ -1893,9 +1893,9 @@ int MonsterTypeFunctions::luaMonsterTypeGetMonstersByBestiaryStars(lua_State* L) lua_createtable(L, monstersByStars.size(), 0); int index = 0; - for (const auto& monsterType : monstersByStars) { + for (const auto &monsterType : monstersByStars) { Lua::pushUserdata(L, monsterType); lua_rawseti(L, -2, ++index); } return 1; -} \ No newline at end of file +} From 4193bc20d1eb07f1c66534a1732b60628c6f8976 Mon Sep 17 00:00:00 2001 From: Pedro Henrique Alves Cruz Date: Sun, 1 Dec 2024 18:52:42 -0300 Subject: [PATCH 04/95] improve: references and iterations --- src/creatures/monsters/monsters.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/creatures/monsters/monsters.cpp b/src/creatures/monsters/monsters.cpp index 1155252bc7d..fd269da1709 100644 --- a/src/creatures/monsters/monsters.cpp +++ b/src/creatures/monsters/monsters.cpp @@ -387,8 +387,8 @@ std::vector> Monsters::getMonstersByRace(BestiaryTy std::vector> monstersByRace; const auto &bestiaryList = g_game().getBestiaryList(); - for (const auto &it : bestiaryList) { - const auto monsterType = g_monsters().getMonsterType(it.second); + for (const auto &[raceId, name] : bestiaryList) { + const auto &monsterType = g_monsters().getMonsterType(name); if (monsterType && monsterType->info.bestiaryRace == race) { monstersByRace.emplace_back(monsterType); } @@ -400,8 +400,8 @@ std::vector> Monsters::getMonstersByBestiaryStars(u std::vector> monstersByStars; const auto &bestiaryList = g_game().getBestiaryList(); - for (const auto &it : bestiaryList) { - const auto monsterType = g_monsters().getMonsterType(it.second); + for (const auto &[raceId, name] : bestiaryList) { + const auto &monsterType = g_monsters().getMonsterType(name); if (monsterType && monsterType->info.bestiaryStars == stars) { monstersByStars.emplace_back(monsterType); } From a415e898ad5b3181a4ef8bc50f1af724fb36ab64 Mon Sep 17 00:00:00 2001 From: Pedro Henrique Alves Cruz Date: Sun, 1 Dec 2024 19:03:03 -0300 Subject: [PATCH 05/95] fix: hpp method declaration --- src/creatures/monsters/monsters.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/creatures/monsters/monsters.hpp b/src/creatures/monsters/monsters.hpp index 434046e6866..979b2ffcc01 100644 --- a/src/creatures/monsters/monsters.hpp +++ b/src/creatures/monsters/monsters.hpp @@ -267,8 +267,8 @@ class Monsters { std::shared_ptr getMonsterTypeByRaceId(uint16_t raceId, bool isBoss = false) const; bool tryAddMonsterType(const std::string &name, const std::shared_ptr &mType); bool deserializeSpell(const std::shared_ptr &spell, spellBlock_t &sb, const std::string &description = "") const; - std::vector> Monsters::getMonstersByRace(BestiaryType_t race) const; - std::vector> Monsters::getMonstersByBestiaryStars(uint8_t stars) const; + std::vector> getMonstersByRace(BestiaryType_t race) const; + std::vector> getMonstersByBestiaryStars(uint8_t stars) const; std::unique_ptr scriptInterface; std::map> monsters; From 30c1866065486358dd17349cc1bf53925192d3a1 Mon Sep 17 00:00:00 2001 From: Pedro Henrique Alves Cruz Date: Mon, 2 Dec 2024 01:43:19 -0300 Subject: [PATCH 06/95] feat: initiating the soulpit stages --- .../scripts/actions/soulpit/soulpit_fight.lua | 145 ++++++++++++++++++ src/creatures/creatures_definitions.hpp | 1 + src/items/functions/item/item_parse.hpp | 2 +- src/items/items.hpp | 4 + .../functions/core/game/game_functions.cpp | 24 +++ .../functions/core/game/game_functions.hpp | 2 + 6 files changed, 177 insertions(+), 1 deletion(-) create mode 100644 data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua diff --git a/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua b/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua new file mode 100644 index 00000000000..46892d6f396 --- /dev/null +++ b/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua @@ -0,0 +1,145 @@ +local config = { + soulCores = Game.getSoulCoreItems(), + requiredLevel = 8, + playerPositions = { + { pos = Position(32371, 31155, 8), teleport = Position(32373, 31138, 8), effect = CONST_ME_TELEPORT }, + { pos = Position(32371, 31156, 8), teleport = Position(32373, 31138, 8), effect = CONST_ME_TELEPORT }, + { pos = Position(32371, 31157, 8), teleport = Position(32373, 31138, 8), effect = CONST_ME_TELEPORT }, + { pos = Position(32371, 31158, 8), teleport = Position(32373, 31138, 8), effect = CONST_ME_TELEPORT }, + { pos = Position(32371, 31159, 8), teleport = Position(32373, 31138, 8), effect = CONST_ME_TELEPORT }, + }, + waves = { + [1] = { + stacks = { + [1] = 7, + } + }, + [2] = { + stacks = { + [1] = 4, + [5] = 3, + } + }, + [3] = { + stacks = { + [1] = 5, + [15] = 2, + } + }, + [4] = { + stacks = { + [1] = 3, + [5] = 3, + [40] = 1, + } + }, + }, + effects = { + [1] = CONST_ME_TELEPORT, + [5] = CONST_ME_ORANGETELEPORT, + [15] = CONST_ME_REDTELEPORT, + [40] = CONST_ME_PURPLETELEPORT, + }, + checkMonstersDelay = 4.5 * 1000, -- 4.5 seconds | The check delay should never be less than the timeToSpawnMonsters. + timeToSpawnMonsters = 4 * 1000, -- 4 seconds + totalMonsters = 7, + bossPosition = Position(32372, 31135, 8), + exit = Position(33659, 32897, 14), + zone = Zone("soulpit"), +} + +config.zone:addArea(Position(32365, 31134, 8), Position(32382, 31152, 8)) + +local soulPitAction = Action() +function soulPitAction.onUse(player, item, fromPosition, target, toPosition, isHotkey) + if not target or target:getId() ~= 49174 then + return false + end + + local isParticipant = false + for _, v in ipairs(config.playerPositions) do + if Position(v.pos) == player:getPosition() then + isParticipant = true + end + end + + if not isParticipant then + return false + end + + local lever = Lever() + lever:setPositions(config.playerPositions) + lever:setCondition(function(creature) + if not creature or not creature:isPlayer() then + return true + end + + local isAccountNormal = creature:getAccountType() < ACCOUNT_TYPE_GAMEMASTER + if isAccountNormal and creature:getLevel() < config.requiredLevel then + local message = "All players need to be level " .. config.requiredLevel .. " or higher." + creature:sendTextMessage(MESSAGE_EVENT_ADVANCE, message) + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, message) + return false + end + + local infoPositions = lever:getInfoPositions() + return true + end) + lever:checkPositions() + if lever:checkConditions() then + lever:teleportPlayers() + end + + local monsterName = string.gsub(item:getName(), " soul core", "") + + logger.warn("monster name: " .. monsterName) + + config.zone:removeMonsters() + + local encounter = Encounter("Soulpit", { + zone = config.zone, + }) + + local function waveStart() + for stack, amount in pairs(config.waves[encounter.currentStage].stacks) do + logger.warn("stack: " .. stack) + logger.warn("amount: " .. amount) + for i = 1, amount do + local position = stack ~= 40 and config.zone:randomPosition() or config.bossPosition + for i = 1, config.timeToSpawnMonsters / 1000 do + encounter:addEvent(function(position) + position:sendMagicEffect(config.effects[stack]) + end, i * 1000, position) + end + + encounter:addEvent(function(name, stack, position) + local monster = Game.createMonster(name, position) + if not monster then + return false + end + monster:setForgeStack(stack) + local icon = stack <= 15 and CreatureIconModifications_ReducedHealth or CreatureIconModifications_ReducedHealthExclamation + monster:removeIcon("forge") + monster:setIcon("soulpit", CreatureIconCategory_Modifications, icon, stack <= 15 and stack or 0) + monster:setDropLoot(false) + end, config.timeToSpawnMonsters, monsterName, stack, position) + end + end + end + + for i = 1, #config.waves do + encounter:addStage({ + start = waveStart, + }):autoAdvance({ delay = config.checkMonstersDelay, monstersKilled = true }) + end + + encounter:start() + encounter:register() + + return true +end + +for _, itemType in pairs(config.soulCores) do + soulPitAction:id(itemType:getId()) +end +soulPitAction:register() diff --git a/src/creatures/creatures_definitions.hpp b/src/creatures/creatures_definitions.hpp index dd554c086b3..4fa8b1fdfbc 100644 --- a/src/creatures/creatures_definitions.hpp +++ b/src/creatures/creatures_definitions.hpp @@ -1332,6 +1332,7 @@ enum class CreatureIconModifications_t { Influenced, Fiendish, ReducedHealth, + ReducedHealthExclamation, }; enum class CreatureIconQuests_t { diff --git a/src/items/functions/item/item_parse.hpp b/src/items/functions/item/item_parse.hpp index 6fe6fbcccd3..43a4f79433c 100644 --- a/src/items/functions/item/item_parse.hpp +++ b/src/items/functions/item/item_parse.hpp @@ -179,7 +179,7 @@ const phmap::flat_hash_map ItemTypesMap = { { "food", ITEM_TYPE_FOOD }, { "valuable", ITEM_TYPE_VALUABLE }, { "potion", ITEM_TYPE_POTION }, - + { "soulcore", ITEM_TYPE_SOULCORES }, { "ladder", ITEM_TYPE_LADDER }, { "dummy", ITEM_TYPE_DUMMY }, }; diff --git a/src/items/items.hpp b/src/items/items.hpp index b977f0bf7a9..f4eae14df27 100644 --- a/src/items/items.hpp +++ b/src/items/items.hpp @@ -415,6 +415,10 @@ class Items { return items.size(); } + std::vector &getItems() { + return items; + } + NameMap nameToItems; void addLadderId(uint16_t newId) { diff --git a/src/lua/functions/core/game/game_functions.cpp b/src/lua/functions/core/game/game_functions.cpp index aad4b71d06c..f4d9a4ce22c 100644 --- a/src/lua/functions/core/game/game_functions.cpp +++ b/src/lua/functions/core/game/game_functions.cpp @@ -107,6 +107,8 @@ void GameFunctions::init(lua_State* L) { Lua::registerMethod(L, "Game", "getSecretAchievements", GameFunctions::luaGameGetSecretAchievements); Lua::registerMethod(L, "Game", "getPublicAchievements", GameFunctions::luaGameGetPublicAchievements); Lua::registerMethod(L, "Game", "getAchievements", GameFunctions::luaGameGetAchievements); + + Lua::registerMethod(L, "Game", "getSoulCoreItems", GameFunctions::luaGameGetSoulCoreItems); } // Game @@ -994,3 +996,25 @@ int GameFunctions::luaGameGetAchievements(lua_State* L) { } return 1; } + +int GameFunctions::luaGameGetSoulCoreItems(lua_State* L) { + // Game.getSoulCoreItems() + std::vector soulCoreItems; + + for (const auto &itemType : Item::items.getItems()) { + if (itemType.m_primaryType == "SoulCores" || itemType.type == ITEM_TYPE_SOULCORES) { + soulCoreItems.emplace_back(&itemType); + } + } + + lua_createtable(L, soulCoreItems.size(), 0); + + int index = 0; + for (const auto *itemType : soulCoreItems) { + Lua::pushUserdata(L, itemType); + Lua::setMetatable(L, -1, "ItemType"); + lua_rawseti(L, -2, ++index); + } + + return 1; +} diff --git a/src/lua/functions/core/game/game_functions.hpp b/src/lua/functions/core/game/game_functions.hpp index 6d332face9f..4a9521c826d 100644 --- a/src/lua/functions/core/game/game_functions.hpp +++ b/src/lua/functions/core/game/game_functions.hpp @@ -88,4 +88,6 @@ class GameFunctions { static int luaGameGetSecretAchievements(lua_State* L); static int luaGameGetPublicAchievements(lua_State* L); static int luaGameGetAchievements(lua_State* L); + + static int luaGameGetSoulCoreItems(lua_State* L); }; From 427116ce7d0268eebc6e6061aaf1297f0bc42baa Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Mon, 2 Dec 2024 04:44:15 +0000 Subject: [PATCH 07/95] Lua code format - (Stylua) --- .../scripts/actions/soulpit/soulpit_fight.lua | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua b/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua index 46892d6f396..e2f716e4397 100644 --- a/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua +++ b/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua @@ -12,26 +12,26 @@ local config = { [1] = { stacks = { [1] = 7, - } + }, }, [2] = { stacks = { [1] = 4, [5] = 3, - } + }, }, [3] = { stacks = { [1] = 5, [15] = 2, - } + }, }, [4] = { stacks = { [1] = 3, [5] = 3, [40] = 1, - } + }, }, }, effects = { @@ -128,9 +128,11 @@ function soulPitAction.onUse(player, item, fromPosition, target, toPosition, isH end for i = 1, #config.waves do - encounter:addStage({ - start = waveStart, - }):autoAdvance({ delay = config.checkMonstersDelay, monstersKilled = true }) + encounter + :addStage({ + start = waveStart, + }) + :autoAdvance({ delay = config.checkMonstersDelay, monstersKilled = true }) end encounter:start() From 9c2cd78a724e16bf7dee238ac1b203c17d89b081 Mon Sep 17 00:00:00 2001 From: Felipe Paluco Date: Mon, 2 Dec 2024 17:39:32 -0300 Subject: [PATCH 08/95] feat: add soul core item drop feature --- .../monster/ondroploot_soul_core.lua | 52 +++++++++++++++++++ .../monster/monster_type_functions.cpp | 1 + 2 files changed, 53 insertions(+) create mode 100644 data/scripts/eventcallbacks/monster/ondroploot_soul_core.lua diff --git a/data/scripts/eventcallbacks/monster/ondroploot_soul_core.lua b/data/scripts/eventcallbacks/monster/ondroploot_soul_core.lua new file mode 100644 index 00000000000..38bbd8ea670 --- /dev/null +++ b/data/scripts/eventcallbacks/monster/ondroploot_soul_core.lua @@ -0,0 +1,52 @@ +local callback = EventCallback("MonsterOnDropLootSoulCore") +local soulCores = Game.getSoulCoreItems() + +function callback.monsterOnDropLoot(monster, corpse) + if not monster or not corpse then + return + end + local player = Player(corpse:getCorpseOwner()) + if not player or not player:canReceiveLoot() then + return + end + if monster:getMonsterForgeClassification() == FORGE_NORMAL_MONSTER then + return + end + + local soulCoreId = nil + local trySameMonsterSoulCore = math.random() <= 0.3 -- 30% of chance to drop the same monster soul core | WIP: Externalize this to a lib like libs/soulpit.lua + local mType = monster:getType() + local lootTable = {} + + if math.random() < 0.5 then -- WIP: Externalize this to a lib like libs/soulpit.lua + if trySameMonsterSoulCore then + local itemName = monster:getName():lower() .. " soul core" + soulCoreId = getItemIdByName(itemName) + end + + if not soulCoreId and not trySameMonsterSoulCore then + local race = mType:Bestiaryrace() + local monstersInCategory = mType:getMonstersByRace(race) + + if monstersInCategory and #monstersInCategory > 0 then + local randomMonster = monstersInCategory[math.random(#monstersInCategory)] + local itemName = randomMonster:name():lower() .. " soul core" + soulCoreId = getItemIdByName(itemName) + logger.info("soulcoreId: " .. soulCoreId) + end + end + + if soulCoreId then + lootTable = { + [soulCoreId] = { + count = 1, + } + } + else + return {} + end + end + corpse:addLoot(mType:generateLootRoll({}, lootTable, player)) +end + +callback:register() diff --git a/src/lua/functions/creatures/monster/monster_type_functions.cpp b/src/lua/functions/creatures/monster/monster_type_functions.cpp index d14d96b8c2a..3cdd75723b9 100644 --- a/src/lua/functions/creatures/monster/monster_type_functions.cpp +++ b/src/lua/functions/creatures/monster/monster_type_functions.cpp @@ -1881,6 +1881,7 @@ int MonsterTypeFunctions::luaMonsterTypeGetMonstersByRace(lua_State* L) { int index = 0; for (const auto &monsterType : monstersByRace) { Lua::pushUserdata(L, monsterType); + Lua::setMetatable(L, -1, "MonsterType"); lua_rawseti(L, -2, ++index); } return 1; From 1b7ed4da0ff5aec6bcfb41c1942266ba40c61937 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Mon, 2 Dec 2024 20:40:09 +0000 Subject: [PATCH 09/95] Code format - (Clang-format) --- src/lua/functions/core/game/game_functions.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lua/functions/core/game/game_functions.cpp b/src/lua/functions/core/game/game_functions.cpp index f4d9a4ce22c..c338ad1eba4 100644 --- a/src/lua/functions/core/game/game_functions.cpp +++ b/src/lua/functions/core/game/game_functions.cpp @@ -1010,7 +1010,7 @@ int GameFunctions::luaGameGetSoulCoreItems(lua_State* L) { lua_createtable(L, soulCoreItems.size(), 0); int index = 0; - for (const auto *itemType : soulCoreItems) { + for (const auto* itemType : soulCoreItems) { Lua::pushUserdata(L, itemType); Lua::setMetatable(L, -1, "ItemType"); lua_rawseti(L, -2, ++index); From 691e847f02becf8c29064533709786f6967b990e Mon Sep 17 00:00:00 2001 From: Pedro Henrique Alves Cruz Date: Mon, 2 Dec 2024 18:35:59 -0300 Subject: [PATCH 10/95] fix: soulpit fight --- .../scripts/actions/soulpit/soulpit_fight.lua | 100 +++++++++++++++++- .../spells/monster/soulpit_intensehex.lua | 26 +++++ .../spells/monster/soulpit_opressor.lua | 43 ++++++++ .../spells/monster/soulpit_powerless.lua | 26 +++++ data/scripts/lib/register_spells.lua | 26 +++++ src/creatures/combat/condition.cpp | 21 ++++ src/creatures/creatures_definitions.hpp | 1 + src/enums/player_icons.hpp | 1 + 8 files changed, 243 insertions(+), 1 deletion(-) create mode 100644 data-otservbr-global/scripts/spells/monster/soulpit_intensehex.lua create mode 100644 data-otservbr-global/scripts/spells/monster/soulpit_opressor.lua create mode 100644 data-otservbr-global/scripts/spells/monster/soulpit_powerless.lua diff --git a/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua b/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua index 46892d6f396..ef44d20029f 100644 --- a/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua +++ b/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua @@ -40,16 +40,76 @@ local config = { [15] = CONST_ME_REDTELEPORT, [40] = CONST_ME_PURPLETELEPORT, }, + bossAbilitiesIndexes = {}, + bossAbilities = { + ["enrage"] = { + monsterEvents = { + "enrageSoulPit", + }, + }, + ["opressor"] = { + spells = { + { name = "soulpit opressor", interval = 2000, chance = 25, minDamage = 0, maxDamage = 0 }, + { name = "soulpit powerless", interval = 2000, chance = 30, minDamage = 0, maxDamage = 0 }, + { name = "soulpit hexer", interval = 2000, chance = 15, minDamage = 0, maxDamage = 0 }, + }, + }, + ["overpower"] = { + playerEvents = { + "overpowerSoulPit" + }, + }, + }, checkMonstersDelay = 4.5 * 1000, -- 4.5 seconds | The check delay should never be less than the timeToSpawnMonsters. timeToSpawnMonsters = 4 * 1000, -- 4 seconds totalMonsters = 7, bossPosition = Position(32372, 31135, 8), - exit = Position(33659, 32897, 14), + exit = Position(32371, 31164, 8), zone = Zone("soulpit"), } +for name, _ in pairs(config.bossAbilities) do + table.insert(config.bossAbilitiesIndexes, name) + logger.warn(name) +end + config.zone:addArea(Position(32365, 31134, 8), Position(32382, 31152, 8)) +local enrage = CreatureEvent("enrageSoulPit") +function enrage.onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType) + if not creature or not creature:isMonster() then + return true + end + + local healthPercentage = creature:getHealth() / creature:getMaxHealth() + + if healthPercentage >= 0.5 and healthPercentage <= 0.7 then + primaryDamage = primaryDamage * 0.9 -- 10% damage reduction + secondaryDamage = secondaryDamage * 0.9 -- 10% damage reduction + elseif healthPercentage >= 0.3 and healthPercentage < 0.5 then + primaryDamage = primaryDamage * 0.75 -- 25% damage reduction + secondaryDamage = secondaryDamage * 0.75 -- 25% damage reduction + elseif healthPercentage > 0 and healthPercentage < 0.3 then + primaryDamage = primaryDamage * 0.6 -- 40% damage reduction + secondaryDamage = secondaryDamage * 0.6 -- 40% damage reduction + end + + return primaryDamage, primaryType, secondaryDamage, secondaryType +end +enrage:register() + +local overpower = CreatureEvent("overpowerSoulPit") +function overpower.onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType) + if attacker:getForgeStack() == 40 then + primaryDamage = primaryDamage * 1.1 + secondaryDamage = secondaryDamage * 1.1 + creature:getPosition():sendMagicEffect(CONST_ME_CRITICAL_DAMAGE) + end + + return primaryDamage, primaryType, secondaryDamage, secondaryType +end +overpower:register() + local soulPitAction = Action() function soulPitAction.onUse(player, item, fromPosition, target, toPosition, isHotkey) if not target or target:getId() ~= 49174 then @@ -122,6 +182,30 @@ function soulPitAction.onUse(player, item, fromPosition, target, toPosition, isH monster:removeIcon("forge") monster:setIcon("soulpit", CreatureIconCategory_Modifications, icon, stack <= 15 and stack or 0) monster:setDropLoot(false) + if stack == 40 then + local monsterType = monster:getType() + local randomAbility = bossAbilitiesIndexes[math.random(#bossAbilitiesIndexes)] + logger.warn("the random ability chosen was: " .. randomAbility) + for _, ability in pairs(config.bossAbilities[randomAbility]) do + if ability.spells then + for _, spell in pairs(ability.spells) do + monsterType:addAttack(readSpell(spell, monsterType)) + end + end + if ability.monsterEvents then + for _, name in pairs(ability.monsterEvents) do + monster:registerEvent(name) + end + end + if ability.playerEvents then + for _, name in pairs(ability.playerEvents) do + for _, players in pairs(config.zone:getPlayers()) do + player:registerEvent(name) + end + end + end + end + end end, config.timeToSpawnMonsters, monsterName, stack, position) end end @@ -133,6 +217,20 @@ function soulPitAction.onUse(player, item, fromPosition, target, toPosition, isH }):autoAdvance({ delay = config.checkMonstersDelay, monstersKilled = true }) end + function encounter:onReset(position) + for _, player in pairs(config.zone:getPlayers()) do + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, string.format("You have defeated the core of the %s soul and unlocked its animus mastery!", monsterName)) + player:teleportTo(config.exit) + for abilityName, abilities in pairs(config.bossAbilities) do + if abilities.playerEvents then + for _, eventName in pairs(abilities.playerEvents) do + player:registerEvent(eventName) + end + end + end + end + end + encounter:start() encounter:register() diff --git a/data-otservbr-global/scripts/spells/monster/soulpit_intensehex.lua b/data-otservbr-global/scripts/spells/monster/soulpit_intensehex.lua new file mode 100644 index 00000000000..da9478bb7f5 --- /dev/null +++ b/data-otservbr-global/scripts/spells/monster/soulpit_intensehex.lua @@ -0,0 +1,26 @@ +local combat = Combat() +combat:setParameter(COMBAT_PARAM_EFFECT, CONST_ME_STUN) +combat:setParameter(COMBAT_PARAM_DISTANCEEFFECT, CONST_ANI_NONE) + +local condition = Condition(CONDITION_INTENSEHEX) +condition:setParameter(CONDITION_PARAM_TICKS, 3000) +combat:addCondition(condition) + +local spell = Spell("instant") + +local zone = Zone("soulpit") + +function spell.onCastSpell(creature, var) + if creature:getForgeStack() ~= 40 or not zone:isInZone(creature:getPosition()) then + return true + end + + return combat:execute(creature, var) +end + +spell:name("soulpit intensehex") +spell:words("###940") +spell:blockWalls(true) +spell:needTarget(false) +spell:needLearn(true) +spell:register() diff --git a/data-otservbr-global/scripts/spells/monster/soulpit_opressor.lua b/data-otservbr-global/scripts/spells/monster/soulpit_opressor.lua new file mode 100644 index 00000000000..ee50ace39df --- /dev/null +++ b/data-otservbr-global/scripts/spells/monster/soulpit_opressor.lua @@ -0,0 +1,43 @@ +-- ROOT +local combatRoot = Combat() +combatRoot:setParameter(COMBAT_PARAM_EFFECT, CONST_ME_ROOTS) + +local area = createCombatArea(AREA_WAVE11) +combatRoot:setArea(area) + +local condition = Condition(CONDITION_ROOTED) +condition:setParameter(CONDITION_PARAM_TICKS, 3000) +combatRoot:addCondition(condition) + +-- FEAR + +local combatFear = Combat() +combatFear:setParameter(COMBAT_PARAM_EFFECT, CONST_ME_BLUE_GHOST) + +local condition = Condition(CONDITION_FEARED) +condition:setParameter(CONDITION_PARAM_TICKS, 3000) +combatFear:addCondition(condition) + +local spell = Spell("instant") + +local combats = { combatRoot, combatFear } + +local zone = Zone("soulpit") + +function spell.onCastSpell(creature, var) + if creature:getForgeStack() ~= 40 or not zone:isInZone(creature:getPosition()) then + return true + end + + for _, combat in pairs(combats) do + combat:execute(creature, var) + end + return true +end + +spell:name("soulpit opressor") +spell:words("###938") +spell:blockWalls(true) +spell:needTarget(false) +spell:needLearn(true) +spell:register() diff --git a/data-otservbr-global/scripts/spells/monster/soulpit_powerless.lua b/data-otservbr-global/scripts/spells/monster/soulpit_powerless.lua new file mode 100644 index 00000000000..01d71c55121 --- /dev/null +++ b/data-otservbr-global/scripts/spells/monster/soulpit_powerless.lua @@ -0,0 +1,26 @@ +local combat = Combat() +combat:setParameter(COMBAT_PARAM_EFFECT, CONST_ME_EXPLOSIONHIT) +combat:setParameter(COMBAT_PARAM_DISTANCEEFFECT, CONST_ANI_NONE) + +local condition = Condition(CONDITION_ROOTED) +condition:setParameter(CONDITION_PARAM_TICKS, 3000) +combat:addCondition(condition) + +local spell = Spell("instant") + +local zone = Zone("soulpit") + +function spell.onCastSpell(creature, var) + if creature:getForgeStack() ~= 40 or not zone:isInZone(creature:getPosition()) then + return true + end + + return combat:execute(creature, var) +end + +spell:name("soulpit powerless") +spell:words("###939") +spell:blockWalls(true) +spell:needTarget(false) +spell:needLearn(true) +spell:register() diff --git a/data/scripts/lib/register_spells.lua b/data/scripts/lib/register_spells.lua index f2e7cacee3d..3d945661ae4 100644 --- a/data/scripts/lib/register_spells.lua +++ b/data/scripts/lib/register_spells.lua @@ -399,6 +399,32 @@ CrossBeamArea3X2 = { { 0, 3, 0 }, } +AREA_FEAR_OPRESSOR = { + { 0, 1, 1, 1, 0 }, + { 1, 1, 1, 1, 1 }, + { 1, 1, 3, 1, 1 }, + { 1, 1, 1, 1, 1 }, + { 0, 1, 1, 1, 0 }, +} + +AREA_ROOT_OPRESSOR = { + { 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0 }, + { 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0 }, + { 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0 }, + { 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0 }, + { 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0 }, + { 1, 1, 1, 1, 1, 0, 0, 3, 0, 0, 1, 1, 1, 1, 1 }, + { 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0 }, + { 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0 }, + { 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0 }, + { 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0 }, + { 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 }, +} + -- The numbered-keys represents the damage values, and their table -- contains the minimum and maximum number of rounds of those damage values. RANGE = { diff --git a/src/creatures/combat/condition.cpp b/src/creatures/combat/condition.cpp index da90b03f65a..3cc72d0f4d0 100644 --- a/src/creatures/combat/condition.cpp +++ b/src/creatures/combat/condition.cpp @@ -260,6 +260,10 @@ std::shared_ptr Condition::createCondition(ConditionId_t id, Conditio case CONDITION_MUTED: case CONDITION_CHANNELMUTEDTICKS: case CONDITION_YELLTICKS: + case CONDITION_POWERLESS: + case CONDITION_LESSERHEX: + case CONDITION_INTENSEHEX: + case CONDITION_GREATERHEX: case CONDITION_PACIFIED: return std::make_shared(id, type, ticks, buff, subId); case CONDITION_BAKRAGORE: @@ -461,6 +465,23 @@ std::unordered_set ConditionGeneric::getIcons() const { case CONDITION_ROOTED: icons.insert(PlayerIcon::Rooted); break; + + case CONDITION_LESSERHEX: + icons.insert(PlayerIcon::LesserHex); + break; + + case CONDITION_INTENSEHEX: + icons.insert(PlayerIcon::IntenseHex); + break; + + case CONDITION_GREATERHEX: + icons.insert(PlayerIcon::GreaterHex); + break; + + case CONDITION_POWERLESS: + icons.insert(PlayerIcon::Powerless); + break; + case CONDITION_GOSHNARTAINT: switch (subId) { case 1: diff --git a/src/creatures/creatures_definitions.hpp b/src/creatures/creatures_definitions.hpp index 4fa8b1fdfbc..eba438ba44d 100644 --- a/src/creatures/creatures_definitions.hpp +++ b/src/creatures/creatures_definitions.hpp @@ -116,6 +116,7 @@ enum ConditionType_t : uint8_t { CONDITION_GREATERHEX = 33, CONDITION_BAKRAGORE = 34, CONDITION_GOSHNARTAINT = 35, + CONDITION_POWERLESS = 36, // Need the last ever CONDITION_COUNT diff --git a/src/enums/player_icons.hpp b/src/enums/player_icons.hpp index 7878d9e5037..ecab8a5d8ce 100644 --- a/src/enums/player_icons.hpp +++ b/src/enums/player_icons.hpp @@ -42,6 +42,7 @@ enum class PlayerIcon : uint8_t { GoshnarTaint5 = 25, NewManaShield = 26, Agony = 27, + Powerless = 28, // Must always be the last Count From 44fdeced700a22cdf60003200ed2a7ba1d98d051 Mon Sep 17 00:00:00 2001 From: Pedro Henrique Alves Cruz Date: Mon, 2 Dec 2024 18:41:16 -0300 Subject: [PATCH 11/95] fix: spells --- .../scripts/spells/monster/soulpit_intensehex.lua | 4 ++-- .../scripts/spells/monster/soulpit_opressor.lua | 2 +- .../scripts/spells/monster/soulpit_powerless.lua | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/data-otservbr-global/scripts/spells/monster/soulpit_intensehex.lua b/data-otservbr-global/scripts/spells/monster/soulpit_intensehex.lua index da9478bb7f5..b737cfec107 100644 --- a/data-otservbr-global/scripts/spells/monster/soulpit_intensehex.lua +++ b/data-otservbr-global/scripts/spells/monster/soulpit_intensehex.lua @@ -11,7 +11,7 @@ local spell = Spell("instant") local zone = Zone("soulpit") function spell.onCastSpell(creature, var) - if creature:getForgeStack() ~= 40 or not zone:isInZone(creature:getPosition()) then + if not zone:isInZone(creature:getPosition()) or creature:getForgeStack() ~= 40 then return true end @@ -21,6 +21,6 @@ end spell:name("soulpit intensehex") spell:words("###940") spell:blockWalls(true) -spell:needTarget(false) +spell:needTarget(true) spell:needLearn(true) spell:register() diff --git a/data-otservbr-global/scripts/spells/monster/soulpit_opressor.lua b/data-otservbr-global/scripts/spells/monster/soulpit_opressor.lua index ee50ace39df..7fda90ef77a 100644 --- a/data-otservbr-global/scripts/spells/monster/soulpit_opressor.lua +++ b/data-otservbr-global/scripts/spells/monster/soulpit_opressor.lua @@ -25,7 +25,7 @@ local combats = { combatRoot, combatFear } local zone = Zone("soulpit") function spell.onCastSpell(creature, var) - if creature:getForgeStack() ~= 40 or not zone:isInZone(creature:getPosition()) then + if not zone:isInZone(creature:getPosition()) or creature:getForgeStack() ~= 40 then return true end diff --git a/data-otservbr-global/scripts/spells/monster/soulpit_powerless.lua b/data-otservbr-global/scripts/spells/monster/soulpit_powerless.lua index 01d71c55121..516bf0c7194 100644 --- a/data-otservbr-global/scripts/spells/monster/soulpit_powerless.lua +++ b/data-otservbr-global/scripts/spells/monster/soulpit_powerless.lua @@ -11,7 +11,7 @@ local spell = Spell("instant") local zone = Zone("soulpit") function spell.onCastSpell(creature, var) - if creature:getForgeStack() ~= 40 or not zone:isInZone(creature:getPosition()) then + if not zone:isInZone(creature:getPosition()) or creature:getForgeStack() ~= 40 then return true end @@ -21,6 +21,6 @@ end spell:name("soulpit powerless") spell:words("###939") spell:blockWalls(true) -spell:needTarget(false) +spell:needTarget(true) spell:needLearn(true) spell:register() From 3647df2157ada94a473facaf9fba04cfe81a27df Mon Sep 17 00:00:00 2001 From: Pedro Henrique Alves Cruz Date: Mon, 2 Dec 2024 21:32:18 -0300 Subject: [PATCH 12/95] fix: soulpit fight --- data-otservbr-global/lib/others/load.lua | 1 + data-otservbr-global/lib/others/soulpit.lua | 73 ++++++ .../monster/humanoids/orc_warlord.lua | 3 + .../scripts/actions/soulpit/soulpit_fight.lua | 220 +++++------------- .../spells/monster/soulpit_intensehex.lua | 8 +- .../spells/monster/soulpit_opressor.lua | 16 +- .../spells/monster/soulpit_powerless.lua | 10 +- src/creatures/monsters/monster.cpp | 20 +- src/creatures/monsters/monster.hpp | 2 + .../functions/core/game/game_functions.cpp | 49 ++++ .../functions/core/game/game_functions.hpp | 1 + 11 files changed, 219 insertions(+), 184 deletions(-) create mode 100644 data-otservbr-global/lib/others/soulpit.lua diff --git a/data-otservbr-global/lib/others/load.lua b/data-otservbr-global/lib/others/load.lua index 031c8fb2026..3422819b9f8 100644 --- a/data-otservbr-global/lib/others/load.lua +++ b/data-otservbr-global/lib/others/load.lua @@ -1 +1,2 @@ dofile(DATA_DIRECTORY .. "/lib/others/dawnport.lua") +dofile(DATA_DIRECTORY .. "/lib/others/soulpit.lua") diff --git a/data-otservbr-global/lib/others/soulpit.lua b/data-otservbr-global/lib/others/soulpit.lua new file mode 100644 index 00000000000..c716e8c7817 --- /dev/null +++ b/data-otservbr-global/lib/others/soulpit.lua @@ -0,0 +1,73 @@ +SoulPit = { + encounter = nil, + soulCores = Game.getSoulCoreItems(), + requiredLevel = 8, + playerPositions = { + { pos = Position(32371, 31155, 8), teleport = Position(32373, 31138, 8), effect = CONST_ME_TELEPORT }, + { pos = Position(32371, 31156, 8), teleport = Position(32373, 31138, 8), effect = CONST_ME_TELEPORT }, + { pos = Position(32371, 31157, 8), teleport = Position(32373, 31138, 8), effect = CONST_ME_TELEPORT }, + { pos = Position(32371, 31158, 8), teleport = Position(32373, 31138, 8), effect = CONST_ME_TELEPORT }, + { pos = Position(32371, 31159, 8), teleport = Position(32373, 31138, 8), effect = CONST_ME_TELEPORT }, + }, + waves = { + [1] = { + stacks = { + [1] = 6, + [40] = 1 + }, + }, + [2] = { + stacks = { + [1] = 4, + [5] = 3, + }, + }, + [3] = { + stacks = { + [1] = 5, + [15] = 2, + }, + }, + [4] = { + stacks = { + [1] = 3, + [5] = 3, + [40] = 1, + }, + }, + }, + effects = { + [1] = CONST_ME_TELEPORT, + [5] = CONST_ME_ORANGETELEPORT, + [15] = CONST_ME_REDTELEPORT, + [40] = CONST_ME_PURPLETELEPORT, + }, + possibleAbilities = { + "overpowerSoulPit", + "enrageSoulPit", + "opressorSoulPit", + }, + bossAbilities = { + ["overpowerSoulPit"] = { + player = true, + monster = false, + }, + ["enrageSoulPit"] = { + player = false, + monster = true, + }, + ["opressorSoulPit"] = { + player = false, + monster = true, + }, + }, + timeToKick = 3 * 1000, -- 3 seconds + checkMonstersDelay = 4.5 * 1000, -- 4.5 seconds | The check delay should never be less than the timeToSpawnMonsters. + timeToSpawnMonsters = 4 * 1000, -- 4 seconds + totalMonsters = 7, + bossPosition = Position(32372, 31135, 8), + exit = Position(32371, 31164, 8), + zone = Zone("soulpit"), +} + +SoulPit.zone:addArea(Position(32365, 31134, 8), Position(32382, 31152, 8)) diff --git a/data-otservbr-global/monster/humanoids/orc_warlord.lua b/data-otservbr-global/monster/humanoids/orc_warlord.lua index db5b0e20288..5b33843d971 100644 --- a/data-otservbr-global/monster/humanoids/orc_warlord.lua +++ b/data-otservbr-global/monster/humanoids/orc_warlord.lua @@ -107,6 +107,9 @@ monster.loot = { monster.attacks = { { name = "melee", interval = 2000, chance = 100, minDamage = 0, maxDamage = -250 }, { name = "combat", interval = 2000, chance = 20, type = COMBAT_PHYSICALDAMAGE, minDamage = 0, maxDamage = -200, range = 7, shootEffect = CONST_ANI_THROWINGSTAR, target = false }, + { name = "soulpit opressor", interval = 2000, chance = 25, minDamage = 0, maxDamage = 0 }, + { name = "soulpit powerless", interval = 2000, chance = 30, minDamage = 0, maxDamage = 0 }, + { name = "soulpit intensehex", interval = 2000, chance = 15, minDamage = 0, maxDamage = 0 }, } monster.defenses = { diff --git a/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua b/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua index d389b77fc0d..09303ccbd3a 100644 --- a/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua +++ b/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua @@ -1,114 +1,24 @@ -local config = { - soulCores = Game.getSoulCoreItems(), - requiredLevel = 8, - playerPositions = { - { pos = Position(32371, 31155, 8), teleport = Position(32373, 31138, 8), effect = CONST_ME_TELEPORT }, - { pos = Position(32371, 31156, 8), teleport = Position(32373, 31138, 8), effect = CONST_ME_TELEPORT }, - { pos = Position(32371, 31157, 8), teleport = Position(32373, 31138, 8), effect = CONST_ME_TELEPORT }, - { pos = Position(32371, 31158, 8), teleport = Position(32373, 31138, 8), effect = CONST_ME_TELEPORT }, - { pos = Position(32371, 31159, 8), teleport = Position(32373, 31138, 8), effect = CONST_ME_TELEPORT }, - }, - waves = { - [1] = { - stacks = { - [1] = 7, - }, - }, - [2] = { - stacks = { - [1] = 4, - [5] = 3, - }, - }, - [3] = { - stacks = { - [1] = 5, - [15] = 2, - }, - }, - [4] = { - stacks = { - [1] = 3, - [5] = 3, - [40] = 1, - }, - }, - }, - effects = { - [1] = CONST_ME_TELEPORT, - [5] = CONST_ME_ORANGETELEPORT, - [15] = CONST_ME_REDTELEPORT, - [40] = CONST_ME_PURPLETELEPORT, - }, - bossAbilitiesIndexes = {}, - bossAbilities = { - ["enrage"] = { - monsterEvents = { - "enrageSoulPit", - }, - }, - ["opressor"] = { - spells = { - { name = "soulpit opressor", interval = 2000, chance = 25, minDamage = 0, maxDamage = 0 }, - { name = "soulpit powerless", interval = 2000, chance = 30, minDamage = 0, maxDamage = 0 }, - { name = "soulpit hexer", interval = 2000, chance = 15, minDamage = 0, maxDamage = 0 }, - }, - }, - ["overpower"] = { - playerEvents = { - "overpowerSoulPit" - }, - }, - }, - checkMonstersDelay = 4.5 * 1000, -- 4.5 seconds | The check delay should never be less than the timeToSpawnMonsters. - timeToSpawnMonsters = 4 * 1000, -- 4 seconds - totalMonsters = 7, - bossPosition = Position(32372, 31135, 8), - exit = Position(32371, 31164, 8), - zone = Zone("soulpit"), -} - -for name, _ in pairs(config.bossAbilities) do - table.insert(config.bossAbilitiesIndexes, name) - logger.warn(name) -end - -config.zone:addArea(Position(32365, 31134, 8), Position(32382, 31152, 8)) - -local enrage = CreatureEvent("enrageSoulPit") -function enrage.onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType) - if not creature or not creature:isMonster() then - return true +local zoneEvent = ZoneEvent(SoulPit.zone) +function zoneEvent.afterLeave(zone, creature) + local player = creature:getPlayer() + if not player then + return false end - local healthPercentage = creature:getHealth() / creature:getMaxHealth() - - if healthPercentage >= 0.5 and healthPercentage <= 0.7 then - primaryDamage = primaryDamage * 0.9 -- 10% damage reduction - secondaryDamage = secondaryDamage * 0.9 -- 10% damage reduction - elseif healthPercentage >= 0.3 and healthPercentage < 0.5 then - primaryDamage = primaryDamage * 0.75 -- 25% damage reduction - secondaryDamage = secondaryDamage * 0.75 -- 25% damage reduction - elseif healthPercentage > 0 and healthPercentage < 0.3 then - primaryDamage = primaryDamage * 0.6 -- 40% damage reduction - secondaryDamage = secondaryDamage * 0.6 -- 40% damage reduction + if table.empty(zone:getPlayers()) then + if SoulPit.encounter then + SoulPit.encounter:reset() + SoulPit.encounter = nil + end end - return primaryDamage, primaryType, secondaryDamage, secondaryType -end -enrage:register() - -local overpower = CreatureEvent("overpowerSoulPit") -function overpower.onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType) - if attacker:getForgeStack() == 40 then - primaryDamage = primaryDamage * 1.1 - secondaryDamage = secondaryDamage * 1.1 - creature:getPosition():sendMagicEffect(CONST_ME_CRITICAL_DAMAGE) + for abilityName, abilityInfo in pairs(SoulPit.bossAbilities) do + if abilityInfo.player then + player:unregisterEvent(abilityName) + end end - - return primaryDamage, primaryType, secondaryDamage, secondaryType end -overpower:register() +zoneEvent:register() local soulPitAction = Action() function soulPitAction.onUse(player, item, fromPosition, target, toPosition, isHotkey) @@ -117,7 +27,7 @@ function soulPitAction.onUse(player, item, fromPosition, target, toPosition, isH end local isParticipant = false - for _, v in ipairs(config.playerPositions) do + for _, v in ipairs(SoulPit.playerPositions) do if Position(v.pos) == player:getPosition() then isParticipant = true end @@ -128,17 +38,22 @@ function soulPitAction.onUse(player, item, fromPosition, target, toPosition, isH end local lever = Lever() - lever:setPositions(config.playerPositions) + lever:setPositions(SoulPit.playerPositions) lever:setCondition(function(creature) if not creature or not creature:isPlayer() then return true end + if not table.empty(SoulPit.zone:getPlayers()) then + local message = "Someone is fighting in the arena!" + creature:sendTextMessage(MESSAGE_EVENT_ADVANCE, message) + return false + end + local isAccountNormal = creature:getAccountType() < ACCOUNT_TYPE_GAMEMASTER - if isAccountNormal and creature:getLevel() < config.requiredLevel then - local message = "All players need to be level " .. config.requiredLevel .. " or higher." + if isAccountNormal and creature:getLevel() < SoulPit.requiredLevel then + local message = string.format("All players need to be level %s or higher.", SoulPit.requiredLevel) creature:sendTextMessage(MESSAGE_EVENT_ADVANCE, message) - player:sendTextMessage(MESSAGE_EVENT_ADVANCE, message) return false end @@ -154,83 +69,74 @@ function soulPitAction.onUse(player, item, fromPosition, target, toPosition, isH logger.warn("monster name: " .. monsterName) - config.zone:removeMonsters() + SoulPit.zone:removeMonsters() + + if SoulPit.encounter ~= nil then + SoulPit.encounter:reset() + end local encounter = Encounter("Soulpit", { - zone = config.zone, + zone = SoulPit.zone, }) + SoulPit.encounter = encounter + local function waveStart() - for stack, amount in pairs(config.waves[encounter.currentStage].stacks) do + for stack, amount in pairs(SoulPit.waves[encounter.currentStage].stacks) do logger.warn("stack: " .. stack) logger.warn("amount: " .. amount) for i = 1, amount do - local position = stack ~= 40 and config.zone:randomPosition() or config.bossPosition - for i = 1, config.timeToSpawnMonsters / 1000 do + local position = stack ~= 40 and SoulPit.zone:randomPosition() or SoulPit.bossPosition + for i = 1, SoulPit.timeToSpawnMonsters / 1000 do encounter:addEvent(function(position) - position:sendMagicEffect(config.effects[stack]) + position:sendMagicEffect(SoulPit.effects[stack]) end, i * 1000, position) end - encounter:addEvent(function(name, stack, position) - local monster = Game.createMonster(name, position) + local randomAbility = SoulPit.possibleAbilities[math.random(1, #SoulPit.possibleAbilities)] + local chosenBossAbility = SoulPit.bossAbilities[randomAbility] + + encounter:addEvent(function(name, stack, position, bossAbilityName, bossAbility) + local monster = Game.createSoulPitMonster(name, position, stack) if not monster then return false end - monster:setForgeStack(stack) - local icon = stack <= 15 and CreatureIconModifications_ReducedHealth or CreatureIconModifications_ReducedHealthExclamation - monster:removeIcon("forge") - monster:setIcon("soulpit", CreatureIconCategory_Modifications, icon, stack <= 15 and stack or 0) - monster:setDropLoot(false) if stack == 40 then - local monsterType = monster:getType() - local randomAbility = bossAbilitiesIndexes[math.random(#bossAbilitiesIndexes)] - logger.warn("the random ability chosen was: " .. randomAbility) - for _, ability in pairs(config.bossAbilities[randomAbility]) do - if ability.spells then - for _, spell in pairs(ability.spells) do - monsterType:addAttack(readSpell(spell, monsterType)) - end - end - if ability.monsterEvents then - for _, name in pairs(ability.monsterEvents) do - monster:registerEvent(name) - end - end - if ability.playerEvents then - for _, name in pairs(ability.playerEvents) do - for _, players in pairs(config.zone:getPlayers()) do - player:registerEvent(name) - end - end - end + logger.warn("ability name: {}", bossAbilityName) + if bossAbility.monster then + monster:registerEvent(bossAbilityName) + end + if bossAbility.player then + player:registerEvent(bossAbilityName) end end - end, config.timeToSpawnMonsters, monsterName, stack, position) + end, SoulPit.timeToSpawnMonsters, monsterName, stack, position, randomAbility, chosenBossAbility) end end end - for i = 1, #config.waves do + for i = 1, #SoulPit.waves do encounter :addStage({ start = waveStart, }) - :autoAdvance({ delay = config.checkMonstersDelay, monstersKilled = true }) + :autoAdvance({ delay = SoulPit.checkMonstersDelay, monstersKilled = true }) end function encounter:onReset(position) - for _, player in pairs(config.zone:getPlayers()) do + SoulPit.zone:removeMonsters() + + for _, player in pairs(SoulPit.zone:getPlayers()) do player:sendTextMessage(MESSAGE_EVENT_ADVANCE, string.format("You have defeated the core of the %s soul and unlocked its animus mastery!", monsterName)) - player:teleportTo(config.exit) - for abilityName, abilities in pairs(config.bossAbilities) do - if abilities.playerEvents then - for _, eventName in pairs(abilities.playerEvents) do - player:registerEvent(eventName) - end - end - end + -- Add the monster animus mastery for the player. end + + addEvent(function() + SoulPit.encounter = nil + for _, player in pairs(SoulPit.zone:getPlayers()) do + player:teleportTo(SoulPit.exit) + end + end, SoulPit.timeToKick) end encounter:start() @@ -239,7 +145,7 @@ function soulPitAction.onUse(player, item, fromPosition, target, toPosition, isH return true end -for _, itemType in pairs(config.soulCores) do +for _, itemType in pairs(SoulPit.soulCores) do soulPitAction:id(itemType:getId()) end soulPitAction:register() diff --git a/data-otservbr-global/scripts/spells/monster/soulpit_intensehex.lua b/data-otservbr-global/scripts/spells/monster/soulpit_intensehex.lua index b737cfec107..b9ce99fdd89 100644 --- a/data-otservbr-global/scripts/spells/monster/soulpit_intensehex.lua +++ b/data-otservbr-global/scripts/spells/monster/soulpit_intensehex.lua @@ -8,14 +8,12 @@ combat:addCondition(condition) local spell = Spell("instant") -local zone = Zone("soulpit") - function spell.onCastSpell(creature, var) - if not zone:isInZone(creature:getPosition()) or creature:getForgeStack() ~= 40 then - return true + if table.contains(creature:getEvents(CREATURE_EVENT_THINK), "opressorSoulPit") then + return combat:execute(creature, var) end - return combat:execute(creature, var) + return true end spell:name("soulpit intensehex") diff --git a/data-otservbr-global/scripts/spells/monster/soulpit_opressor.lua b/data-otservbr-global/scripts/spells/monster/soulpit_opressor.lua index 7fda90ef77a..afe2dae554e 100644 --- a/data-otservbr-global/scripts/spells/monster/soulpit_opressor.lua +++ b/data-otservbr-global/scripts/spells/monster/soulpit_opressor.lua @@ -2,7 +2,7 @@ local combatRoot = Combat() combatRoot:setParameter(COMBAT_PARAM_EFFECT, CONST_ME_ROOTS) -local area = createCombatArea(AREA_WAVE11) +local area = createCombatArea(AREA_ROOT_OPRESSOR) combatRoot:setArea(area) local condition = Condition(CONDITION_ROOTED) @@ -14,6 +14,9 @@ combatRoot:addCondition(condition) local combatFear = Combat() combatFear:setParameter(COMBAT_PARAM_EFFECT, CONST_ME_BLUE_GHOST) +local area = createCombatArea(AREA_FEAR_OPRESSOR) +combatRoot:setArea(area) + local condition = Condition(CONDITION_FEARED) condition:setParameter(CONDITION_PARAM_TICKS, 3000) combatFear:addCondition(condition) @@ -22,16 +25,13 @@ local spell = Spell("instant") local combats = { combatRoot, combatFear } -local zone = Zone("soulpit") - function spell.onCastSpell(creature, var) - if not zone:isInZone(creature:getPosition()) or creature:getForgeStack() ~= 40 then - return true + if table.contains(creature:getEvents(CREATURE_EVENT_THINK), "opressorSoulPit") then + for _, combat in pairs(combats) do + combat:execute(creature, var) + end end - for _, combat in pairs(combats) do - combat:execute(creature, var) - end return true end diff --git a/data-otservbr-global/scripts/spells/monster/soulpit_powerless.lua b/data-otservbr-global/scripts/spells/monster/soulpit_powerless.lua index 516bf0c7194..2edbe0b6d8b 100644 --- a/data-otservbr-global/scripts/spells/monster/soulpit_powerless.lua +++ b/data-otservbr-global/scripts/spells/monster/soulpit_powerless.lua @@ -2,20 +2,18 @@ local combat = Combat() combat:setParameter(COMBAT_PARAM_EFFECT, CONST_ME_EXPLOSIONHIT) combat:setParameter(COMBAT_PARAM_DISTANCEEFFECT, CONST_ANI_NONE) -local condition = Condition(CONDITION_ROOTED) +local condition = Condition(CONDITION_POWERLESS) condition:setParameter(CONDITION_PARAM_TICKS, 3000) combat:addCondition(condition) local spell = Spell("instant") -local zone = Zone("soulpit") - function spell.onCastSpell(creature, var) - if not zone:isInZone(creature:getPosition()) or creature:getForgeStack() ~= 40 then - return true + if table.contains(creature:getEvents(CREATURE_EVENT_THINK), "opressorSoulPit") then + return combat:execute(creature, var) end - return combat:execute(creature, var) + return true end spell:name("soulpit powerless") diff --git a/src/creatures/monsters/monster.cpp b/src/creatures/monsters/monster.cpp index 7276bea19d1..d396bb47744 100644 --- a/src/creatures/monsters/monster.cpp +++ b/src/creatures/monsters/monster.cpp @@ -697,7 +697,8 @@ bool Monster::isOpponent(const std::shared_ptr &creature) const { } uint64_t Monster::getLostExperience() const { - return skillLoss ? mType->info.experience : 0; + float extraExperience = forgeStack <= 15 ? (forgeStack + 10) / 10 : 28; + return skillLoss ? static_cast(std::round(mType->info.experience * extraExperience)) : 0; } uint16_t Monster::getLookCorpse() const { @@ -2541,6 +2542,15 @@ void Monster::getPathSearchParams(const std::shared_ptr &creature, Fin } } +void Monster::applyStacks() { + // Change health based in stacks + const auto percentToIncrement = 1 + (15 * forgeStack + 35) / 100.f; + auto newHealth = static_cast(std::ceil(static_cast(healthMax) * percentToIncrement)); + + healthMax = newHealth; + health = newHealth; +} + void Monster::configureForgeSystem() { if (!canBeForgeMonster()) { return; @@ -2557,13 +2567,6 @@ void Monster::configureForgeSystem() { g_game().updateCreatureIcon(static_self_cast()); } - // Change health based in stacks - const auto percentToIncrement = 1 + (15 * forgeStack + 35) / 100.f; - auto newHealth = static_cast(std::ceil(static_cast(healthMax) * percentToIncrement)); - - healthMax = newHealth; - health = newHealth; - // Event to give Dusts const std::string &Eventname = "ForgeSystemMonster"; registerCreatureEvent(Eventname); @@ -2589,6 +2592,7 @@ uint16_t Monster::getForgeStack() const { void Monster::setForgeStack(uint16_t stack) { forgeStack = stack; + applyStacks(); } ForgeClassifications_t Monster::getMonsterForgeClassification() const { diff --git a/src/creatures/monsters/monster.hpp b/src/creatures/monsters/monster.hpp index b8a6087acc1..c9e5745da69 100644 --- a/src/creatures/monsters/monster.hpp +++ b/src/creatures/monsters/monster.hpp @@ -183,6 +183,8 @@ class Monster final : public Creature { static uint32_t monsterAutoID; + void applyStacks(); + void configureForgeSystem(); bool canBeForgeMonster() const; diff --git a/src/lua/functions/core/game/game_functions.cpp b/src/lua/functions/core/game/game_functions.cpp index c338ad1eba4..97e97e996a2 100644 --- a/src/lua/functions/core/game/game_functions.cpp +++ b/src/lua/functions/core/game/game_functions.cpp @@ -66,6 +66,7 @@ void GameFunctions::init(lua_State* L) { Lua::registerMethod(L, "Game", "createItem", GameFunctions::luaGameCreateItem); Lua::registerMethod(L, "Game", "createContainer", GameFunctions::luaGameCreateContainer); Lua::registerMethod(L, "Game", "createMonster", GameFunctions::luaGameCreateMonster); + Lua::registerMethod(L, "Game", "createSoulPitMonster", GameFunctions::luaGameCreateSoulPitMonster); Lua::registerMethod(L, "Game", "createNpc", GameFunctions::luaGameCreateNpc); Lua::registerMethod(L, "Game", "generateNpc", GameFunctions::luaGameGenerateNpc); Lua::registerMethod(L, "Game", "createTile", GameFunctions::luaGameCreateTile); @@ -548,6 +549,54 @@ int GameFunctions::luaGameCreateMonster(lua_State* L) { return 1; } +int GameFunctions::luaGameCreateSoulPitMonster(lua_State* L) { + // Game.createSoulPitMonster(monsterName, position, [stack = 1, [, extended = false[, force = false[, master = nil]]]]) + const auto &monster = Monster::createMonster(Lua::getString(L, 1)); + if (!monster) { + lua_pushnil(L); + return 1; + } + + bool isSummon = false; + if (lua_gettop(L) >= 6) { + if (const auto &master = Lua::getCreature(L, 6)) { + monster->setMaster(master, true); + isSummon = true; + } + } + + const Position &position = Lua::getPosition(L, 2); + const uint8_t stack = Lua::getNumber(L, 3, 1); + const bool extended = Lua::getBoolean(L, 4, false); + const bool force = Lua::getBoolean(L, 5, false); + const CreatureIconModifications_t icon = stack < 40 ? CreatureIconModifications_t::ReducedHealth : CreatureIconModifications_t::ReducedHealthExclamation; + if (g_game().placeCreature(monster, position, extended, force)) { + monster->setSkillLoss(stack == 40); + monster->setForgeStack(stack); + monster->setDropLoot(false); + monster->setIcon("soulpit", CreatureIcon(icon, stack < 40 ? stack : 0)); + monster->onSpawn(); + const auto &mtype = monster->getMonsterType(); + if (mtype && mtype->info.raceid > 0 && mtype->info.bosstiaryRace == BosstiaryRarity_t::RARITY_ARCHFOE) { + for (const auto &spectator : Spectators().find(monster->getPosition(), true)) { + if (const auto &tmpPlayer = spectator->getPlayer()) { + tmpPlayer->sendBosstiaryCooldownTimer(); + } + } + } + + Lua::pushUserdata(L, monster); + Lua::setMetatable(L, -1, "Monster"); + } else { + if (isSummon) { + monster->setMaster(nullptr); + } else { + } + lua_pushnil(L); + } + return 1; +} + int GameFunctions::luaGameGenerateNpc(lua_State* L) { // Game.generateNpc(npcName) const auto &npc = Npc::createNpc(Lua::getString(L, 1)); diff --git a/src/lua/functions/core/game/game_functions.hpp b/src/lua/functions/core/game/game_functions.hpp index 4a9521c826d..e70c7c0f6f1 100644 --- a/src/lua/functions/core/game/game_functions.hpp +++ b/src/lua/functions/core/game/game_functions.hpp @@ -46,6 +46,7 @@ class GameFunctions { static int luaGameCreateItem(lua_State* L); static int luaGameCreateContainer(lua_State* L); static int luaGameCreateMonster(lua_State* L); + static int luaGameCreateSoulPitMonster(lua_State* L); static int luaGameGenerateNpc(lua_State* L); static int luaGameCreateNpc(lua_State* L); static int luaGameCreateTile(lua_State* L); From 21b6a08b5e456963dad2bd41a58b5096684753cd Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Tue, 3 Dec 2024 00:33:12 +0000 Subject: [PATCH 13/95] Lua code format - (Stylua) --- data-otservbr-global/lib/others/soulpit.lua | 2 +- data/scripts/eventcallbacks/monster/ondroploot_soul_core.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/data-otservbr-global/lib/others/soulpit.lua b/data-otservbr-global/lib/others/soulpit.lua index c716e8c7817..c116ac67afc 100644 --- a/data-otservbr-global/lib/others/soulpit.lua +++ b/data-otservbr-global/lib/others/soulpit.lua @@ -13,7 +13,7 @@ SoulPit = { [1] = { stacks = { [1] = 6, - [40] = 1 + [40] = 1, }, }, [2] = { diff --git a/data/scripts/eventcallbacks/monster/ondroploot_soul_core.lua b/data/scripts/eventcallbacks/monster/ondroploot_soul_core.lua index 38bbd8ea670..41678f28c0e 100644 --- a/data/scripts/eventcallbacks/monster/ondroploot_soul_core.lua +++ b/data/scripts/eventcallbacks/monster/ondroploot_soul_core.lua @@ -40,7 +40,7 @@ function callback.monsterOnDropLoot(monster, corpse) lootTable = { [soulCoreId] = { count = 1, - } + }, } else return {} From b67867b533241bd3fb5f3cdf767db12fca1c4cf2 Mon Sep 17 00:00:00 2001 From: Pedro Henrique Alves Cruz Date: Mon, 2 Dec 2024 23:57:00 -0300 Subject: [PATCH 14/95] fix: conditions and spells --- data-otservbr-global/lib/others/soulpit.lua | 1 + .../scripts/actions/soulpit/soulpit_fight.lua | 35 ++++++++++--------- .../spells/monster/soulpit_opressor.lua | 2 +- src/creatures/combat/combat.cpp | 4 +++ src/creatures/combat/condition.cpp | 28 +++++++++++++-- src/creatures/combat/spells.cpp | 10 +++++- src/creatures/combat/spells.hpp | 2 +- src/creatures/creatures_definitions.hpp | 1 + 8 files changed, 61 insertions(+), 22 deletions(-) diff --git a/data-otservbr-global/lib/others/soulpit.lua b/data-otservbr-global/lib/others/soulpit.lua index c716e8c7817..e3da921ef9a 100644 --- a/data-otservbr-global/lib/others/soulpit.lua +++ b/data-otservbr-global/lib/others/soulpit.lua @@ -1,5 +1,6 @@ SoulPit = { encounter = nil, + kickEvent = nil, soulCores = Game.getSoulCoreItems(), requiredLevel = 8, playerPositions = { diff --git a/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua b/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua index 09303ccbd3a..9abcc28e8e9 100644 --- a/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua +++ b/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua @@ -10,6 +10,9 @@ function zoneEvent.afterLeave(zone, creature) SoulPit.encounter:reset() SoulPit.encounter = nil end + if SoulPit.kickEvent then + stopEvent(SoulPit.kickEvent) + end end for abilityName, abilityInfo in pairs(SoulPit.bossAbilities) do @@ -79,6 +82,22 @@ function soulPitAction.onUse(player, item, fromPosition, target, toPosition, isH zone = SoulPit.zone, }) + function encounter:onReset(position) + SoulPit.zone:removeMonsters() + + for _, player in pairs(SoulPit.zone:getPlayers()) do + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, string.format("You have defeated the core of the %s soul and unlocked its animus mastery!", monsterName)) + -- Add the monster animus mastery for the player. + end + + SoulPit.kickEvent = addEvent(function() + SoulPit.encounter = nil + for _, player in pairs(SoulPit.zone:getPlayers()) do + player:teleportTo(SoulPit.exit) + end + end, SoulPit.timeToKick) + end + SoulPit.encounter = encounter local function waveStart() @@ -123,22 +142,6 @@ function soulPitAction.onUse(player, item, fromPosition, target, toPosition, isH :autoAdvance({ delay = SoulPit.checkMonstersDelay, monstersKilled = true }) end - function encounter:onReset(position) - SoulPit.zone:removeMonsters() - - for _, player in pairs(SoulPit.zone:getPlayers()) do - player:sendTextMessage(MESSAGE_EVENT_ADVANCE, string.format("You have defeated the core of the %s soul and unlocked its animus mastery!", monsterName)) - -- Add the monster animus mastery for the player. - end - - addEvent(function() - SoulPit.encounter = nil - for _, player in pairs(SoulPit.zone:getPlayers()) do - player:teleportTo(SoulPit.exit) - end - end, SoulPit.timeToKick) - end - encounter:start() encounter:register() diff --git a/data-otservbr-global/scripts/spells/monster/soulpit_opressor.lua b/data-otservbr-global/scripts/spells/monster/soulpit_opressor.lua index afe2dae554e..bfbadc8a714 100644 --- a/data-otservbr-global/scripts/spells/monster/soulpit_opressor.lua +++ b/data-otservbr-global/scripts/spells/monster/soulpit_opressor.lua @@ -15,7 +15,7 @@ local combatFear = Combat() combatFear:setParameter(COMBAT_PARAM_EFFECT, CONST_ME_BLUE_GHOST) local area = createCombatArea(AREA_FEAR_OPRESSOR) -combatRoot:setArea(area) +combatFear:setArea(area) local condition = Condition(CONDITION_FEARED) condition:setParameter(CONDITION_PARAM_TICKS, 3000) diff --git a/src/creatures/combat/combat.cpp b/src/creatures/combat/combat.cpp index 6b7667a71a5..a70ef43a63a 100644 --- a/src/creatures/combat/combat.cpp +++ b/src/creatures/combat/combat.cpp @@ -641,6 +641,10 @@ void Combat::CombatHealthFunc(const std::shared_ptr &caster, const std } } + if (targetPlayer && damage.primary.type == COMBAT_HEALING) { + damage.primary.value *= targetPlayer->getBuff(BUFF_HEALINGRECEIVED) / 100.; + } + damage.damageMultiplier += attackerPlayer->wheel()->getMajorStatConditional("Divine Empowerment", WheelMajor_t::DAMAGE); g_logger().trace("Wheel Divine Empowerment damage multiplier {}", damage.damageMultiplier); } diff --git a/src/creatures/combat/condition.cpp b/src/creatures/combat/condition.cpp index 3cc72d0f4d0..9bb69dac9bc 100644 --- a/src/creatures/combat/condition.cpp +++ b/src/creatures/combat/condition.cpp @@ -206,6 +206,8 @@ bool Condition::executeCondition(const std::shared_ptr &creature, int3 } std::shared_ptr Condition::createCondition(ConditionId_t id, ConditionType_t type, int32_t ticks, int32_t param /* = 0*/, bool buff /* = false*/, uint32_t subId /* = 0*/, bool isPersistent /* = false*/) { + + std::shared_ptr conditionAttributes = nullptr; switch (type) { case CONDITION_POISON: case CONDITION_FIRE: @@ -251,6 +253,24 @@ std::shared_ptr Condition::createCondition(ConditionId_t id, Conditio case CONDITION_FEARED: return std::make_shared(id, type, ticks, buff, subId); + case CONDITION_LESSERHEX: + conditionAttributes = std::make_shared(id, type, ticks, buff, subId); + conditionAttributes->setParam(CONDITION_PARAM_BUFF_HEALINGRECEIVED, 50); + return conditionAttributes; + + case CONDITION_INTENSEHEX: + conditionAttributes = std::make_shared(id, type, ticks, buff, subId); + conditionAttributes->setParam(CONDITION_PARAM_BUFF_HEALINGRECEIVED, 50); + conditionAttributes->setParam(CONDITION_PARAM_BUFF_DAMAGEDEALT, 50); + return conditionAttributes; + + case CONDITION_GREATERHEX: + conditionAttributes = std::make_shared(id, type, ticks, buff, subId); + conditionAttributes->setParam(CONDITION_PARAM_BUFF_HEALINGRECEIVED, 50); + conditionAttributes->setParam(CONDITION_PARAM_BUFF_DAMAGEDEALT, 50); + conditionAttributes->setParam(CONDITION_PARAM_STAT_MAXHITPOINTSPERCENT, 60); + return conditionAttributes; + case CONDITION_ROOTED: case CONDITION_INFIGHT: case CONDITION_DRUNK: @@ -261,9 +281,6 @@ std::shared_ptr Condition::createCondition(ConditionId_t id, Conditio case CONDITION_CHANNELMUTEDTICKS: case CONDITION_YELLTICKS: case CONDITION_POWERLESS: - case CONDITION_LESSERHEX: - case CONDITION_INTENSEHEX: - case CONDITION_GREATERHEX: case CONDITION_PACIFIED: return std::make_shared(id, type, ticks, buff, subId); case CONDITION_BAKRAGORE: @@ -1022,6 +1039,11 @@ bool ConditionAttributes::setParam(ConditionParam_t param, int32_t value) { return true; } + case CONDITION_PARAM_BUFF_HEALINGRECEIVED: { + buffsPercent[BUFF_HEALINGRECEIVED] = std::max(0, value); + return true; + } + case CONDITION_PARAM_BUFF_DAMAGEDEALT: { buffsPercent[BUFF_DAMAGEDEALT] = std::max(0, value); return true; diff --git a/src/creatures/combat/spells.cpp b/src/creatures/combat/spells.cpp index f522eac86a6..8365428ef94 100644 --- a/src/creatures/combat/spells.cpp +++ b/src/creatures/combat/spells.cpp @@ -1053,11 +1053,15 @@ void Spell::setLockedPZ(bool b) { InstantSpell::InstantSpell() = default; -bool InstantSpell::playerCastInstant(const std::shared_ptr &player, std::string ¶m) const { +bool InstantSpell::playerCastInstant(const std::shared_ptr &player, std::string ¶m) { if (!playerSpellCheck(player)) { return false; } + if (player->hasCondition(CONDITION_POWERLESS) && getGroup() == SPELLGROUP_ATTACK) { + return false; + } + LuaVariant var; var.instantName = getName(); std::shared_ptr playerTarget = nullptr; @@ -1379,6 +1383,10 @@ bool RuneSpell::executeUse(const std::shared_ptr &player, const std::sha return false; } + if (player->hasCondition(CONDITION_POWERLESS) && getGroup() == SPELLGROUP_ATTACK) { + return false; + } + LuaVariant var; var.runeName = getName(); diff --git a/src/creatures/combat/spells.hpp b/src/creatures/combat/spells.hpp index 56ff1a330de..0dc9fa72efa 100644 --- a/src/creatures/combat/spells.hpp +++ b/src/creatures/combat/spells.hpp @@ -280,7 +280,7 @@ class Spell : public BaseSpell { class InstantSpell final : public Spell { public: InstantSpell(); - bool playerCastInstant(const std::shared_ptr &player, std::string ¶m) const; + bool playerCastInstant(const std::shared_ptr &player, std::string ¶m); bool castSpell(const std::shared_ptr &creature) override; bool castSpell(const std::shared_ptr &creature, const std::shared_ptr &target) override; diff --git a/src/creatures/creatures_definitions.hpp b/src/creatures/creatures_definitions.hpp index eba438ba44d..017a8db7c62 100644 --- a/src/creatures/creatures_definitions.hpp +++ b/src/creatures/creatures_definitions.hpp @@ -223,6 +223,7 @@ enum ConditionParam_t { CONDITION_PARAM_INCREASE_MANADRAINPERCENT = 80, CONDITION_PARAM_INCREASE_DROWNPERCENT = 81, CONDITION_PARAM_CHARM_CHANCE_MODIFIER = 82, + CONDITION_PARAM_BUFF_HEALINGRECEIVED = 83, }; enum stats_t { From 8c3842fb4abebc2430a95e88a96a8ba633822aa9 Mon Sep 17 00:00:00 2001 From: Pedro Henrique Alves Cruz Date: Tue, 3 Dec 2024 20:46:11 -0300 Subject: [PATCH 15/95] improve: monsters soulpit creation --- data-otservbr-global/lib/others/soulpit.lua | 6 ++-- .../scripts/actions/soulpit/soulpit_fight.lua | 6 +++- src/creatures/monsters/monster.cpp | 32 +++++++++++++++---- src/creatures/monsters/monster.hpp | 6 ++++ .../functions/core/game/game_functions.cpp | 6 +--- 5 files changed, 42 insertions(+), 14 deletions(-) diff --git a/data-otservbr-global/lib/others/soulpit.lua b/data-otservbr-global/lib/others/soulpit.lua index 16c1e739872..3f17178354a 100644 --- a/data-otservbr-global/lib/others/soulpit.lua +++ b/data-otservbr-global/lib/others/soulpit.lua @@ -13,8 +13,7 @@ SoulPit = { waves = { [1] = { stacks = { - [1] = 6, - [40] = 1, + [1] = 7, }, }, [2] = { @@ -66,6 +65,9 @@ SoulPit = { checkMonstersDelay = 4.5 * 1000, -- 4.5 seconds | The check delay should never be less than the timeToSpawnMonsters. timeToSpawnMonsters = 4 * 1000, -- 4 seconds totalMonsters = 7, + obeliskActive = 49175, + obeliskInactive = 49174, + obeliskPosition = Position(32371, 31154, 8), bossPosition = Position(32372, 31135, 8), exit = Position(32371, 31164, 8), zone = Zone("soulpit"), diff --git a/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua b/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua index 9abcc28e8e9..0a803e4ec29 100644 --- a/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua +++ b/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua @@ -12,6 +12,7 @@ function zoneEvent.afterLeave(zone, creature) end if SoulPit.kickEvent then stopEvent(SoulPit.kickEvent) + SoulPit.obeliskPosition:transformItem(SoulPit.obeliskActive, SoulPit.obeliskInactive) end end @@ -25,7 +26,8 @@ zoneEvent:register() local soulPitAction = Action() function soulPitAction.onUse(player, item, fromPosition, target, toPosition, isHotkey) - if not target or target:getId() ~= 49174 then + logger.warn(item:getName()) + if not target or target:getId() ~= SoulPit.obeliskInactive then return false end @@ -66,6 +68,7 @@ function soulPitAction.onUse(player, item, fromPosition, target, toPosition, isH lever:checkPositions() if lever:checkConditions() then lever:teleportPlayers() + SoulPit.obeliskPosition:transformItem(SoulPit.obeliskInactive, SoulPit.obeliskActive) end local monsterName = string.gsub(item:getName(), " soul core", "") @@ -95,6 +98,7 @@ function soulPitAction.onUse(player, item, fromPosition, target, toPosition, isH for _, player in pairs(SoulPit.zone:getPlayers()) do player:teleportTo(SoulPit.exit) end + SoulPit.obeliskPosition:transformItem(SoulPit.obeliskActive, SoulPit.obeliskInactive) end, SoulPit.timeToKick) end diff --git a/src/creatures/monsters/monster.cpp b/src/creatures/monsters/monster.cpp index d396bb47744..ae1f997076a 100644 --- a/src/creatures/monsters/monster.cpp +++ b/src/creatures/monsters/monster.cpp @@ -1324,13 +1324,15 @@ void Monster::onThinkDefense(uint32_t interval) { } const auto &summon = Monster::createMonster(summonName); - if (summon) { - if (g_game().placeCreature(summon, getPosition(), false, summonForce)) { - summon->setMaster(static_self_cast(), true); - g_game().addMagicEffect(getPosition(), CONST_ME_MAGIC_BLUE); - g_game().addMagicEffect(summon->getPosition(), CONST_ME_TELEPORT); - g_game().sendSingleSoundEffect(summon->getPosition(), SoundEffect_t::MONSTER_SPELL_SUMMON, getMonster()); + if (summon && g_game().placeCreature(summon, getPosition(), false, summonForce)) { + if (getSoulPit()) { + const auto stack = getForgeStack(); + summon->setSoulPitStack(stack, true); } + summon->setMaster(static_self_cast(), true); + g_game().addMagicEffect(getPosition(), CONST_ME_MAGIC_BLUE); + g_game().addMagicEffect(summon->getPosition(), CONST_ME_TELEPORT); + g_game().sendSingleSoundEffect(summon->getPosition(), SoundEffect_t::MONSTER_SPELL_SUMMON, getMonster()); } } } @@ -2218,6 +2220,24 @@ void Monster::setHazardSystemDefenseBoost(bool value) { hazardDefenseBoost = value; } +bool Monster::getSoulPit() const { + return soulPit; +} + +void Monster::setSoulPit(bool value) { + soulPit = value; +} + +void Monster::setSoulPitStack(uint8_t stack, bool isSummon /* = false */) { + const bool isBoss = stack == 40; + const CreatureIconModifications_t icon = isBoss ? CreatureIconModifications_t::ReducedHealthExclamation : CreatureIconModifications_t::ReducedHealth; + setForgeStack(stack); + setIcon("soulpit", CreatureIcon(icon, isBoss ? 0 : stack)); + setSoulPit(true); + setDropLoot(false); + setSkillLoss(isBoss && !isSummon); +} + bool Monster::canWalkTo(Position pos, Direction moveDirection) { pos = getNextPosition(moveDirection, pos); if (isInSpawnRange(pos)) { diff --git a/src/creatures/monsters/monster.hpp b/src/creatures/monsters/monster.hpp index c9e5745da69..4c9a2e6c308 100644 --- a/src/creatures/monsters/monster.hpp +++ b/src/creatures/monsters/monster.hpp @@ -175,6 +175,10 @@ class Monster final : public Creature { void setHazardSystemDefenseBoost(bool value); // Hazard end + bool getSoulPit() const; + void setSoulPit(bool value); + void setSoulPitStack(uint8_t stack, bool isSummon = false); + void updateTargetList(); void clearTargetList(); void clearFriendList(); @@ -285,6 +289,8 @@ class Monster final : public Creature { bool hazardDamageBoost = false; bool hazardDefenseBoost = false; + bool soulPit = false; + bool m_isDead = false; bool m_isImmune = false; diff --git a/src/lua/functions/core/game/game_functions.cpp b/src/lua/functions/core/game/game_functions.cpp index 97e97e996a2..8182811bd9e 100644 --- a/src/lua/functions/core/game/game_functions.cpp +++ b/src/lua/functions/core/game/game_functions.cpp @@ -569,12 +569,8 @@ int GameFunctions::luaGameCreateSoulPitMonster(lua_State* L) { const uint8_t stack = Lua::getNumber(L, 3, 1); const bool extended = Lua::getBoolean(L, 4, false); const bool force = Lua::getBoolean(L, 5, false); - const CreatureIconModifications_t icon = stack < 40 ? CreatureIconModifications_t::ReducedHealth : CreatureIconModifications_t::ReducedHealthExclamation; if (g_game().placeCreature(monster, position, extended, force)) { - monster->setSkillLoss(stack == 40); - monster->setForgeStack(stack); - monster->setDropLoot(false); - monster->setIcon("soulpit", CreatureIcon(icon, stack < 40 ? stack : 0)); + monster->setSoulPitStack(stack); monster->onSpawn(); const auto &mtype = monster->getMonsterType(); if (mtype && mtype->info.raceid > 0 && mtype->info.bosstiaryRace == BosstiaryRarity_t::RARITY_ARCHFOE) { From d15f6660d7d42318f976b184f001661ca04028ec Mon Sep 17 00:00:00 2001 From: Pedro Henrique Alves Cruz Date: Tue, 3 Dec 2024 23:47:07 -0300 Subject: [PATCH 16/95] fix: soulpit creatureevents --- .../monster/soulpit_creatureevents.lua | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 data-otservbr-global/scripts/creaturescripts/monster/soulpit_creatureevents.lua diff --git a/data-otservbr-global/scripts/creaturescripts/monster/soulpit_creatureevents.lua b/data-otservbr-global/scripts/creaturescripts/monster/soulpit_creatureevents.lua new file mode 100644 index 00000000000..2dcb067a7e0 --- /dev/null +++ b/data-otservbr-global/scripts/creaturescripts/monster/soulpit_creatureevents.lua @@ -0,0 +1,42 @@ +local enrage = CreatureEvent("enrageSoulPit") +function enrage.onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType) + if not creature or not creature:isMonster() then + return true + end + + local healthPercentage = creature:getHealth() / creature:getMaxHealth() + + if healthPercentage >= 0.6 and healthPercentage <= 0.8 then + primaryDamage = primaryDamage * 0.9 -- 10% damage reduction + secondaryDamage = secondaryDamage * 0.9 -- 10% damage reduction + elseif healthPercentage >= 0.4 and healthPercentage < 0.6 then + primaryDamage = primaryDamage * 0.75 -- 25% damage reduction + secondaryDamage = secondaryDamage * 0.75 -- 25% damage reduction + elseif healthPercentage >= 0.2 and healthPercentage < 0.4 then + primaryDamage = primaryDamage * 0.6 -- 40% damage reduction + secondaryDamage = secondaryDamage * 0.6 -- 40% damage reduction + elseif healthPercentage > 0 and healthPercentage < 0.2 then + primaryDamage = primaryDamage * 0.4 -- 60% damage reduction + secondaryDamage = secondaryDamage * 0.4 -- 60% damage reduction + end + + return primaryDamage, primaryType, secondaryDamage, secondaryType +end +enrage:register() + +local overpower = CreatureEvent("overpowerSoulPit") +function overpower.onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType) + if creature and attacker and attacker:isMonster() and attacker:getForgeStack() == 40 then + primaryDamage = primaryDamage * 1.1 + secondaryDamage = secondaryDamage * 1.1 + creature:getPosition():sendMagicEffect(CONST_ME_CRITICAL_DAMAGE) + end + + return primaryDamage, primaryType, secondaryDamage, secondaryType +end +overpower:register() + +local opressor = CreatureEvent("opressorSoulPit") +function opressor.onThink() +end +opressor:register() From 2cef8b49a22dcde524c73d0858b3d9b962fb3d85 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Wed, 4 Dec 2024 02:48:04 +0000 Subject: [PATCH 17/95] Lua code format - (Stylua) --- .../scripts/creaturescripts/monster/soulpit_creatureevents.lua | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/data-otservbr-global/scripts/creaturescripts/monster/soulpit_creatureevents.lua b/data-otservbr-global/scripts/creaturescripts/monster/soulpit_creatureevents.lua index 2dcb067a7e0..6a228144942 100644 --- a/data-otservbr-global/scripts/creaturescripts/monster/soulpit_creatureevents.lua +++ b/data-otservbr-global/scripts/creaturescripts/monster/soulpit_creatureevents.lua @@ -37,6 +37,5 @@ end overpower:register() local opressor = CreatureEvent("opressorSoulPit") -function opressor.onThink() -end +function opressor.onThink() end opressor:register() From 1d484a954a14843a53310b1801e37fc24d58f812 Mon Sep 17 00:00:00 2001 From: Pedro Henrique Alves Cruz Date: Wed, 4 Dec 2024 14:01:58 -0300 Subject: [PATCH 18/95] fix: bestiary on battle and improvements --- .../spells/monster/soulpit_intensehex.lua | 3 ++- .../spells/monster/soulpit_opressor.lua | 3 ++- .../spells/monster/soulpit_powerless.lua | 3 ++- src/creatures/players/player.cpp | 4 +++- .../creatures/monster/monster_functions.cpp | 19 +++++++++++++++++++ .../creatures/monster/monster_functions.hpp | 2 ++ 6 files changed, 30 insertions(+), 4 deletions(-) diff --git a/data-otservbr-global/scripts/spells/monster/soulpit_intensehex.lua b/data-otservbr-global/scripts/spells/monster/soulpit_intensehex.lua index b9ce99fdd89..6b26cdd45d3 100644 --- a/data-otservbr-global/scripts/spells/monster/soulpit_intensehex.lua +++ b/data-otservbr-global/scripts/spells/monster/soulpit_intensehex.lua @@ -9,7 +9,8 @@ combat:addCondition(condition) local spell = Spell("instant") function spell.onCastSpell(creature, var) - if table.contains(creature:getEvents(CREATURE_EVENT_THINK), "opressorSoulPit") then + local monster = creature:getMonster() + if monster and monster:soulPit() and table.contains(creature:getEvents(CREATURE_EVENT_THINK), "opressorSoulPit") then return combat:execute(creature, var) end diff --git a/data-otservbr-global/scripts/spells/monster/soulpit_opressor.lua b/data-otservbr-global/scripts/spells/monster/soulpit_opressor.lua index bfbadc8a714..293c8859dea 100644 --- a/data-otservbr-global/scripts/spells/monster/soulpit_opressor.lua +++ b/data-otservbr-global/scripts/spells/monster/soulpit_opressor.lua @@ -26,7 +26,8 @@ local spell = Spell("instant") local combats = { combatRoot, combatFear } function spell.onCastSpell(creature, var) - if table.contains(creature:getEvents(CREATURE_EVENT_THINK), "opressorSoulPit") then + local monster = creature:getMonster() + if monster and monster:soulPit() and table.contains(creature:getEvents(CREATURE_EVENT_THINK), "opressorSoulPit") then for _, combat in pairs(combats) do combat:execute(creature, var) end diff --git a/data-otservbr-global/scripts/spells/monster/soulpit_powerless.lua b/data-otservbr-global/scripts/spells/monster/soulpit_powerless.lua index 2edbe0b6d8b..7ed71742f8f 100644 --- a/data-otservbr-global/scripts/spells/monster/soulpit_powerless.lua +++ b/data-otservbr-global/scripts/spells/monster/soulpit_powerless.lua @@ -9,7 +9,8 @@ combat:addCondition(condition) local spell = Spell("instant") function spell.onCastSpell(creature, var) - if table.contains(creature:getEvents(CREATURE_EVENT_THINK), "opressorSoulPit") then + local monster = creature:getMonster() + if monster and monster:soulPit() and table.contains(creature:getEvents(CREATURE_EVENT_THINK), "opressorSoulPit") then return combat:execute(creature, var) end diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index 4335db2bd53..e6764b496d3 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -5744,7 +5744,9 @@ bool Player::onKilledMonster(const std::shared_ptr &monster) { return false; } addHuntingTaskKill(mType); - addBestiaryKill(mType); + if (!monster->getSoulPit()) { + addBestiaryKill(mType); + } addBosstiaryKill(mType); return false; } diff --git a/src/lua/functions/creatures/monster/monster_functions.cpp b/src/lua/functions/creatures/monster/monster_functions.cpp index 3f5d0c25ea8..77793f8508b 100644 --- a/src/lua/functions/creatures/monster/monster_functions.cpp +++ b/src/lua/functions/creatures/monster/monster_functions.cpp @@ -66,6 +66,8 @@ void MonsterFunctions::init(lua_State* L) { Lua::registerMethod(L, "Monster", "hazardDamageBoost", MonsterFunctions::luaMonsterHazardDamageBoost); Lua::registerMethod(L, "Monster", "hazardDefenseBoost", MonsterFunctions::luaMonsterHazardDefenseBoost); + Lua::registerMethod(L, "Monster", "soulPit", MonsterFunctions::luaMonsterSoulPit); + Lua::registerMethod(L, "Monster", "addReflectElement", MonsterFunctions::luaMonsterAddReflectElement); Lua::registerMethod(L, "Monster", "addDefense", MonsterFunctions::luaMonsterAddDefense); Lua::registerMethod(L, "Monster", "getDefense", MonsterFunctions::luaMonsterGetDefense); @@ -700,6 +702,23 @@ int MonsterFunctions::luaMonsterHazardDefenseBoost(lua_State* L) { return 1; } +int MonsterFunctions::luaMonsterSoulPit(lua_State* L) { + // get: monster:soulPit() ; set: monster:soulPit(hazard) + const auto &monster = Lua::getUserdataShared(L, 1); + const bool soulPit = Lua::getBoolean(L, 2, false); + if (monster) { + if (lua_gettop(L) == 1) { + Lua::pushBoolean(L, monster->getSoulPit()); + } else { + monster->setSoulPit(soulPit); + Lua::pushBoolean(L, monster->getSoulPit()); + } + } else { + lua_pushnil(L); + } + return 1; +} + int MonsterFunctions::luaMonsterAddReflectElement(lua_State* L) { // monster:addReflectElement(type, percent) const auto &monster = Lua::getUserdataShared(L, 1); diff --git a/src/lua/functions/creatures/monster/monster_functions.hpp b/src/lua/functions/creatures/monster/monster_functions.hpp index 4ba696e941b..fdc45d7732c 100644 --- a/src/lua/functions/creatures/monster/monster_functions.hpp +++ b/src/lua/functions/creatures/monster/monster_functions.hpp @@ -76,6 +76,8 @@ class MonsterFunctions { static int luaMonsterAddDefense(lua_State* L); static int luaMonsterGetDefense(lua_State* L); + static int luaMonsterSoulPit(lua_State* L); + static int luaMonsterIsDead(lua_State* L); static int luaMonsterImmune(lua_State* L); From 89efd86761260e24466efdb834dbac484673e2b5 Mon Sep 17 00:00:00 2001 From: Pedro Henrique Alves Cruz Date: Wed, 4 Dec 2024 14:06:46 -0300 Subject: [PATCH 19/95] fix: soulpit bosstiary and hunting task points --- src/creatures/players/player.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index e6764b496d3..a41b61ed3d3 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -5743,11 +5743,11 @@ bool Player::onKilledMonster(const std::shared_ptr &monster) { g_logger().error("[{}] Monster type is null.", __FUNCTION__); return false; } - addHuntingTaskKill(mType); if (!monster->getSoulPit()) { + addHuntingTaskKill(mType); addBestiaryKill(mType); + addBosstiaryKill(mType); } - addBosstiaryKill(mType); return false; } From dd2f9672cf86dcc2b8b6f52328ef1398062c8813 Mon Sep 17 00:00:00 2001 From: Felipe Paluco Date: Wed, 4 Dec 2024 14:42:23 -0300 Subject: [PATCH 20/95] feat: add upgrade soul prism mechanic --- data-otservbr-global/lib/others/soulpit.lua | 91 +++++++++++---- .../scripts/actions/soulpit/soulpit_fight.lua | 4 +- data/items/items.xml | 4 + data/scripts/actions/items/soul_prism.lua | 106 ++++++++++++++++++ .../monster/ondroploot_soul_core.lua | 5 +- .../monster/monster_type_functions.cpp | 1 + 6 files changed, 184 insertions(+), 27 deletions(-) create mode 100644 data/scripts/actions/items/soul_prism.lua diff --git a/data-otservbr-global/lib/others/soulpit.lua b/data-otservbr-global/lib/others/soulpit.lua index 3f17178354a..d9554c2ef3f 100644 --- a/data-otservbr-global/lib/others/soulpit.lua +++ b/data-otservbr-global/lib/others/soulpit.lua @@ -1,65 +1,110 @@ SoulPit = { + SoulCoresConfiguration = { + chanceToGetSameMonsterSoulCore = 30, -- 30% + chanceToDropSoulCore = 50, -- 50% + chanceToGetOminousSoulCore = 5, -- 5% + monsterVariationsSoulCore = { + ["Horse"] = "horse soul core (taupe)", + ["Brown Horse"] = "horse soul core (brown)", + ["Grey Horse"] = "horse soul core (gray)", + ["Nomad"] = "nomad soul core (basic)", + ["Nomad Blue"] = "nomad soul core (blue)", + ["Nomad Female"] = "nomad soul core (female)", + ["Purple Butterfly"] = "butterfly soul core (purple)", + ["Butterfly"] = "butterfly soul core (blue)", + ["Blue Butterfly"] = "butterfly soul core (blue)", + ["Red Butterfly"] = "butterfly soul core (red)" + }, + monstersDifficulties = { + ["Harmless"] = 1, + ["Trivial"] = 2, + ["Easy"] = 3, + ["Medium"] = 4, + ["Hard"] = 5, + ["Challenge"] = 6 + } + }, encounter = nil, kickEvent = nil, soulCores = Game.getSoulCoreItems(), requiredLevel = 8, playerPositions = { - { pos = Position(32371, 31155, 8), teleport = Position(32373, 31138, 8), effect = CONST_ME_TELEPORT }, - { pos = Position(32371, 31156, 8), teleport = Position(32373, 31138, 8), effect = CONST_ME_TELEPORT }, - { pos = Position(32371, 31157, 8), teleport = Position(32373, 31138, 8), effect = CONST_ME_TELEPORT }, - { pos = Position(32371, 31158, 8), teleport = Position(32373, 31138, 8), effect = CONST_ME_TELEPORT }, - { pos = Position(32371, 31159, 8), teleport = Position(32373, 31138, 8), effect = CONST_ME_TELEPORT }, + { + pos = Position(32371, 31155, 8), + teleport = Position(32373, 31138, 8), + effect = CONST_ME_TELEPORT + }, + { + pos = Position(32371, 31156, 8), + teleport = Position(32373, 31138, 8), + effect = CONST_ME_TELEPORT + }, + { + pos = Position(32371, 31157, 8), + teleport = Position(32373, 31138, 8), + effect = CONST_ME_TELEPORT + }, + { + pos = Position(32371, 31158, 8), + teleport = Position(32373, 31138, 8), + effect = CONST_ME_TELEPORT + }, + { + pos = Position(32371, 31159, 8), + teleport = Position(32373, 31138, 8), + effect = CONST_ME_TELEPORT + } }, waves = { [1] = { stacks = { - [1] = 7, - }, + [1] = 7 + } }, [2] = { stacks = { [1] = 4, - [5] = 3, - }, + [5] = 3 + } }, [3] = { stacks = { [1] = 5, - [15] = 2, - }, + [15] = 2 + } }, [4] = { stacks = { [1] = 3, [5] = 3, - [40] = 1, - }, - }, + [40] = 1 + } + } }, effects = { [1] = CONST_ME_TELEPORT, [5] = CONST_ME_ORANGETELEPORT, [15] = CONST_ME_REDTELEPORT, - [40] = CONST_ME_PURPLETELEPORT, + [40] = CONST_ME_PURPLETELEPORT }, possibleAbilities = { "overpowerSoulPit", "enrageSoulPit", - "opressorSoulPit", + "opressorSoulPit" }, bossAbilities = { - ["overpowerSoulPit"] = { + overpowerSoulPit = { player = true, - monster = false, + monster = false }, - ["enrageSoulPit"] = { + enrageSoulPit = { player = false, - monster = true, + monster = true }, - ["opressorSoulPit"] = { + opressorSoulPit = { player = false, - monster = true, - }, + monster = true + } }, timeToKick = 3 * 1000, -- 3 seconds checkMonstersDelay = 4.5 * 1000, -- 4.5 seconds | The check delay should never be less than the timeToSpawnMonsters. diff --git a/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua b/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua index 0a803e4ec29..d4ba27b4735 100644 --- a/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua +++ b/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua @@ -153,6 +153,8 @@ function soulPitAction.onUse(player, item, fromPosition, target, toPosition, isH end for _, itemType in pairs(SoulPit.soulCores) do - soulPitAction:id(itemType:getId()) + if itemType:getId() ~= 49164 then -- TO-DO: currently Game.getSoulCoreItems() it's returning soul prism item in the results, we don't want this. + soulPitAction:id(itemType:getId()) + end end soulPitAction:register() diff --git a/data/items/items.xml b/data/items/items.xml index c1b8e8a63ba..46f1c95a9d3 100644 --- a/data/items/items.xml +++ b/data/items/items.xml @@ -79730,5 +79730,9 @@ Granted by TibiaGoals.com"/> + + + + diff --git a/data/scripts/actions/items/soul_prism.lua b/data/scripts/actions/items/soul_prism.lua new file mode 100644 index 00000000000..b4db7df71a9 --- /dev/null +++ b/data/scripts/actions/items/soul_prism.lua @@ -0,0 +1,106 @@ +local soulPrism = Action() + +local function getNextDifficultyLevel(currentLevel) + for level, value in pairs(SoulPit.SoulCoresConfiguration.monstersDifficulties) do + if value == currentLevel + 1 then + return level + end + end + return nil +end + +local function getPreviousDifficultyLevel(currentLevel) + for level, value in pairs(SoulPit.SoulCoresConfiguration.monstersDifficulties) do + if value == currentLevel - 1 then + return level + end + end + return nil +end + +local function getSoulCoreItemForMonster(monsterName) + local lowerMonsterName = monsterName:lower() + local soulCoreName = SoulPit.SoulCoresConfiguration.monsterVariationsSoulCore[monsterName] + + if soulCoreName then + local newSoulCoreId = getItemIdByName(soulCoreName) + if newSoulCoreId then + return newSoulCoreId + end + else + local newMonsterSoulCore = monsterName .. " soul core" + local newSoulCoreId = getItemIdByName(newMonsterSoulCore) + if newSoulCoreId then + return newSoulCoreId + end + end + + return false +end + +function soulPrism.onUse(player, item, fromPosition, target, toPosition, isHotkey) + local itemName = target:getName() + local monsterName = itemName:match("^(.-) soul core") + + if not monsterName then + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You can only use Soul Prism with a Soul Core.") + player:getPosition():sendMagicEffect(CONST_ME_POFF) + return false + end + + local monsterType = MonsterType(monsterName) + if not monsterType then + player:sendTextMessage(MESSAGE_GAME_HIGHLIGHT, "Invalid monster type. Please contact an administrator.") + player:getPosition():sendMagicEffect(CONST_ME_POFF) + return false + end + + local currentDifficulty = monsterType:getBestiaryStars() + local nextDifficultyLevel = getNextDifficultyLevel(currentDifficulty) + local nextDifficultyMonsters = nil + + if nextDifficultyLevel then + nextDifficultyMonsters = monsterType:getMonstersByBestiaryStars(SoulPit.SoulCoresConfiguration.monstersDifficulties[nextDifficultyLevel]) + else + nextDifficultyLevel = currentDifficulty + nextDifficultyMonsters = monsterType:getMonstersByBestiaryStars(SoulPit.SoulCoresConfiguration.monstersDifficulties[currentDifficulty]) + end + + if #nextDifficultyMonsters == 0 then + player:sendTextMessage(MESSAGE_GAME_HIGHLIGHT, "No monsters available for the next difficulty level. Please contact an administrator.") + player:getPosition():sendMagicEffect(CONST_ME_POFF) + return false + end + + local newMonsterType = nextDifficultyMonsters[math.random(#nextDifficultyMonsters)] + local newSoulCoreItem = getSoulCoreItemForMonster(newMonsterType:getName()) + if not newSoulCoreItem then -- Retry a second time. + newSoulCoreItem = getSoulCoreItemForMonster(newMonsterType:getName()) + if not newSoulCoreItem then + player:sendTextMessage(MESSAGE_GAME_HIGHLIGHT, "Failed to generate a Soul Core. Please contact an administrator.") + player:getPosition():sendMagicEffect(CONST_ME_POFF) + return false + end + end + + if player:getFreeCapacity() < ItemType(newSoulCoreItem):getWeight() then + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You do not have enough capacity.") + player:getPosition():sendMagicEffect(CONST_ME_POFF) + return false + end + + if math.random(100) <= SoulPit.SoulCoresConfiguration.chanceToGetOminousSoulCore then + player:addItem(49163, 1) + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You have received an Ominous Soul Core.") + else + player:addItem(newSoulCoreItem, 1) + player:removeItem(target:getId(), 1) + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You have received a " .. newMonsterType:getName() .. " soul core.") + end + player:removeItem(item:getId(), 1) + player:getPosition():sendMagicEffect(CONST_ME_MAGIC_BLUE) + return true +end + +soulPrism:id(49164) +soulPrism:register() \ No newline at end of file diff --git a/data/scripts/eventcallbacks/monster/ondroploot_soul_core.lua b/data/scripts/eventcallbacks/monster/ondroploot_soul_core.lua index 41678f28c0e..eafa067bc98 100644 --- a/data/scripts/eventcallbacks/monster/ondroploot_soul_core.lua +++ b/data/scripts/eventcallbacks/monster/ondroploot_soul_core.lua @@ -1,5 +1,4 @@ local callback = EventCallback("MonsterOnDropLootSoulCore") -local soulCores = Game.getSoulCoreItems() function callback.monsterOnDropLoot(monster, corpse) if not monster or not corpse then @@ -14,11 +13,11 @@ function callback.monsterOnDropLoot(monster, corpse) end local soulCoreId = nil - local trySameMonsterSoulCore = math.random() <= 0.3 -- 30% of chance to drop the same monster soul core | WIP: Externalize this to a lib like libs/soulpit.lua + local trySameMonsterSoulCore = math.random(100) <= SoulPit.SoulCoresConfiguration.chanceToGetSameMonsterSoulCore local mType = monster:getType() local lootTable = {} - if math.random() < 0.5 then -- WIP: Externalize this to a lib like libs/soulpit.lua + if math.random(100) < SoulPit.SoulCoresConfiguration.chanceToDropSoulCore then if trySameMonsterSoulCore then local itemName = monster:getName():lower() .. " soul core" soulCoreId = getItemIdByName(itemName) diff --git a/src/lua/functions/creatures/monster/monster_type_functions.cpp b/src/lua/functions/creatures/monster/monster_type_functions.cpp index 3cdd75723b9..86749cfc98d 100644 --- a/src/lua/functions/creatures/monster/monster_type_functions.cpp +++ b/src/lua/functions/creatures/monster/monster_type_functions.cpp @@ -1896,6 +1896,7 @@ int MonsterTypeFunctions::luaMonsterTypeGetMonstersByBestiaryStars(lua_State* L) int index = 0; for (const auto &monsterType : monstersByStars) { Lua::pushUserdata(L, monsterType); + Lua::setMetatable(L, -1, "MonsterType"); lua_rawseti(L, -2, ++index); } return 1; From 09aef6be8ce2ccd05deb7ebbf63ec3f8f16a09db Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Wed, 4 Dec 2024 17:43:05 +0000 Subject: [PATCH 21/95] Lua code format - (Stylua) --- data-otservbr-global/lib/others/soulpit.lua | 48 +++--- data/scripts/actions/items/soul_prism.lua | 168 ++++++++++---------- 2 files changed, 108 insertions(+), 108 deletions(-) diff --git a/data-otservbr-global/lib/others/soulpit.lua b/data-otservbr-global/lib/others/soulpit.lua index d9554c2ef3f..2628c1307be 100644 --- a/data-otservbr-global/lib/others/soulpit.lua +++ b/data-otservbr-global/lib/others/soulpit.lua @@ -13,7 +13,7 @@ SoulPit = { ["Purple Butterfly"] = "butterfly soul core (purple)", ["Butterfly"] = "butterfly soul core (blue)", ["Blue Butterfly"] = "butterfly soul core (blue)", - ["Red Butterfly"] = "butterfly soul core (red)" + ["Red Butterfly"] = "butterfly soul core (red)", }, monstersDifficulties = { ["Harmless"] = 1, @@ -21,8 +21,8 @@ SoulPit = { ["Easy"] = 3, ["Medium"] = 4, ["Hard"] = 5, - ["Challenge"] = 6 - } + ["Challenge"] = 6, + }, }, encounter = nil, kickEvent = nil, @@ -32,79 +32,79 @@ SoulPit = { { pos = Position(32371, 31155, 8), teleport = Position(32373, 31138, 8), - effect = CONST_ME_TELEPORT + effect = CONST_ME_TELEPORT, }, { pos = Position(32371, 31156, 8), teleport = Position(32373, 31138, 8), - effect = CONST_ME_TELEPORT + effect = CONST_ME_TELEPORT, }, { pos = Position(32371, 31157, 8), teleport = Position(32373, 31138, 8), - effect = CONST_ME_TELEPORT + effect = CONST_ME_TELEPORT, }, { pos = Position(32371, 31158, 8), teleport = Position(32373, 31138, 8), - effect = CONST_ME_TELEPORT + effect = CONST_ME_TELEPORT, }, { pos = Position(32371, 31159, 8), teleport = Position(32373, 31138, 8), - effect = CONST_ME_TELEPORT - } + effect = CONST_ME_TELEPORT, + }, }, waves = { [1] = { stacks = { - [1] = 7 - } + [1] = 7, + }, }, [2] = { stacks = { [1] = 4, - [5] = 3 - } + [5] = 3, + }, }, [3] = { stacks = { [1] = 5, - [15] = 2 - } + [15] = 2, + }, }, [4] = { stacks = { [1] = 3, [5] = 3, - [40] = 1 - } - } + [40] = 1, + }, + }, }, effects = { [1] = CONST_ME_TELEPORT, [5] = CONST_ME_ORANGETELEPORT, [15] = CONST_ME_REDTELEPORT, - [40] = CONST_ME_PURPLETELEPORT + [40] = CONST_ME_PURPLETELEPORT, }, possibleAbilities = { "overpowerSoulPit", "enrageSoulPit", - "opressorSoulPit" + "opressorSoulPit", }, bossAbilities = { overpowerSoulPit = { player = true, - monster = false + monster = false, }, enrageSoulPit = { player = false, - monster = true + monster = true, }, opressorSoulPit = { player = false, - monster = true - } + monster = true, + }, }, timeToKick = 3 * 1000, -- 3 seconds checkMonstersDelay = 4.5 * 1000, -- 4.5 seconds | The check delay should never be less than the timeToSpawnMonsters. diff --git a/data/scripts/actions/items/soul_prism.lua b/data/scripts/actions/items/soul_prism.lua index b4db7df71a9..7c092b8bdb0 100644 --- a/data/scripts/actions/items/soul_prism.lua +++ b/data/scripts/actions/items/soul_prism.lua @@ -1,106 +1,106 @@ local soulPrism = Action() local function getNextDifficultyLevel(currentLevel) - for level, value in pairs(SoulPit.SoulCoresConfiguration.monstersDifficulties) do - if value == currentLevel + 1 then - return level - end - end - return nil + for level, value in pairs(SoulPit.SoulCoresConfiguration.monstersDifficulties) do + if value == currentLevel + 1 then + return level + end + end + return nil end local function getPreviousDifficultyLevel(currentLevel) - for level, value in pairs(SoulPit.SoulCoresConfiguration.monstersDifficulties) do - if value == currentLevel - 1 then - return level - end - end - return nil + for level, value in pairs(SoulPit.SoulCoresConfiguration.monstersDifficulties) do + if value == currentLevel - 1 then + return level + end + end + return nil end local function getSoulCoreItemForMonster(monsterName) - local lowerMonsterName = monsterName:lower() - local soulCoreName = SoulPit.SoulCoresConfiguration.monsterVariationsSoulCore[monsterName] - - if soulCoreName then - local newSoulCoreId = getItemIdByName(soulCoreName) - if newSoulCoreId then - return newSoulCoreId - end - else - local newMonsterSoulCore = monsterName .. " soul core" - local newSoulCoreId = getItemIdByName(newMonsterSoulCore) - if newSoulCoreId then - return newSoulCoreId - end - end - - return false + local lowerMonsterName = monsterName:lower() + local soulCoreName = SoulPit.SoulCoresConfiguration.monsterVariationsSoulCore[monsterName] + + if soulCoreName then + local newSoulCoreId = getItemIdByName(soulCoreName) + if newSoulCoreId then + return newSoulCoreId + end + else + local newMonsterSoulCore = monsterName .. " soul core" + local newSoulCoreId = getItemIdByName(newMonsterSoulCore) + if newSoulCoreId then + return newSoulCoreId + end + end + + return false end function soulPrism.onUse(player, item, fromPosition, target, toPosition, isHotkey) - local itemName = target:getName() - local monsterName = itemName:match("^(.-) soul core") - - if not monsterName then - player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You can only use Soul Prism with a Soul Core.") - player:getPosition():sendMagicEffect(CONST_ME_POFF) - return false - end + local itemName = target:getName() + local monsterName = itemName:match("^(.-) soul core") + + if not monsterName then + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You can only use Soul Prism with a Soul Core.") + player:getPosition():sendMagicEffect(CONST_ME_POFF) + return false + end - local monsterType = MonsterType(monsterName) - if not monsterType then - player:sendTextMessage(MESSAGE_GAME_HIGHLIGHT, "Invalid monster type. Please contact an administrator.") - player:getPosition():sendMagicEffect(CONST_ME_POFF) - return false - end + local monsterType = MonsterType(monsterName) + if not monsterType then + player:sendTextMessage(MESSAGE_GAME_HIGHLIGHT, "Invalid monster type. Please contact an administrator.") + player:getPosition():sendMagicEffect(CONST_ME_POFF) + return false + end - local currentDifficulty = monsterType:getBestiaryStars() - local nextDifficultyLevel = getNextDifficultyLevel(currentDifficulty) - local nextDifficultyMonsters = nil + local currentDifficulty = monsterType:getBestiaryStars() + local nextDifficultyLevel = getNextDifficultyLevel(currentDifficulty) + local nextDifficultyMonsters = nil - if nextDifficultyLevel then - nextDifficultyMonsters = monsterType:getMonstersByBestiaryStars(SoulPit.SoulCoresConfiguration.monstersDifficulties[nextDifficultyLevel]) - else - nextDifficultyLevel = currentDifficulty - nextDifficultyMonsters = monsterType:getMonstersByBestiaryStars(SoulPit.SoulCoresConfiguration.monstersDifficulties[currentDifficulty]) - end + if nextDifficultyLevel then + nextDifficultyMonsters = monsterType:getMonstersByBestiaryStars(SoulPit.SoulCoresConfiguration.monstersDifficulties[nextDifficultyLevel]) + else + nextDifficultyLevel = currentDifficulty + nextDifficultyMonsters = monsterType:getMonstersByBestiaryStars(SoulPit.SoulCoresConfiguration.monstersDifficulties[currentDifficulty]) + end - if #nextDifficultyMonsters == 0 then - player:sendTextMessage(MESSAGE_GAME_HIGHLIGHT, "No monsters available for the next difficulty level. Please contact an administrator.") - player:getPosition():sendMagicEffect(CONST_ME_POFF) - return false - end + if #nextDifficultyMonsters == 0 then + player:sendTextMessage(MESSAGE_GAME_HIGHLIGHT, "No monsters available for the next difficulty level. Please contact an administrator.") + player:getPosition():sendMagicEffect(CONST_ME_POFF) + return false + end - local newMonsterType = nextDifficultyMonsters[math.random(#nextDifficultyMonsters)] - local newSoulCoreItem = getSoulCoreItemForMonster(newMonsterType:getName()) - if not newSoulCoreItem then -- Retry a second time. - newSoulCoreItem = getSoulCoreItemForMonster(newMonsterType:getName()) - if not newSoulCoreItem then - player:sendTextMessage(MESSAGE_GAME_HIGHLIGHT, "Failed to generate a Soul Core. Please contact an administrator.") - player:getPosition():sendMagicEffect(CONST_ME_POFF) - return false - end - end + local newMonsterType = nextDifficultyMonsters[math.random(#nextDifficultyMonsters)] + local newSoulCoreItem = getSoulCoreItemForMonster(newMonsterType:getName()) + if not newSoulCoreItem then -- Retry a second time. + newSoulCoreItem = getSoulCoreItemForMonster(newMonsterType:getName()) + if not newSoulCoreItem then + player:sendTextMessage(MESSAGE_GAME_HIGHLIGHT, "Failed to generate a Soul Core. Please contact an administrator.") + player:getPosition():sendMagicEffect(CONST_ME_POFF) + return false + end + end - if player:getFreeCapacity() < ItemType(newSoulCoreItem):getWeight() then - player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You do not have enough capacity.") - player:getPosition():sendMagicEffect(CONST_ME_POFF) - return false - end + if player:getFreeCapacity() < ItemType(newSoulCoreItem):getWeight() then + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You do not have enough capacity.") + player:getPosition():sendMagicEffect(CONST_ME_POFF) + return false + end - if math.random(100) <= SoulPit.SoulCoresConfiguration.chanceToGetOminousSoulCore then - player:addItem(49163, 1) - player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You have received an Ominous Soul Core.") - else - player:addItem(newSoulCoreItem, 1) - player:removeItem(target:getId(), 1) - player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You have received a " .. newMonsterType:getName() .. " soul core.") - end - player:removeItem(item:getId(), 1) - player:getPosition():sendMagicEffect(CONST_ME_MAGIC_BLUE) - return true + if math.random(100) <= SoulPit.SoulCoresConfiguration.chanceToGetOminousSoulCore then + player:addItem(49163, 1) + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You have received an Ominous Soul Core.") + else + player:addItem(newSoulCoreItem, 1) + player:removeItem(target:getId(), 1) + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You have received a " .. newMonsterType:getName() .. " soul core.") + end + player:removeItem(item:getId(), 1) + player:getPosition():sendMagicEffect(CONST_ME_MAGIC_BLUE) + return true end soulPrism:id(49164) -soulPrism:register() \ No newline at end of file +soulPrism:register() From d081da23a73c7686f31cc14815b2c028d3be7240 Mon Sep 17 00:00:00 2001 From: Pedro Henrique Alves Cruz Date: Wed, 4 Dec 2024 15:33:45 -0300 Subject: [PATCH 22/95] improve: soulprism code --- data/scripts/actions/items/soul_prism.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/data/scripts/actions/items/soul_prism.lua b/data/scripts/actions/items/soul_prism.lua index 7c092b8bdb0..4241f8277e5 100644 --- a/data/scripts/actions/items/soul_prism.lua +++ b/data/scripts/actions/items/soul_prism.lua @@ -28,7 +28,7 @@ local function getSoulCoreItemForMonster(monsterName) return newSoulCoreId end else - local newMonsterSoulCore = monsterName .. " soul core" + local newMonsterSoulCore = string.format("%s soul core", monsterName) local newSoulCoreId = getItemIdByName(newMonsterSoulCore) if newSoulCoreId then return newSoulCoreId @@ -67,7 +67,7 @@ function soulPrism.onUse(player, item, fromPosition, target, toPosition, isHotke end if #nextDifficultyMonsters == 0 then - player:sendTextMessage(MESSAGE_GAME_HIGHLIGHT, "No monsters available for the next difficulty level. Please contact an administrator.") + player:sendTextMessage(MESSAGE_GAME_HIGHLIGHT, "No monsters available for the next difficulty level.") player:getPosition():sendMagicEffect(CONST_ME_POFF) return false end @@ -77,7 +77,7 @@ function soulPrism.onUse(player, item, fromPosition, target, toPosition, isHotke if not newSoulCoreItem then -- Retry a second time. newSoulCoreItem = getSoulCoreItemForMonster(newMonsterType:getName()) if not newSoulCoreItem then - player:sendTextMessage(MESSAGE_GAME_HIGHLIGHT, "Failed to generate a Soul Core. Please contact an administrator.") + player:sendTextMessage(MESSAGE_GAME_HIGHLIGHT, "Failed to generate a Soul Core.") player:getPosition():sendMagicEffect(CONST_ME_POFF) return false end @@ -95,7 +95,7 @@ function soulPrism.onUse(player, item, fromPosition, target, toPosition, isHotke else player:addItem(newSoulCoreItem, 1) player:removeItem(target:getId(), 1) - player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You have received a " .. newMonsterType:getName() .. " soul core.") + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, string.format("You have received a %s soul core.", newMonsterType:getName())) end player:removeItem(item:getId(), 1) player:getPosition():sendMagicEffect(CONST_ME_MAGIC_BLUE) From 19ad89017194295f103f53ca355f546a8a600e5d Mon Sep 17 00:00:00 2001 From: Pedro Henrique Alves Cruz Date: Thu, 5 Dec 2024 19:04:38 -0300 Subject: [PATCH 23/95] fix: summons inheriting boss abilities and some improvements --- data-otservbr-global/lib/others/soulpit.lua | 11 +++++++++ .../scripts/actions/soulpit/soulpit_fight.lua | 23 ++++++++----------- .../monster/soulpit_creatureevents.lua | 4 ++-- .../spells/monster/soulpit_intensehex.lua | 2 +- .../spells/monster/soulpit_opressor.lua | 2 +- .../spells/monster/soulpit_powerless.lua | 2 +- 6 files changed, 25 insertions(+), 19 deletions(-) diff --git a/data-otservbr-global/lib/others/soulpit.lua b/data-otservbr-global/lib/others/soulpit.lua index 2628c1307be..8eeb80faa7e 100644 --- a/data-otservbr-global/lib/others/soulpit.lua +++ b/data-otservbr-global/lib/others/soulpit.lua @@ -116,6 +116,17 @@ SoulPit = { bossPosition = Position(32372, 31135, 8), exit = Position(32371, 31164, 8), zone = Zone("soulpit"), + + getMonsterVariationNameBySoulCore = function(searchName) + for mTypeName, soulCoreName in pairs(SoulPit.SoulCoresConfiguration.monsterVariationsSoulCore) do + if soulCoreName == searchName then + return mTypeName + end + end + + return nil + end, } SoulPit.zone:addArea(Position(32365, 31134, 8), Position(32382, 31152, 8)) +SoulPit.zone:setRemoveDestination(SoulPit.exit) diff --git a/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua b/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua index d4ba27b4735..1d3ddd21f66 100644 --- a/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua +++ b/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua @@ -1,3 +1,5 @@ +SoulPit.zone:blockFamiliars() + local zoneEvent = ZoneEvent(SoulPit.zone) function zoneEvent.afterLeave(zone, creature) local player = creature:getPlayer() @@ -27,6 +29,10 @@ zoneEvent:register() local soulPitAction = Action() function soulPitAction.onUse(player, item, fromPosition, target, toPosition, isHotkey) logger.warn(item:getName()) + if target and target:getId() == SoulPit.obeliskActive then + creature:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Someone is fighting in the soulpit!") + return false + end if not target or target:getId() ~= SoulPit.obeliskInactive then return false end @@ -49,12 +55,6 @@ function soulPitAction.onUse(player, item, fromPosition, target, toPosition, isH return true end - if not table.empty(SoulPit.zone:getPlayers()) then - local message = "Someone is fighting in the arena!" - creature:sendTextMessage(MESSAGE_EVENT_ADVANCE, message) - return false - end - local isAccountNormal = creature:getAccountType() < ACCOUNT_TYPE_GAMEMASTER if isAccountNormal and creature:getLevel() < SoulPit.requiredLevel then local message = string.format("All players need to be level %s or higher.", SoulPit.requiredLevel) @@ -72,8 +72,8 @@ function soulPitAction.onUse(player, item, fromPosition, target, toPosition, isH end local monsterName = string.gsub(item:getName(), " soul core", "") - - logger.warn("monster name: " .. monsterName) + local monsterVariationName = SoulPit.getMonsterVariationNameBySoulCore(item:getName()) + monsterName = monsterVariationName and monsterVariationName or monsterName SoulPit.zone:removeMonsters() @@ -95,9 +95,7 @@ function soulPitAction.onUse(player, item, fromPosition, target, toPosition, isH SoulPit.kickEvent = addEvent(function() SoulPit.encounter = nil - for _, player in pairs(SoulPit.zone:getPlayers()) do - player:teleportTo(SoulPit.exit) - end + SoulPit.zone:removePlayers() SoulPit.obeliskPosition:transformItem(SoulPit.obeliskActive, SoulPit.obeliskInactive) end, SoulPit.timeToKick) end @@ -106,8 +104,6 @@ function soulPitAction.onUse(player, item, fromPosition, target, toPosition, isH local function waveStart() for stack, amount in pairs(SoulPit.waves[encounter.currentStage].stacks) do - logger.warn("stack: " .. stack) - logger.warn("amount: " .. amount) for i = 1, amount do local position = stack ~= 40 and SoulPit.zone:randomPosition() or SoulPit.bossPosition for i = 1, SoulPit.timeToSpawnMonsters / 1000 do @@ -125,7 +121,6 @@ function soulPitAction.onUse(player, item, fromPosition, target, toPosition, isH return false end if stack == 40 then - logger.warn("ability name: {}", bossAbilityName) if bossAbility.monster then monster:registerEvent(bossAbilityName) end diff --git a/data-otservbr-global/scripts/creaturescripts/monster/soulpit_creatureevents.lua b/data-otservbr-global/scripts/creaturescripts/monster/soulpit_creatureevents.lua index 6a228144942..1810bab01fe 100644 --- a/data-otservbr-global/scripts/creaturescripts/monster/soulpit_creatureevents.lua +++ b/data-otservbr-global/scripts/creaturescripts/monster/soulpit_creatureevents.lua @@ -1,6 +1,6 @@ local enrage = CreatureEvent("enrageSoulPit") function enrage.onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType) - if not creature or not creature:isMonster() then + if not creature or not creature:isMonster() and creature:getMaster() then return true end @@ -26,7 +26,7 @@ enrage:register() local overpower = CreatureEvent("overpowerSoulPit") function overpower.onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType) - if creature and attacker and attacker:isMonster() and attacker:getForgeStack() == 40 then + if creature and attacker and attacker:isMonster() and attacker:getForgeStack() == 40 and not attacker:getMaster() then primaryDamage = primaryDamage * 1.1 secondaryDamage = secondaryDamage * 1.1 creature:getPosition():sendMagicEffect(CONST_ME_CRITICAL_DAMAGE) diff --git a/data-otservbr-global/scripts/spells/monster/soulpit_intensehex.lua b/data-otservbr-global/scripts/spells/monster/soulpit_intensehex.lua index 6b26cdd45d3..51ccca2d979 100644 --- a/data-otservbr-global/scripts/spells/monster/soulpit_intensehex.lua +++ b/data-otservbr-global/scripts/spells/monster/soulpit_intensehex.lua @@ -10,7 +10,7 @@ local spell = Spell("instant") function spell.onCastSpell(creature, var) local monster = creature:getMonster() - if monster and monster:soulPit() and table.contains(creature:getEvents(CREATURE_EVENT_THINK), "opressorSoulPit") then + if monster and monster:soulPit() and not monster:getMaster() and table.contains(creature:getEvents(CREATURE_EVENT_THINK), "opressorSoulPit") then return combat:execute(creature, var) end diff --git a/data-otservbr-global/scripts/spells/monster/soulpit_opressor.lua b/data-otservbr-global/scripts/spells/monster/soulpit_opressor.lua index 293c8859dea..05493618461 100644 --- a/data-otservbr-global/scripts/spells/monster/soulpit_opressor.lua +++ b/data-otservbr-global/scripts/spells/monster/soulpit_opressor.lua @@ -27,7 +27,7 @@ local combats = { combatRoot, combatFear } function spell.onCastSpell(creature, var) local monster = creature:getMonster() - if monster and monster:soulPit() and table.contains(creature:getEvents(CREATURE_EVENT_THINK), "opressorSoulPit") then + if monster and monster:soulPit() and not monster:getMaster() and table.contains(creature:getEvents(CREATURE_EVENT_THINK), "opressorSoulPit") then for _, combat in pairs(combats) do combat:execute(creature, var) end diff --git a/data-otservbr-global/scripts/spells/monster/soulpit_powerless.lua b/data-otservbr-global/scripts/spells/monster/soulpit_powerless.lua index 7ed71742f8f..f2fe9d3a4a9 100644 --- a/data-otservbr-global/scripts/spells/monster/soulpit_powerless.lua +++ b/data-otservbr-global/scripts/spells/monster/soulpit_powerless.lua @@ -10,7 +10,7 @@ local spell = Spell("instant") function spell.onCastSpell(creature, var) local monster = creature:getMonster() - if monster and monster:soulPit() and table.contains(creature:getEvents(CREATURE_EVENT_THINK), "opressorSoulPit") then + if monster and monster:soulPit() and not monster:getMaster() and table.contains(creature:getEvents(CREATURE_EVENT_THINK), "opressorSoulPit") then return combat:execute(creature, var) end From d26e2e2fd767b15f7f0e0cc9f1f826cc95f92dfd Mon Sep 17 00:00:00 2001 From: Renato Machado Date: Wed, 4 Dec 2024 16:06:40 -0300 Subject: [PATCH 24/95] Update game.cpp --- src/game/game.cpp | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/game/game.cpp b/src/game/game.cpp index e69654a47ad..648229a9d8e 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -72,7 +72,7 @@ #include -std::vector> checkCreatureLists[EVENT_CREATURECOUNT]; +std::vector> checkCreatureLists[EVENT_CREATURECOUNT]; namespace InternalGame { void sendBlockEffect(BlockType_t blockType, CombatType_t combatType, const Position &targetPos, const std::shared_ptr &source) { @@ -6461,15 +6461,13 @@ void Game::addCreatureCheck(const std::shared_ptr &creature) { creature->creatureCheck.store(true); - if (creature->inCheckCreaturesVector.load()) { + if (creature->inCheckCreaturesVector.exchange(true)) { // already in a vector return; } - creature->inCheckCreaturesVector.store(true); - - creature->safeCall([this, creature] { - checkCreatureLists[uniform_random(0, EVENT_CREATURECOUNT - 1)].emplace_back(creature); + creature->safeCall([this, index = uniform_random(0, EVENT_CREATURECOUNT - 1), creature] { + checkCreatureLists[index].emplace_back(creature); }); } @@ -6484,15 +6482,18 @@ void Game::checkCreatures() { metrics::method_latency measure(__METRICS_METHOD_NAME__); static size_t index = 0; - std::erase_if(checkCreatureLists[index], [this](const std::shared_ptr creature) { - if (creature->creatureCheck && creature->isAlive()) { - creature->onThink(EVENT_CREATURE_THINK_INTERVAL); - creature->onAttacking(EVENT_CREATURE_THINK_INTERVAL); - creature->executeConditions(EVENT_CREATURE_THINK_INTERVAL); - return false; + std::erase_if(checkCreatureLists[index], [this](const std::weak_ptr& weak) { + if (const auto creature = weak.lock()) { + if (creature->creatureCheck && creature->isAlive()) { + creature->onThink(EVENT_CREATURE_THINK_INTERVAL); + creature->onAttacking(EVENT_CREATURE_THINK_INTERVAL); + creature->executeConditions(EVENT_CREATURE_THINK_INTERVAL); + return false; + } + + creature->inCheckCreaturesVector = false; } - creature->inCheckCreaturesVector = false; return true; }); From 11b1101790a1bc511fea870d4f0a609c73aeeb13 Mon Sep 17 00:00:00 2001 From: Renato Machado Date: Thu, 5 Dec 2024 15:43:43 -0300 Subject: [PATCH 25/95] update --- src/game/game.cpp | 4 ++-- src/game/scheduling/task.hpp | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/game/game.cpp b/src/game/game.cpp index 648229a9d8e..1809662c279 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -6466,9 +6466,9 @@ void Game::addCreatureCheck(const std::shared_ptr &creature) { return; } - creature->safeCall([this, index = uniform_random(0, EVENT_CREATURECOUNT - 1), creature] { + g_dispatcher().addEvent([this, index = uniform_random(0, EVENT_CREATURECOUNT - 1), creature] { checkCreatureLists[index].emplace_back(creature); - }); + }, "Game::addCreatureCheck"); } void Game::removeCreatureCheck(const std::shared_ptr &creature) { diff --git a/src/game/scheduling/task.hpp b/src/game/scheduling/task.hpp index c01dbe2f676..6cd9eef342d 100644 --- a/src/game/scheduling/task.hpp +++ b/src/game/scheduling/task.hpp @@ -75,6 +75,7 @@ class Task { "Game::createInfluencedMonsters", "Game::updateCreatureWalk", "Game::updateForgeableMonsters", + "Game::addCreatureCheck", "GlobalEvents::think", "LuaEnvironment::executeTimerEvent", "Modules::executeOnRecvbyte", From 1783742f776101abbecd5b4de5259fbb67648bf8 Mon Sep 17 00:00:00 2001 From: Pedro Henrique Alves Cruz Date: Thu, 5 Dec 2024 19:58:12 -0300 Subject: [PATCH 26/95] improve: customizable bosses abilities --- data-otservbr-global/lib/others/soulpit.lua | 7 +++++++ .../monster/soulpit_creatureevents.lua | 21 +++++++------------ 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/data-otservbr-global/lib/others/soulpit.lua b/data-otservbr-global/lib/others/soulpit.lua index 8eeb80faa7e..0b1d35ebdb7 100644 --- a/data-otservbr-global/lib/others/soulpit.lua +++ b/data-otservbr-global/lib/others/soulpit.lua @@ -94,10 +94,17 @@ SoulPit = { }, bossAbilities = { overpowerSoulPit = { + bonusCriticalDamage = 1.1, player = true, monster = false, }, enrageSoulPit = { + bounds = { + [{0.8, 0.6}] = 0.9, -- 10% damage reduction + [{0.6, 0.4}] = 0.75, -- 25% damage reduction + [{0.4, 0.2}] = 0.6, -- 40% damage reduction + [{0.0, 0.2}] = 0.4, -- 60% damage reduction + }, player = false, monster = true, }, diff --git a/data-otservbr-global/scripts/creaturescripts/monster/soulpit_creatureevents.lua b/data-otservbr-global/scripts/creaturescripts/monster/soulpit_creatureevents.lua index 1810bab01fe..c399ec7fb0a 100644 --- a/data-otservbr-global/scripts/creaturescripts/monster/soulpit_creatureevents.lua +++ b/data-otservbr-global/scripts/creaturescripts/monster/soulpit_creatureevents.lua @@ -6,18 +6,10 @@ function enrage.onHealthChange(creature, attacker, primaryDamage, primaryType, s local healthPercentage = creature:getHealth() / creature:getMaxHealth() - if healthPercentage >= 0.6 and healthPercentage <= 0.8 then - primaryDamage = primaryDamage * 0.9 -- 10% damage reduction - secondaryDamage = secondaryDamage * 0.9 -- 10% damage reduction - elseif healthPercentage >= 0.4 and healthPercentage < 0.6 then - primaryDamage = primaryDamage * 0.75 -- 25% damage reduction - secondaryDamage = secondaryDamage * 0.75 -- 25% damage reduction - elseif healthPercentage >= 0.2 and healthPercentage < 0.4 then - primaryDamage = primaryDamage * 0.6 -- 40% damage reduction - secondaryDamage = secondaryDamage * 0.6 -- 40% damage reduction - elseif healthPercentage > 0 and healthPercentage < 0.2 then - primaryDamage = primaryDamage * 0.4 -- 60% damage reduction - secondaryDamage = secondaryDamage * 0.4 -- 60% damage reduction + for bounds, reduction in pairs(SoulPit.bossAbilities.enrageSoulPit.bounds) do + if healthPercentage > bounds[2] and healthPercentage <= bounds[1] then + return primaryDamage * reduction, primaryType, secondaryDamage * reduction, secondaryType + end end return primaryDamage, primaryType, secondaryDamage, secondaryType @@ -27,8 +19,9 @@ enrage:register() local overpower = CreatureEvent("overpowerSoulPit") function overpower.onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType) if creature and attacker and attacker:isMonster() and attacker:getForgeStack() == 40 and not attacker:getMaster() then - primaryDamage = primaryDamage * 1.1 - secondaryDamage = secondaryDamage * 1.1 + local bonusCriticalDamage = SoulPit.bossAbilities.overpowerSoulPit.bonusCriticalDamage + primaryDamage = primaryDamage * bonusCriticalDamage + secondaryDamage = secondaryDamage * bonusCriticalDamage creature:getPosition():sendMagicEffect(CONST_ME_CRITICAL_DAMAGE) end From 3b2978399ec60a5537e48d63854b4223bdbae647 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Thu, 5 Dec 2024 22:59:37 +0000 Subject: [PATCH 27/95] Lua code format - (Stylua) --- data-otservbr-global/lib/others/soulpit.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/data-otservbr-global/lib/others/soulpit.lua b/data-otservbr-global/lib/others/soulpit.lua index 0b1d35ebdb7..4eb4dac4d53 100644 --- a/data-otservbr-global/lib/others/soulpit.lua +++ b/data-otservbr-global/lib/others/soulpit.lua @@ -100,10 +100,10 @@ SoulPit = { }, enrageSoulPit = { bounds = { - [{0.8, 0.6}] = 0.9, -- 10% damage reduction - [{0.6, 0.4}] = 0.75, -- 25% damage reduction - [{0.4, 0.2}] = 0.6, -- 40% damage reduction - [{0.0, 0.2}] = 0.4, -- 60% damage reduction + [{ 0.8, 0.6 }] = 0.9, -- 10% damage reduction + [{ 0.6, 0.4 }] = 0.75, -- 25% damage reduction + [{ 0.4, 0.2 }] = 0.6, -- 40% damage reduction + [{ 0.0, 0.2 }] = 0.4, -- 60% damage reduction }, player = false, monster = true, From b1aa5b3aee96166b7599b325f2b1f8585bec5153 Mon Sep 17 00:00:00 2001 From: Felipe Paluco Date: Fri, 6 Dec 2024 13:37:23 -0300 Subject: [PATCH 28/95] feat: add downgrade exalted core mechanic --- data/scripts/actions/items/exalted_core.lua | 92 +++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 data/scripts/actions/items/exalted_core.lua diff --git a/data/scripts/actions/items/exalted_core.lua b/data/scripts/actions/items/exalted_core.lua new file mode 100644 index 00000000000..a45633a3af7 --- /dev/null +++ b/data/scripts/actions/items/exalted_core.lua @@ -0,0 +1,92 @@ +local exaltedCore = Action() + +local function getPreviousDifficultyLevel(currentLevel) + for level, value in pairs(SoulPit.SoulCoresConfiguration.monstersDifficulties) do + if value == currentLevel - 1 then + return level + end + end + return nil +end + +local function getSoulCoreItemForMonster(monsterName) + local lowerMonsterName = monsterName:lower() + local soulCoreName = SoulPit.SoulCoresConfiguration.monsterVariationsSoulCore[monsterName] + + if soulCoreName then + local newSoulCoreId = getItemIdByName(soulCoreName) + if newSoulCoreId then + return newSoulCoreId + end + else + local newMonsterSoulCore = string.format("%s soul core", monsterName) + local newSoulCoreId = getItemIdByName(newMonsterSoulCore) + if newSoulCoreId then + return newSoulCoreId + end + end + + return false +end + +function exaltedCore.onUse(player, item, fromPosition, target, toPosition, isHotkey) + local itemName = target:getName() + local monsterName = itemName:match("^(.-) soul core") + + if not monsterName then + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You can only use Exalted Core with a Soul Core.") + player:getPosition():sendMagicEffect(CONST_ME_POFF) + return false + end + + local monsterType = MonsterType(monsterName) + if not monsterType then + player:sendTextMessage(MESSAGE_GAME_HIGHLIGHT, "Invalid monster type. Please contact an administrator.") + player:getPosition():sendMagicEffect(CONST_ME_POFF) + return false + end + + local currentDifficulty = monsterType:getBestiaryStars() + local previousDifficultyLevel = getPreviousDifficultyLevel(currentDifficulty) + local previousDifficultyMonsters = nil + + if previousDifficultyLevel then + previousDifficultyMonsters = monsterType:getMonstersByBestiaryStars(SoulPit.SoulCoresConfiguration.monstersDifficulties[previousDifficultyLevel]) + else + previousDifficultyLevel = currentDifficulty + previousDifficultyMonsters = monsterType:getMonstersByBestiaryStars(SoulPit.SoulCoresConfiguration.monstersDifficulties[currentDifficulty]) + end + + if #previousDifficultyMonsters == 0 then + player:sendTextMessage(MESSAGE_GAME_HIGHLIGHT, "No monsters available for the previous difficulty level.") + player:getPosition():sendMagicEffect(CONST_ME_POFF) + return false + end + + local newMonsterType = previousDifficultyMonsters[math.random(#previousDifficultyMonsters)] + local newSoulCoreItem = getSoulCoreItemForMonster(newMonsterType:getName()) + if not newSoulCoreItem then -- Retry a second time. + newSoulCoreItem = getSoulCoreItemForMonster(newMonsterType:getName()) + if not newSoulCoreItem then + player:sendTextMessage(MESSAGE_GAME_HIGHLIGHT, "Failed to generate a Soul Core.") + player:getPosition():sendMagicEffect(CONST_ME_POFF) + return false + end + end + + if player:getFreeCapacity() < ItemType(newSoulCoreItem):getWeight() then + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You do not have enough capacity.") + player:getPosition():sendMagicEffect(CONST_ME_POFF) + return false + end + + player:addItem(newSoulCoreItem, 1) + player:removeItem(target:getId(), 1) + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, string.format("You have received a %s soul core.", newMonsterType:getName())) + player:removeItem(item:getId(), 1) + player:getPosition():sendMagicEffect(CONST_ME_MAGIC_BLUE) + return true +end + +exaltedCore:id(37110) +exaltedCore:register() From b5856916ec077573ba36c1c82aede33acf04947e Mon Sep 17 00:00:00 2001 From: Pedro Henrique Alves Cruz Date: Fri, 6 Dec 2024 13:51:35 -0300 Subject: [PATCH 29/95] fix: soulprimsm being returned --- src/lua/functions/core/game/game_functions.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/lua/functions/core/game/game_functions.cpp b/src/lua/functions/core/game/game_functions.cpp index 8182811bd9e..b7fa2d49a2d 100644 --- a/src/lua/functions/core/game/game_functions.cpp +++ b/src/lua/functions/core/game/game_functions.cpp @@ -1047,6 +1047,9 @@ int GameFunctions::luaGameGetSoulCoreItems(lua_State* L) { std::vector soulCoreItems; for (const auto &itemType : Item::items.getItems()) { + if (itemType.m_primaryType.empty() && itemType.type == ITEM_TYPE_NONE) { + continue; + } if (itemType.m_primaryType == "SoulCores" || itemType.type == ITEM_TYPE_SOULCORES) { soulCoreItems.emplace_back(&itemType); } From 24e8cd621849eec099270d1f9da67887afe46576 Mon Sep 17 00:00:00 2001 From: Felipe Paluco Date: Fri, 6 Dec 2024 14:33:58 -0300 Subject: [PATCH 30/95] feat: add fusion possibility through soul cores --- data-otservbr-global/lib/others/soulpit.lua | 19 +++++++++++++++++++ .../scripts/actions/soulpit/soulpit_fight.lua | 12 ++++++++---- data/items/items.xml | 6 ++++++ data/scripts/actions/items/exalted_core.lua | 2 +- data/scripts/actions/items/soul_prism.lua | 2 +- 5 files changed, 35 insertions(+), 6 deletions(-) diff --git a/data-otservbr-global/lib/others/soulpit.lua b/data-otservbr-global/lib/others/soulpit.lua index 4eb4dac4d53..bbaf0bf48c9 100644 --- a/data-otservbr-global/lib/others/soulpit.lua +++ b/data-otservbr-global/lib/others/soulpit.lua @@ -133,6 +133,25 @@ SoulPit = { return nil end, + getSoulCoreMonster = function(name) + return name:match("^(.-) soul core") + end, + onFuseSoulCores = function(player, item, target) + local itemName = item:getName() + local targetItemName = target:getName() + + if SoulPit.getSoulCoreMonster(itemName) and SoulPit.getSoulCoreMonster(targetItemName) then + local randomSoulCore = SoulPit.soulCores[math.random(#SoulPit.soulCores)] + player:addItem(randomSoulCore:getId(), 1) + player:getPosition():sendMagicEffect(CONST_ME_MAGIC_BLUE) + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, string.format("You have received a %s soul core.", randomSoulCore:getName())) + item:remove(1) + target:remove(1) + return true + end + + return false + end } SoulPit.zone:addArea(Position(32365, 31134, 8), Position(32382, 31152, 8)) diff --git a/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua b/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua index 1d3ddd21f66..fff8a92267d 100644 --- a/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua +++ b/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua @@ -27,7 +27,11 @@ end zoneEvent:register() local soulPitAction = Action() -function soulPitAction.onUse(player, item, fromPosition, target, toPosition, isHotkey) +function soulPitAction.onUse(player, item, fromPosition, target, toPosition, isHotkey) + if SoulPit.onFuseSoulCores(player, item, target) then + return true + end + logger.warn(item:getName()) if target and target:getId() == SoulPit.obeliskActive then creature:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Someone is fighting in the soulpit!") @@ -148,8 +152,8 @@ function soulPitAction.onUse(player, item, fromPosition, target, toPosition, isH end for _, itemType in pairs(SoulPit.soulCores) do - if itemType:getId() ~= 49164 then -- TO-DO: currently Game.getSoulCoreItems() it's returning soul prism item in the results, we don't want this. - soulPitAction:id(itemType:getId()) - end + -- if itemType:getId() ~= 49164 then -- TO-DO: currently Game.getSoulCoreItems() it's returning soul prism item in the results, we don't want this. + soulPitAction:id(itemType:getId()) + -- end end soulPitAction:register() diff --git a/data/items/items.xml b/data/items/items.xml index 46f1c95a9d3..d2e43e35605 100644 --- a/data/items/items.xml +++ b/data/items/items.xml @@ -79734,5 +79734,11 @@ Granted by TibiaGoals.com"/> + + + + + + diff --git a/data/scripts/actions/items/exalted_core.lua b/data/scripts/actions/items/exalted_core.lua index a45633a3af7..7a4c778589a 100644 --- a/data/scripts/actions/items/exalted_core.lua +++ b/data/scripts/actions/items/exalted_core.lua @@ -31,7 +31,7 @@ end function exaltedCore.onUse(player, item, fromPosition, target, toPosition, isHotkey) local itemName = target:getName() - local monsterName = itemName:match("^(.-) soul core") + local monsterName = SoulPit.getSoulCoreMonster(itemName) if not monsterName then player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You can only use Exalted Core with a Soul Core.") diff --git a/data/scripts/actions/items/soul_prism.lua b/data/scripts/actions/items/soul_prism.lua index 4241f8277e5..190ad086098 100644 --- a/data/scripts/actions/items/soul_prism.lua +++ b/data/scripts/actions/items/soul_prism.lua @@ -40,7 +40,7 @@ end function soulPrism.onUse(player, item, fromPosition, target, toPosition, isHotkey) local itemName = target:getName() - local monsterName = itemName:match("^(.-) soul core") + local monsterName = SoulPit.getSoulCoreMonster(itemName) if not monsterName then player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You can only use Soul Prism with a Soul Core.") From 698c4e001945f1e9356231a09a56e05f95d7aef6 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Fri, 6 Dec 2024 17:34:49 +0000 Subject: [PATCH 31/95] Lua code format - (Stylua) --- data-otservbr-global/lib/others/soulpit.lua | 2 +- data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/data-otservbr-global/lib/others/soulpit.lua b/data-otservbr-global/lib/others/soulpit.lua index bbaf0bf48c9..752a207f401 100644 --- a/data-otservbr-global/lib/others/soulpit.lua +++ b/data-otservbr-global/lib/others/soulpit.lua @@ -151,7 +151,7 @@ SoulPit = { end return false - end + end, } SoulPit.zone:addArea(Position(32365, 31134, 8), Position(32382, 31152, 8)) diff --git a/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua b/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua index fff8a92267d..c326eb3b772 100644 --- a/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua +++ b/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua @@ -27,7 +27,7 @@ end zoneEvent:register() local soulPitAction = Action() -function soulPitAction.onUse(player, item, fromPosition, target, toPosition, isHotkey) +function soulPitAction.onUse(player, item, fromPosition, target, toPosition, isHotkey) if SoulPit.onFuseSoulCores(player, item, target) then return true end From 02fff0a8e83a207d63aec62fe47bea6e77b16f3d Mon Sep 17 00:00:00 2001 From: Pedro Henrique Alves Cruz Date: Fri, 6 Dec 2024 16:54:33 -0300 Subject: [PATCH 32/95] feat: animus mastery --- config.lua.dist | 11 +++++ .../scripts/actions/soulpit/soulpit_fight.lua | 2 +- src/config/config_enums.hpp | 4 ++ src/config/configmanager.cpp | 4 ++ src/creatures/CMakeLists.txt | 1 + .../players/animus_mastery/animus_mastery.cpp | 47 +++++++++++++++++++ .../players/animus_mastery/animus_mastery.hpp | 36 ++++++++++++++ src/creatures/players/player.cpp | 21 +++++++-- src/creatures/players/player.hpp | 8 +++- .../functions/core/game/game_functions.cpp | 3 -- .../creatures/player/player_functions.cpp | 44 +++++++++++++++++ .../creatures/player/player_functions.hpp | 4 ++ 12 files changed, 175 insertions(+), 10 deletions(-) create mode 100644 src/creatures/players/animus_mastery/animus_mastery.cpp create mode 100644 src/creatures/players/animus_mastery/animus_mastery.hpp diff --git a/config.lua.dist b/config.lua.dist index 7d0360d9360..85770cf8e91 100644 --- a/config.lua.dist +++ b/config.lua.dist @@ -86,6 +86,17 @@ maxItem = 5000 maxContainer = 500 maxContainerDepth = 200 +-- Animus Mastery - SoulPit (Get more info in: TBD) +-- NOTE: animusMasteryMaxMonsterXpMultiplier is the maximum experience the multiplier can be. +-- NOTE: animusMasteryMonsterXpMultiplier is the monster experience multiplier that has the animus mastery unlocked. +-- NOTE: animusMasteryMonstersXpMultiplier is the multiplier for each 'animusMasteryMonstersToIncreaseXpMultiplier' monsters that +-- the player has the animus mastery unlocked. +-- NOTE: animusMasteryMonstersToIncreaseXpMultiplier is the amount of monster to increase the experience multiplier by 'animusMasteryMonstersXpMultiplier'. +animusMasteryMaxMonsterXpMultiplier = 4.0 +animusMasteryMonsterXpMultiplier = 2.0 +animusMasteryMonstersXpMultiplier = 0.1 +animusMasteryMonstersToIncreaseXpMultiplier = 10 + -- Augments System (Get more info in: https://github.com/opentibiabr/canary/pull/2602) -- NOTE: the following values are for all weapons and equipments that have type of "increase damage", "powerful impact" and "strong impact". -- To customize the percentage of a particular item with these augment types, please add to the item "augments" section on items.xml as the example above. diff --git a/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua b/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua index c326eb3b772..a4036c702d0 100644 --- a/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua +++ b/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua @@ -93,8 +93,8 @@ function soulPitAction.onUse(player, item, fromPosition, target, toPosition, isH SoulPit.zone:removeMonsters() for _, player in pairs(SoulPit.zone:getPlayers()) do + player:addAnimusMastery(monsterName) player:sendTextMessage(MESSAGE_EVENT_ADVANCE, string.format("You have defeated the core of the %s soul and unlocked its animus mastery!", monsterName)) - -- Add the monster animus mastery for the player. end SoulPit.kickEvent = addEvent(function() diff --git a/src/config/config_enums.hpp b/src/config/config_enums.hpp index 559045fdb9b..c25aa1a3268 100644 --- a/src/config/config_enums.hpp +++ b/src/config/config_enums.hpp @@ -16,6 +16,10 @@ enum ConfigKey_t : uint16_t { AIMBOT_HOTKEY_ENABLED, ALLOW_CHANGEOUTFIT, ALLOW_RELOAD, + ANIMUS_MASTERY_MAX_MONSTER_XP_MULTIPLIER, + ANIMUS_MASTERY_MONSTER_XP_MULTIPLIER, + ANIMUS_MASTERY_MONSTERS_XP_MULTIPLIER, + ANIMUS_MASTERY_MONSTERS_TO_INCREASE_XP_MULTIPLIER, AUGMENT_INCREASED_DAMAGE_PERCENT, AUGMENT_POWERFUL_IMPACT_PERCENT, AUGMENT_STRONG_IMPACT_PERCENT, diff --git a/src/config/configmanager.cpp b/src/config/configmanager.cpp index 1c00df76c2f..6334c58f52b 100644 --- a/src/config/configmanager.cpp +++ b/src/config/configmanager.cpp @@ -197,6 +197,9 @@ bool ConfigManager::load() { loadFloatConfig(L, TRANSCENDANCE_CHANCE_FORMULA_A, "transcendanceChanceFormulaA", 0.0127); loadFloatConfig(L, TRANSCENDANCE_CHANCE_FORMULA_B, "transcendanceChanceFormulaB", 0.1070); loadFloatConfig(L, TRANSCENDANCE_CHANCE_FORMULA_C, "transcendanceChanceFormulaC", 0.0073); + loadFloatConfig(L, ANIMUS_MASTERY_MAX_MONSTER_XP_MULTIPLIER, "animusMasteryMaxMonsterXpMultiplier", 4.0); + loadFloatConfig(L, ANIMUS_MASTERY_MONSTER_XP_MULTIPLIER, "animusMasteryMonsterXpMultiplier", 2.0); + loadFloatConfig(L, ANIMUS_MASTERY_MONSTERS_XP_MULTIPLIER, "animusMasteryMonstersXpMultiplier", 0.1); loadIntConfig(L, ACTIONS_DELAY_INTERVAL, "timeBetweenActions", 200); loadIntConfig(L, ADVENTURERSBLESSING_LEVEL, "adventurersBlessingLevel", 21); @@ -341,6 +344,7 @@ bool ConfigManager::load() { loadIntConfig(L, AUGMENT_INCREASED_DAMAGE_PERCENT, "augmentIncreasedDamagePercent", 5); loadIntConfig(L, AUGMENT_POWERFUL_IMPACT_PERCENT, "augmentPowerfulImpactPercent", 10); loadIntConfig(L, AUGMENT_STRONG_IMPACT_PERCENT, "augmentStrongImpactPercent", 7); + loadIntConfig(L, ANIMUS_MASTERY_MONSTERS_TO_INCREASE_XP_MULTIPLIER, "animusMasteryMonstersToIncreaseXpMultiplier", 10); loadStringConfig(L, CORE_DIRECTORY, "coreDirectory", "data"); loadStringConfig(L, DATA_DIRECTORY, "dataPackDirectory", "data-otservbr-global"); diff --git a/src/creatures/CMakeLists.txt b/src/creatures/CMakeLists.txt index c87a45a0aa0..b48b9c42844 100644 --- a/src/creatures/CMakeLists.txt +++ b/src/creatures/CMakeLists.txt @@ -12,6 +12,7 @@ target_sources(${PROJECT_NAME}_lib PRIVATE npcs/npc.cpp npcs/npcs.cpp npcs/spawns/spawn_npc.cpp + players/animus_mastery/animus_mastery.cpp players/grouping/familiars.cpp players/grouping/groups.cpp players/grouping/guild.cpp diff --git a/src/creatures/players/animus_mastery/animus_mastery.cpp b/src/creatures/players/animus_mastery/animus_mastery.cpp new file mode 100644 index 00000000000..62c0f03d99e --- /dev/null +++ b/src/creatures/players/animus_mastery/animus_mastery.cpp @@ -0,0 +1,47 @@ +/** + * Canary - A free and open-source MMORPG server emulator + * Copyright (©) 2019-2024 OpenTibiaBR + * Repository: https://github.com/opentibiabr/canary + * License: https://github.com/opentibiabr/canary/blob/main/LICENSE + * Contributors: https://github.com/opentibiabr/canary/graphs/contributors + * Website: https://docs.opentibiabr.com/ + */ + +#include "creatures/players/animus_mastery/animus_mastery.hpp" +#include "creatures/players/player.hpp" +#include "config/configmanager.hpp" + +AnimusMastery::AnimusMastery(Player &player) : + m_player(player), + maxMonsterXpMultiplier(g_configManager().getFloat(ANIMUS_MASTERY_MAX_MONSTER_XP_MULTIPLIER)), + monsterXpMultiplier(g_configManager().getFloat(ANIMUS_MASTERY_MONSTER_XP_MULTIPLIER)), + monstersXpMultiplier(g_configManager().getFloat(ANIMUS_MASTERY_MONSTERS_XP_MULTIPLIER)), + monstersAmountToMultiply(g_configManager().getFloat(ANIMUS_MASTERY_MONSTERS_TO_INCREASE_XP_MULTIPLIER)) { } + +void AnimusMastery::add(std::string_view addMonsterType) { + if (!has(addMonsterType)) { + animusMasteries.emplace_back(addMonsterType); + } +} + +void AnimusMastery::remove(std::string_view removeMonsterType) { + std::erase_if(animusMasteries, [removeMonsterType](const std::string &monsterType) { + return monsterType == removeMonsterType; + }); +} + +bool AnimusMastery::has(std::string_view searchMonsterType) const { + auto it = std::ranges::find(animusMasteries, searchMonsterType); + + return it != animusMasteries.end(); +} + +float AnimusMastery::getExperienceMultiplier() const { + uint16_t monsterAmountMultiplier = animusMasteries.size() / monstersAmountToMultiply; + + return std::min(maxMonsterXpMultiplier, 1 + (monsterAmountMultiplier * monstersXpMultiplier)); +} + +const std::vector &AnimusMastery::getAnimusMasteries() const { + return animusMasteries; +} diff --git a/src/creatures/players/animus_mastery/animus_mastery.hpp b/src/creatures/players/animus_mastery/animus_mastery.hpp new file mode 100644 index 00000000000..c6eab7ac1da --- /dev/null +++ b/src/creatures/players/animus_mastery/animus_mastery.hpp @@ -0,0 +1,36 @@ +/** + * Canary - A free and open-source MMORPG server emulator + * Copyright (©) 2019-2024 OpenTibiaBR + * Repository: https://github.com/opentibiabr/canary + * License: https://github.com/opentibiabr/canary/blob/main/LICENSE + * Contributors: https://github.com/opentibiabr/canary/graphs/contributors + * Website: https://docs.opentibiabr.com/ + */ + +#pragma once + +class Player; + +class AnimusMastery { +public: + explicit AnimusMastery(Player &player); + + void add(std::string_view addMonsterType); + void remove(std::string_view removeMonsterType); + + bool has(std::string_view searchMonsterType) const; + + float getExperienceMultiplier() const; + + const std::vector &getAnimusMasteries() const; + +private: + Player &m_player; + + float maxMonsterXpMultiplier = 4.0; + float monsterXpMultiplier = 2.0; + float monstersXpMultiplier = 0.1; + uint16_t monstersAmountToMultiply = 10; + + std::vector animusMasteries; +}; diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index a41b61ed3d3..02e99ce62a9 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -19,6 +19,7 @@ #include "creatures/monsters/monster.hpp" #include "creatures/monsters/monsters.hpp" #include "creatures/npcs/npc.hpp" +#include "creatures/players/animus_mastery/animus_mastery.hpp" #include "creatures/players/wheel/player_wheel.hpp" #include "creatures/players/wheel/wheel_gems.hpp" #include "creatures/players/achievement/player_achievement.hpp" @@ -77,6 +78,7 @@ Player::Player(std::shared_ptr p) : m_playerBadge = std::make_unique(*this); m_playerCyclopedia = std::make_unique(*this); m_playerTitle = std::make_unique(*this); + m_animusMastery = std::make_unique(*this); } Player::~Player() { @@ -10248,6 +10250,15 @@ const std::unique_ptr &Player::title() const { return m_playerTitle; } +// Cyclopedia interface +std::unique_ptr &Player::cyclopedia() { + return m_playerCyclopedia; +} + +const std::unique_ptr &Player::cyclopedia() const { + return m_playerCyclopedia; +} + // VIP interface std::unique_ptr &Player::vip() { return m_playerVIP; @@ -10257,13 +10268,13 @@ const std::unique_ptr &Player::vip() const { return m_playerVIP; } -// Cyclopedia -std::unique_ptr &Player::cyclopedia() { - return m_playerCyclopedia; +// Animus Mastery interface +std::unique_ptr &Player::animusMastery() { + return m_animusMastery; } -const std::unique_ptr &Player::cyclopedia() const { - return m_playerCyclopedia; +const std::unique_ptr &Player::animusMastery() const { + return m_animusMastery; } void Player::sendLootMessage(const std::string &message) const { diff --git a/src/creatures/players/player.hpp b/src/creatures/players/player.hpp index 6ab3be4ca7a..c6368821d16 100644 --- a/src/creatures/players/player.hpp +++ b/src/creatures/players/player.hpp @@ -17,6 +17,7 @@ #include "game/movement/position.hpp" #include "creatures/creatures_definitions.hpp" +class AnimusMastery; class House; class NetworkMessage; class Weapon; @@ -1244,7 +1245,7 @@ class Player final : public Creature, public Cylinder, public Bankable { std::unique_ptr &title(); const std::unique_ptr &title() const; - // Player summary interface + // Player cyclopedia interface std::unique_ptr &cyclopedia(); const std::unique_ptr &cyclopedia() const; @@ -1252,6 +1253,10 @@ class Player final : public Creature, public Cylinder, public Bankable { std::unique_ptr &vip(); const std::unique_ptr &vip() const; + // Player animusMastery interface + std::unique_ptr &animusMastery(); + const std::unique_ptr &animusMastery() const; + void sendLootMessage(const std::string &message) const; std::shared_ptr getLootPouch(); @@ -1623,6 +1628,7 @@ class Player final : public Creature, public Cylinder, public Bankable { std::unique_ptr m_playerCyclopedia; std::unique_ptr m_playerTitle; std::unique_ptr m_playerVIP; + std::unique_ptr m_animusMastery; std::mutex quickLootMutex; diff --git a/src/lua/functions/core/game/game_functions.cpp b/src/lua/functions/core/game/game_functions.cpp index b7fa2d49a2d..8182811bd9e 100644 --- a/src/lua/functions/core/game/game_functions.cpp +++ b/src/lua/functions/core/game/game_functions.cpp @@ -1047,9 +1047,6 @@ int GameFunctions::luaGameGetSoulCoreItems(lua_State* L) { std::vector soulCoreItems; for (const auto &itemType : Item::items.getItems()) { - if (itemType.m_primaryType.empty() && itemType.type == ITEM_TYPE_NONE) { - continue; - } if (itemType.m_primaryType == "SoulCores" || itemType.type == ITEM_TYPE_SOULCORES) { soulCoreItems.emplace_back(&itemType); } diff --git a/src/lua/functions/creatures/player/player_functions.cpp b/src/lua/functions/creatures/player/player_functions.cpp index 08045f1de00..a158ae21139 100644 --- a/src/lua/functions/creatures/player/player_functions.cpp +++ b/src/lua/functions/creatures/player/player_functions.cpp @@ -15,6 +15,7 @@ #include "creatures/creature.hpp" #include "creatures/interactions/chat.hpp" #include "creatures/monsters/monsters.hpp" +#include "creatures/players/animus_mastery/animus_mastery.hpp" #include "creatures/players/achievement/player_achievement.hpp" #include "creatures/players/cyclopedia/player_cyclopedia.hpp" #include "creatures/players/cyclopedia/player_title.hpp" @@ -403,6 +404,10 @@ void PlayerFunctions::init(lua_State* L) { Lua::registerMethod(L, "Player", "removeIconBakragore", PlayerFunctions::luaPlayerRemoveIconBakragore); Lua::registerMethod(L, "Player", "sendCreatureAppear", PlayerFunctions::luaPlayerSendCreatureAppear); + Lua::registerMethod(L, "Player", "addAnimusMastery", PlayerFunctions::luaPlayerAddAnimusMastery); + Lua::registerMethod(L, "Player", "removeAnimusMastery", PlayerFunctions::luaPlayerRemoveAnimusMastery); + Lua::registerMethod(L, "Player", "hasAnimusMastery", PlayerFunctions::luaPlayerHasAnimusMastery); + GroupFunctions::init(L); GuildFunctions::init(L); MountFunctions::init(L); @@ -4849,3 +4854,42 @@ int PlayerFunctions::luaPlayerSendCreatureAppear(lua_State* L) { Lua::pushBoolean(L, true); return 1; } + +int PlayerFunctions::luaPlayerAddAnimusMastery(lua_State* L) { + auto player = Lua::getUserdataShared(L, 1); + if (!player) { + Lua::reportErrorFunc(Lua::getErrorDesc(LUA_ERROR_PLAYER_NOT_FOUND)); + return 1; + } + + const std::string &monsterType = Lua::getString(L, 2); + player->animusMastery()->add(monsterType); + + return 1; +} +int PlayerFunctions::luaPlayerRemoveAnimusMastery(lua_State* L) { + auto player = Lua::getUserdataShared(L, 1); + if (!player) { + Lua::reportErrorFunc(Lua::getErrorDesc(LUA_ERROR_PLAYER_NOT_FOUND)); + return 1; + } + + const std::string &monsterType = Lua::getString(L, 2); + player->animusMastery()->remove(monsterType); + + return 1; +} +int PlayerFunctions::luaPlayerHasAnimusMastery(lua_State* L) { + auto player = Lua::getUserdataShared(L, 1); + if (!player) { + Lua::reportErrorFunc(Lua::getErrorDesc(LUA_ERROR_PLAYER_NOT_FOUND)); + return 1; + } + + const std::string &monsterType = Lua::getString(L, 2); + + bool has = player->animusMastery()->has(monsterType); + Lua::pushBoolean(L, has); + + return 1; +} diff --git a/src/lua/functions/creatures/player/player_functions.hpp b/src/lua/functions/creatures/player/player_functions.hpp index ab617c35132..992d0f66b45 100644 --- a/src/lua/functions/creatures/player/player_functions.hpp +++ b/src/lua/functions/creatures/player/player_functions.hpp @@ -383,5 +383,9 @@ class PlayerFunctions { static int luaPlayerSendCreatureAppear(lua_State* L); + static int luaPlayerAddAnimusMastery(lua_State* L); + static int luaPlayerRemoveAnimusMastery(lua_State* L); + static int luaPlayerHasAnimusMastery(lua_State* L); + friend class CreatureFunctions; }; From 71e5a78a79c23e10b411e173927e115d717dc3b0 Mon Sep 17 00:00:00 2001 From: Pedro Henrique Alves Cruz Date: Fri, 6 Dec 2024 21:43:37 -0300 Subject: [PATCH 33/95] feat: gain animus mastery experience and exp message update --- data-otservbr-global/migrations/47.lua | 6 ++- data-otservbr-global/migrations/48.lua | 3 ++ .../players/animus_mastery/animus_mastery.cpp | 47 ++++++++++++++----- .../players/animus_mastery/animus_mastery.hpp | 13 +++-- src/creatures/players/player.cpp | 12 +++++ src/io/functions/iologindata_load_player.cpp | 15 ++++++ src/io/functions/iologindata_load_player.hpp | 1 + src/io/functions/iologindata_save_player.cpp | 9 ++++ src/io/iologindata.cpp | 3 ++ src/server/network/protocol/protocolgame.cpp | 5 +- 10 files changed, 95 insertions(+), 19 deletions(-) create mode 100644 data-otservbr-global/migrations/48.lua diff --git a/data-otservbr-global/migrations/47.lua b/data-otservbr-global/migrations/47.lua index 86a6d8ffec1..a3953b337e3 100644 --- a/data-otservbr-global/migrations/47.lua +++ b/data-otservbr-global/migrations/47.lua @@ -1,3 +1,7 @@ function onUpdateDatabase() - return false -- true = There are others migrations file | false = this is the last migration file + logger.info("Updating database to version 48 (feat: animus mastery (soulpit))") + + db.query("ALTER TABLE `players` ADD `animus_mastery` mediumblob NOT NULL;") + + return true end diff --git a/data-otservbr-global/migrations/48.lua b/data-otservbr-global/migrations/48.lua new file mode 100644 index 00000000000..86a6d8ffec1 --- /dev/null +++ b/data-otservbr-global/migrations/48.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/src/creatures/players/animus_mastery/animus_mastery.cpp b/src/creatures/players/animus_mastery/animus_mastery.cpp index 62c0f03d99e..d6f7b2f0459 100644 --- a/src/creatures/players/animus_mastery/animus_mastery.cpp +++ b/src/creatures/players/animus_mastery/animus_mastery.cpp @@ -12,26 +12,30 @@ #include "config/configmanager.hpp" AnimusMastery::AnimusMastery(Player &player) : - m_player(player), - maxMonsterXpMultiplier(g_configManager().getFloat(ANIMUS_MASTERY_MAX_MONSTER_XP_MULTIPLIER)), - monsterXpMultiplier(g_configManager().getFloat(ANIMUS_MASTERY_MONSTER_XP_MULTIPLIER)), - monstersXpMultiplier(g_configManager().getFloat(ANIMUS_MASTERY_MONSTERS_XP_MULTIPLIER)), - monstersAmountToMultiply(g_configManager().getFloat(ANIMUS_MASTERY_MONSTERS_TO_INCREASE_XP_MULTIPLIER)) { } + m_player(player) { + maxMonsterXpMultiplier = g_configManager().getFloat(ANIMUS_MASTERY_MAX_MONSTER_XP_MULTIPLIER); + monsterXpMultiplier = g_configManager().getFloat(ANIMUS_MASTERY_MONSTER_XP_MULTIPLIER); + monstersXpMultiplier = g_configManager().getFloat(ANIMUS_MASTERY_MONSTERS_XP_MULTIPLIER); + monstersAmountToMultiply = std::clamp(g_configManager().getNumber(ANIMUS_MASTERY_MONSTERS_TO_INCREASE_XP_MULTIPLIER), 1, std::numeric_limits::max()); +} -void AnimusMastery::add(std::string_view addMonsterType) { +void AnimusMastery::add(const std::string &addMonsterType) { if (!has(addMonsterType)) { - animusMasteries.emplace_back(addMonsterType); + const std::string &lowerMonsterName = asLowerCaseString(addMonsterType); + animusMasteries.emplace_back(lowerMonsterName); } } -void AnimusMastery::remove(std::string_view removeMonsterType) { - std::erase_if(animusMasteries, [removeMonsterType](const std::string &monsterType) { - return monsterType == removeMonsterType; +void AnimusMastery::remove(const std::string &removeMonsterType) { + const std::string &lowerMonsterName = asLowerCaseString(removeMonsterType); + std::erase_if(animusMasteries, [lowerMonsterName](const std::string &monsterType) { + return asLowerCaseString(monsterType) == lowerMonsterName; }); } -bool AnimusMastery::has(std::string_view searchMonsterType) const { - auto it = std::ranges::find(animusMasteries, searchMonsterType); +bool AnimusMastery::has(const std::string &searchMonsterType) const { + const std::string &lowerMonsterName = asLowerCaseString(searchMonsterType); + auto it = std::ranges::find(animusMasteries, lowerMonsterName); return it != animusMasteries.end(); } @@ -39,9 +43,26 @@ bool AnimusMastery::has(std::string_view searchMonsterType) const { float AnimusMastery::getExperienceMultiplier() const { uint16_t monsterAmountMultiplier = animusMasteries.size() / monstersAmountToMultiply; - return std::min(maxMonsterXpMultiplier, 1 + (monsterAmountMultiplier * monstersXpMultiplier)); + return std::min(maxMonsterXpMultiplier, 1 + (monsterXpMultiplier + (monsterAmountMultiplier * monstersXpMultiplier)) / 100); +} + +uint16_t AnimusMastery::getPoints() const { + return animusMasteries.size(); } const std::vector &AnimusMastery::getAnimusMasteries() const { return animusMasteries; } + +void AnimusMastery::serialize(PropWriteStream &propWriteStream) const { + std::ranges::for_each(animusMasteries, [&propWriteStream](const std::string &monsterName) { + propWriteStream.writeString(monsterName); + }); +} + +bool AnimusMastery::unserialize(PropStream &propStream) { + std::string monsterName; + while (propStream.readString(monsterName)) { + animusMasteries.emplace_back(monsterName); + } +} diff --git a/src/creatures/players/animus_mastery/animus_mastery.hpp b/src/creatures/players/animus_mastery/animus_mastery.hpp index c6eab7ac1da..e0b9625b4f2 100644 --- a/src/creatures/players/animus_mastery/animus_mastery.hpp +++ b/src/creatures/players/animus_mastery/animus_mastery.hpp @@ -10,20 +10,27 @@ #pragma once class Player; +class PropStream; +class PropWriteStream; class AnimusMastery { public: explicit AnimusMastery(Player &player); - void add(std::string_view addMonsterType); - void remove(std::string_view removeMonsterType); + void add(const std::string &addMonsterType); + void remove(const std::string &removeMonsterType); - bool has(std::string_view searchMonsterType) const; + bool has(const std::string &searchMonsterType) const; float getExperienceMultiplier() const; + uint16_t getPoints() const; + const std::vector &getAnimusMasteries() const; + void serialize(PropWriteStream &propWriteStream) const; + bool unserialize(PropStream &propStream); + private: Player &m_player; diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index 02e99ce62a9..7ff49607d79 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -3078,6 +3078,14 @@ void Player::addExperience(const std::shared_ptr &target, uint64_t exp exp += (exp * (1.75 * getHazardSystemPoints() * g_configManager().getFloat(HAZARD_EXP_BONUS_MULTIPLIER))) / 100.; } + const bool handleAnimusMastery = monster && animusMastery()->has(monster->getMonsterType()->name); + float animusMasteryMultiplier = 0; + + if (handleAnimusMastery) { + animusMasteryMultiplier = animusMastery()->getExperienceMultiplier(); + exp *= animusMasteryMultiplier; + } + experience += exp; if (sendText) { @@ -3089,6 +3097,10 @@ void Player::addExperience(const std::shared_ptr &target, uint64_t exp } } + if (handleAnimusMastery) { + expString = fmt::format("{} (animus mastery bonus {:.1f}%)", expString, (animusMasteryMultiplier - 1) * 100); + } + TextMessage message(MESSAGE_EXPERIENCE, "You gained " + expString + (handleHazardExperience ? " (Hazard)" : "")); message.position = position; message.primary.value = exp; diff --git a/src/io/functions/iologindata_load_player.cpp b/src/io/functions/iologindata_load_player.cpp index e724d0ceda9..aec81caaf2d 100644 --- a/src/io/functions/iologindata_load_player.cpp +++ b/src/io/functions/iologindata_load_player.cpp @@ -14,6 +14,7 @@ #include "creatures/combat/condition.hpp" #include "database/database.hpp" #include "creatures/monsters/monsters.hpp" +#include "creatures/players/animus_mastery/animus_mastery.hpp" #include "creatures/players/achievement/player_achievement.hpp" #include "creatures/players/cyclopedia/player_badge.hpp" #include "creatures/players/cyclopedia/player_cyclopedia.hpp" @@ -275,6 +276,20 @@ void IOLoginDataLoad::loadPlayerConditions(const std::shared_ptr &player } } +void IOLoginDataLoad::loadPlayerAnimusMastery(const std::shared_ptr &player, const DBResult_ptr &result) { + if (!result || !player) { + g_logger().warn("[{}] - Player or Result nullptr", __FUNCTION__); + return; + } + + unsigned long attrSize; + const char* attr = result->getStream("animus_mastery", attrSize); + PropStream propStream; + propStream.init(attr, attrSize); + + player->animusMastery()->unserialize(propStream); +} + void IOLoginDataLoad::loadPlayerDefaultOutfit(const std::shared_ptr &player, const DBResult_ptr &result) { if (!result || !player) { g_logger().warn("[{}] - Player or Result nullptr", __FUNCTION__); diff --git a/src/io/functions/iologindata_load_player.hpp b/src/io/functions/iologindata_load_player.hpp index ca05bdcb1b8..08d31e93072 100644 --- a/src/io/functions/iologindata_load_player.hpp +++ b/src/io/functions/iologindata_load_player.hpp @@ -22,6 +22,7 @@ class IOLoginDataLoad : public IOLoginData { static void loadPlayerExperience(const std::shared_ptr &player, const DBResult_ptr &result); static void loadPlayerBlessings(const std::shared_ptr &player, const DBResult_ptr &result); static void loadPlayerConditions(const std::shared_ptr &player, const DBResult_ptr &result); + static void loadPlayerAnimusMastery(const std::shared_ptr &player, const DBResult_ptr &result); static void loadPlayerDefaultOutfit(const std::shared_ptr &player, const DBResult_ptr &result); static void loadPlayerSkullSystem(const std::shared_ptr &player, const DBResult_ptr &result); static void loadPlayerSkill(const std::shared_ptr &player, const DBResult_ptr &result); diff --git a/src/io/functions/iologindata_save_player.cpp b/src/io/functions/iologindata_save_player.cpp index 613fdac1cb0..47b2c92fee0 100644 --- a/src/io/functions/iologindata_save_player.cpp +++ b/src/io/functions/iologindata_save_player.cpp @@ -10,6 +10,7 @@ #include "io/functions/iologindata_save_player.hpp" #include "config/configmanager.hpp" +#include "creatures/players/animus_mastery/animus_mastery.hpp" #include "creatures/combat/condition.hpp" #include "creatures/monsters/monsters.hpp" #include "game/game.hpp" @@ -246,6 +247,14 @@ bool IOLoginDataSave::savePlayerFirst(const std::shared_ptr &player) { query << "`conditions` = " << db.escapeBlob(attributes, static_cast(attributesSize)) << ","; + // serialize animus mastery + PropWriteStream propAnimusMasteryStream; + player->animusMastery()->serialize(propAnimusMasteryStream); + size_t animusMasterySize; + const char* animusMastery = propAnimusMasteryStream.getStream(animusMasterySize); + + query << "`animus_mastery` = " << db.escapeBlob(animusMastery, static_cast(animusMasterySize)) << ","; + if (g_game().getWorldType() != WORLD_TYPE_PVP_ENFORCED) { int64_t skullTime = 0; diff --git a/src/io/iologindata.cpp b/src/io/iologindata.cpp index c0a6a13f363..7847e0fab73 100644 --- a/src/io/iologindata.cpp +++ b/src/io/iologindata.cpp @@ -113,6 +113,9 @@ bool IOLoginData::loadPlayer(const std::shared_ptr &player, const DBResu // load conditions IOLoginDataLoad::loadPlayerConditions(player, result); + // load animus mastery + IOLoginDataLoad::loadPlayerAnimusMastery(player, result); + // load default outfit IOLoginDataLoad::loadPlayerDefaultOutfit(player, result); diff --git a/src/server/network/protocol/protocolgame.cpp b/src/server/network/protocol/protocolgame.cpp index 2f7ff9e2479..3fe6544f805 100644 --- a/src/server/network/protocol/protocolgame.cpp +++ b/src/server/network/protocol/protocolgame.cpp @@ -19,6 +19,7 @@ #include "creatures/monsters/monster.hpp" #include "creatures/monsters/monsters.hpp" #include "creatures/npcs/npc.hpp" +#include "creatures/players/animus_mastery/animus_mastery.hpp" #include "creatures/players/achievement/player_achievement.hpp" #include "creatures/players/cyclopedia/player_badge.hpp" #include "creatures/players/cyclopedia/player_cyclopedia.hpp" @@ -2379,8 +2380,8 @@ void ProtocolGame::parseBestiarysendMonsterData(NetworkMessage &msg) { newmsg.addByte(currentLevel); - newmsg.add(0); // Animus Mastery Bonus - newmsg.add(0); // Animus Mastery Points + newmsg.add(static_cast((player->animusMastery()->getExperienceMultiplier() - 1) * 1000)); // Animus Mastery Bonus + newmsg.add(player->animusMastery()->getPoints()); // Animus Mastery Points newmsg.add(killCounter); From c61916ec78f96ad2c0b988337da2fc8c714c6cf3 Mon Sep 17 00:00:00 2001 From: Felipe Paluco Date: Sat, 7 Dec 2024 09:45:13 -0300 Subject: [PATCH 34/95] feat: add chance to drop soul prism --- data-otservbr-global/lib/others/soulpit.lua | 7 ++++--- .../monster/ondroploot_soul_core.lua | 17 ++++++++++++----- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/data-otservbr-global/lib/others/soulpit.lua b/data-otservbr-global/lib/others/soulpit.lua index 752a207f401..15ed59d7681 100644 --- a/data-otservbr-global/lib/others/soulpit.lua +++ b/data-otservbr-global/lib/others/soulpit.lua @@ -1,8 +1,9 @@ SoulPit = { SoulCoresConfiguration = { - chanceToGetSameMonsterSoulCore = 30, -- 30% - chanceToDropSoulCore = 50, -- 50% - chanceToGetOminousSoulCore = 5, -- 5% + chanceToGetSameMonsterSoulCore = 15, -- 15% + chanceToDropSoulCore = 10, -- 10% + chanceToGetOminousSoulCore = 3, -- 3% + chanceToDropSoulPrism = 8, -- 8% monsterVariationsSoulCore = { ["Horse"] = "horse soul core (taupe)", ["Brown Horse"] = "horse soul core (brown)", diff --git a/data/scripts/eventcallbacks/monster/ondroploot_soul_core.lua b/data/scripts/eventcallbacks/monster/ondroploot_soul_core.lua index eafa067bc98..aeb086cfd47 100644 --- a/data/scripts/eventcallbacks/monster/ondroploot_soul_core.lua +++ b/data/scripts/eventcallbacks/monster/ondroploot_soul_core.lua @@ -8,7 +8,7 @@ function callback.monsterOnDropLoot(monster, corpse) if not player or not player:canReceiveLoot() then return end - if monster:getMonsterForgeClassification() == FORGE_NORMAL_MONSTER then + if monster:getMonsterForgeClassification() ~= FORGE_FIENDISH_MONSTER then return end @@ -36,15 +36,22 @@ function callback.monsterOnDropLoot(monster, corpse) end if soulCoreId then - lootTable = { - [soulCoreId] = { - count = 1, - }, + lootTable[soulCoreId] = { + count = 1, } else return {} end end + + if math.random(100) < SoulPit.SoulCoresConfiguration.chanceToDropSoulPrism then + local soulPrismId = getItemIdByName("soul prism") + if soulPrismId then + lootTable[soulPrismId] = { + count = 1, + } + end + end corpse:addLoot(mType:generateLootRoll({}, lootTable, player)) end From 384f35d4a4c614af870e56993e04e0efb88ba1bb Mon Sep 17 00:00:00 2001 From: Pedro Henrique Alves Cruz Date: Mon, 9 Dec 2024 02:27:47 -0300 Subject: [PATCH 35/95] fix: final adjustments --- data-otservbr-global/lib/others/soulpit.lua | 43 +++++++++++++---- .../monster/humanoids/orc_warlord.lua | 3 -- .../scripts/actions/soulpit/soulpit_fight.lua | 47 +++++++++---------- .../monster/soulpit_creatureevents.lua | 13 ----- .../spells/monster/soulpit_intensehex.lua | 1 + .../spells/monster/soulpit_opressor.lua | 1 + .../spells/monster/soulpit_powerless.lua | 1 + src/creatures/combat/combat.cpp | 3 +- src/creatures/monsters/monster.cpp | 20 ++++++-- src/creatures/monsters/monster.hpp | 10 +++- src/game/game.cpp | 14 +++++- .../creatures/monster/monster_functions.cpp | 37 +++++++++++++++ .../creatures/monster/monster_functions.hpp | 4 +- 13 files changed, 140 insertions(+), 57 deletions(-) diff --git a/data-otservbr-global/lib/others/soulpit.lua b/data-otservbr-global/lib/others/soulpit.lua index 752a207f401..ad5d7076053 100644 --- a/data-otservbr-global/lib/others/soulpit.lua +++ b/data-otservbr-global/lib/others/soulpit.lua @@ -1,4 +1,5 @@ SoulPit = { + registeredMonstersSpell = {}, SoulCoresConfiguration = { chanceToGetSameMonsterSoulCore = 30, -- 30% chanceToDropSoulCore = 50, -- 50% @@ -94,9 +95,12 @@ SoulPit = { }, bossAbilities = { overpowerSoulPit = { - bonusCriticalDamage = 1.1, - player = true, - monster = false, + criticalChance = 50, -- 50% + criticalDamage = 25, -- 25% + apply = function(monster) + monster:criticalChance(SoulPit.bossAbilities.overpowerSoulPit.criticalChance) + monster:criticalDamage(SoulPit.bossAbilities.overpowerSoulPit.criticalDamage) + end, }, enrageSoulPit = { bounds = { @@ -105,15 +109,38 @@ SoulPit = { [{ 0.4, 0.2 }] = 0.6, -- 40% damage reduction [{ 0.0, 0.2 }] = 0.4, -- 60% damage reduction }, - player = false, - monster = true, + apply = function(monster) + monster:registerEvent("enrageSoulPit") + end, }, opressorSoulPit = { - player = false, - monster = true, + spells = { + { name = "soulpit opressor", interval = 2000, chance = 25, minDamage = 0, maxDamage = 0 }, + { name = "soulpit powerless", interval = 2000, chance = 30, minDamage = 0, maxDamage = 0 }, + { name = "soulpit intensehex", interval = 2000, chance = 15, minDamage = 0, maxDamage = 0 }, + }, + apply = function(monster) + monster:registerEvent("opressorSoulPit") + local monsterType = monster:getType() + local monsterTypeName = monsterType:name() + + -- Checking spells + if SoulPit.registeredMonstersSpell[monsterTypeName] then + return true + end + + -- Applying spells + for _, spell in pairs(SoulPit.bossAbilities.opressorSoulPit.spells) do + monsterType:addAttack(readSpell(spell, monsterType)) + end + + SoulPit.registeredMonstersSpell[monsterTypeName] = true + + return true + end, }, }, - timeToKick = 3 * 1000, -- 3 seconds + timeToKick = 10 * 60 * 1000, -- 10 minutes checkMonstersDelay = 4.5 * 1000, -- 4.5 seconds | The check delay should never be less than the timeToSpawnMonsters. timeToSpawnMonsters = 4 * 1000, -- 4 seconds totalMonsters = 7, diff --git a/data-otservbr-global/monster/humanoids/orc_warlord.lua b/data-otservbr-global/monster/humanoids/orc_warlord.lua index 5b33843d971..db5b0e20288 100644 --- a/data-otservbr-global/monster/humanoids/orc_warlord.lua +++ b/data-otservbr-global/monster/humanoids/orc_warlord.lua @@ -107,9 +107,6 @@ monster.loot = { monster.attacks = { { name = "melee", interval = 2000, chance = 100, minDamage = 0, maxDamage = -250 }, { name = "combat", interval = 2000, chance = 20, type = COMBAT_PHYSICALDAMAGE, minDamage = 0, maxDamage = -200, range = 7, shootEffect = CONST_ANI_THROWINGSTAR, target = false }, - { name = "soulpit opressor", interval = 2000, chance = 25, minDamage = 0, maxDamage = 0 }, - { name = "soulpit powerless", interval = 2000, chance = 30, minDamage = 0, maxDamage = 0 }, - { name = "soulpit intensehex", interval = 2000, chance = 15, minDamage = 0, maxDamage = 0 }, } monster.defenses = { diff --git a/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua b/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua index a4036c702d0..0cb2bfd402b 100644 --- a/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua +++ b/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua @@ -17,12 +17,6 @@ function zoneEvent.afterLeave(zone, creature) SoulPit.obeliskPosition:transformItem(SoulPit.obeliskActive, SoulPit.obeliskInactive) end end - - for abilityName, abilityInfo in pairs(SoulPit.bossAbilities) do - if abilityInfo.player then - player:unregisterEvent(abilityName) - end - end end zoneEvent:register() @@ -32,7 +26,6 @@ function soulPitAction.onUse(player, item, fromPosition, target, toPosition, isH return true end - logger.warn(item:getName()) if target and target:getId() == SoulPit.obeliskActive then creature:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Someone is fighting in the soulpit!") return false @@ -69,12 +62,27 @@ function soulPitAction.onUse(player, item, fromPosition, target, toPosition, isH local infoPositions = lever:getInfoPositions() return true end) + lever:checkPositions() - if lever:checkConditions() then - lever:teleportPlayers() - SoulPit.obeliskPosition:transformItem(SoulPit.obeliskInactive, SoulPit.obeliskActive) + if not lever:checkConditions() then + return true + end + + item:remove(1) + + if SoulPit.kickEvent then + stopEvent(SoulPit.kickEvent) end + lever:teleportPlayers() + SoulPit.obeliskPosition:transformItem(SoulPit.obeliskInactive, SoulPit.obeliskActive) + SoulPit.kickEvent = addEvent(function() + SoulPit.kickEvent = nil + SoulPit.encounter = nil + SoulPit.zone:removePlayers() + SoulPit.obeliskPosition:transformItem(SoulPit.obeliskActive, SoulPit.obeliskInactive) + end, SoulPit.timeToKick) + local monsterName = string.gsub(item:getName(), " soul core", "") local monsterVariationName = SoulPit.getMonsterVariationNameBySoulCore(item:getName()) monsterName = monsterVariationName and monsterVariationName or monsterName @@ -97,11 +105,7 @@ function soulPitAction.onUse(player, item, fromPosition, target, toPosition, isH player:sendTextMessage(MESSAGE_EVENT_ADVANCE, string.format("You have defeated the core of the %s soul and unlocked its animus mastery!", monsterName)) end - SoulPit.kickEvent = addEvent(function() - SoulPit.encounter = nil - SoulPit.zone:removePlayers() - SoulPit.obeliskPosition:transformItem(SoulPit.obeliskActive, SoulPit.obeliskInactive) - end, SoulPit.timeToKick) + SoulPit.zone:removePlayers() end SoulPit.encounter = encounter @@ -125,12 +129,7 @@ function soulPitAction.onUse(player, item, fromPosition, target, toPosition, isH return false end if stack == 40 then - if bossAbility.monster then - monster:registerEvent(bossAbilityName) - end - if bossAbility.player then - player:registerEvent(bossAbilityName) - end + bossAbility.apply(monster) end end, SoulPit.timeToSpawnMonsters, monsterName, stack, position, randomAbility, chosenBossAbility) end @@ -152,8 +151,8 @@ function soulPitAction.onUse(player, item, fromPosition, target, toPosition, isH end for _, itemType in pairs(SoulPit.soulCores) do - -- if itemType:getId() ~= 49164 then -- TO-DO: currently Game.getSoulCoreItems() it's returning soul prism item in the results, we don't want this. - soulPitAction:id(itemType:getId()) - -- end + if itemType:getId() ~= 49164 then -- Exclude soul prism + soulPitAction:id(itemType:getId()) + end end soulPitAction:register() diff --git a/data-otservbr-global/scripts/creaturescripts/monster/soulpit_creatureevents.lua b/data-otservbr-global/scripts/creaturescripts/monster/soulpit_creatureevents.lua index c399ec7fb0a..e2bc9a27cea 100644 --- a/data-otservbr-global/scripts/creaturescripts/monster/soulpit_creatureevents.lua +++ b/data-otservbr-global/scripts/creaturescripts/monster/soulpit_creatureevents.lua @@ -16,19 +16,6 @@ function enrage.onHealthChange(creature, attacker, primaryDamage, primaryType, s end enrage:register() -local overpower = CreatureEvent("overpowerSoulPit") -function overpower.onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType) - if creature and attacker and attacker:isMonster() and attacker:getForgeStack() == 40 and not attacker:getMaster() then - local bonusCriticalDamage = SoulPit.bossAbilities.overpowerSoulPit.bonusCriticalDamage - primaryDamage = primaryDamage * bonusCriticalDamage - secondaryDamage = secondaryDamage * bonusCriticalDamage - creature:getPosition():sendMagicEffect(CONST_ME_CRITICAL_DAMAGE) - end - - return primaryDamage, primaryType, secondaryDamage, secondaryType -end -overpower:register() - local opressor = CreatureEvent("opressorSoulPit") function opressor.onThink() end opressor:register() diff --git a/data-otservbr-global/scripts/spells/monster/soulpit_intensehex.lua b/data-otservbr-global/scripts/spells/monster/soulpit_intensehex.lua index 51ccca2d979..2f6b25b0d78 100644 --- a/data-otservbr-global/scripts/spells/monster/soulpit_intensehex.lua +++ b/data-otservbr-global/scripts/spells/monster/soulpit_intensehex.lua @@ -22,4 +22,5 @@ spell:words("###940") spell:blockWalls(true) spell:needTarget(true) spell:needLearn(true) +spell:isAggressive(true) spell:register() diff --git a/data-otservbr-global/scripts/spells/monster/soulpit_opressor.lua b/data-otservbr-global/scripts/spells/monster/soulpit_opressor.lua index 05493618461..b504fcb0116 100644 --- a/data-otservbr-global/scripts/spells/monster/soulpit_opressor.lua +++ b/data-otservbr-global/scripts/spells/monster/soulpit_opressor.lua @@ -41,4 +41,5 @@ spell:words("###938") spell:blockWalls(true) spell:needTarget(false) spell:needLearn(true) +spell:isAggressive(true) spell:register() diff --git a/data-otservbr-global/scripts/spells/monster/soulpit_powerless.lua b/data-otservbr-global/scripts/spells/monster/soulpit_powerless.lua index f2fe9d3a4a9..13f2969eaac 100644 --- a/data-otservbr-global/scripts/spells/monster/soulpit_powerless.lua +++ b/data-otservbr-global/scripts/spells/monster/soulpit_powerless.lua @@ -22,4 +22,5 @@ spell:words("###939") spell:blockWalls(true) spell:needTarget(true) spell:needLearn(true) +spell:isAggressive(true) spell:register() diff --git a/src/creatures/combat/combat.cpp b/src/creatures/combat/combat.cpp index a70ef43a63a..273eb3d382d 100644 --- a/src/creatures/combat/combat.cpp +++ b/src/creatures/combat/combat.cpp @@ -2254,7 +2254,8 @@ void Combat::applyExtensions(const std::shared_ptr &caster, const std: } } } else if (monster) { - chance = monster->critChance() * 100; + chance = monster->getCriticalChance() * 100; + bonus = monster->getCriticalDamage() * 100; } bonus += damage.criticalDamage; diff --git a/src/creatures/monsters/monster.cpp b/src/creatures/monsters/monster.cpp index ae1f997076a..e6f9b8d7d27 100644 --- a/src/creatures/monsters/monster.cpp +++ b/src/creatures/monsters/monster.cpp @@ -248,8 +248,20 @@ bool Monster::canSeeInvisibility() const { return isImmune(CONDITION_INVISIBLE); } -uint16_t Monster::critChance() const { - return mType->info.critChance; +void Monster::setCriticalDamage(uint16_t damage) { + criticalDamage = damage; +} + +uint16_t Monster::getCriticalDamage() const { + return criticalDamage; +} + +void Monster::setCriticalChance(uint16_t chance) { + criticalChance = chance; +} + +uint16_t Monster::getCriticalChance() const { + return mType->info.critChance + criticalChance; } uint32_t Monster::getManaCost() const { @@ -1131,7 +1143,7 @@ void Monster::doAttacking(uint32_t interval) { bool inRange = false; if (spellBlock.spell == nullptr || (spellBlock.isMelee && isFleeing())) { - continue; + continue; } if (canUseSpell(myPos, targetPos, spellBlock, interval, inRange, resetTicks)) { @@ -1145,7 +1157,7 @@ void Monster::doAttacking(uint32_t interval) { maxCombatValue = spellBlock.maxCombatValue; if (spellBlock.spell == nullptr) { - continue; + continue; } spellBlock.spell->castSpell(getMonster(), attackedCreature); diff --git a/src/creatures/monsters/monster.hpp b/src/creatures/monsters/monster.hpp index 4c9a2e6c308..c6c0ac6aa1e 100644 --- a/src/creatures/monsters/monster.hpp +++ b/src/creatures/monsters/monster.hpp @@ -73,7 +73,6 @@ class Monster final : public Creature { bool isHostile() const; bool isFamiliar() const; bool canSeeInvisibility() const override; - uint16_t critChance() const; uint32_t getManaCost() const; RespawnType getRespawnType() const; void setSpawnMonster(const std::shared_ptr &newSpawnMonster); @@ -227,6 +226,12 @@ class Monster final : public Creature { void setDead(bool isDead); + void setCriticalChance(uint16_t chance); + uint16_t getCriticalChance() const; + + void setCriticalDamage(uint16_t damage); + uint16_t getCriticalDamage() const; + protected: void onExecuteAsyncTasks() override; @@ -257,6 +262,9 @@ class Monster final : public Creature { uint16_t totalPlayersOnScreen = 0; + uint16_t criticalChance = 0; + uint16_t criticalDamage = 0; + uint32_t attackTicks = 0; uint32_t targetChangeTicks = 0; uint32_t defenseTicks = 0; diff --git a/src/game/game.cpp b/src/game/game.cpp index 7a492348649..fc17d664921 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -7616,8 +7616,15 @@ void Game::buildMessageAsTarget( const std::string &damageString ) const { ss.str({}); - auto attackMsg = damage.critical ? "critical " : ""; - auto article = damage.critical ? "a" : "an"; + const auto &monster = attacker->getMonster(); + bool handleSoulPit = monster ? monster->getSoulPit() && monster->getForgeStack() == 40 : false; + + std::string attackMsg = ""; + std::string article = "an"; + if (!handleSoulPit && damage.critical) { + attackMsg = "critical"; + article = "a"; + } ss << "You lose " << damageString; if (!attacker) { ss << '.'; @@ -7629,6 +7636,9 @@ void Game::buildMessageAsTarget( if (damage.extension) { ss << " " << damage.exString; } + if (handleSoulPit && damage.critical) { + ss << " (Soulpit Crit)"; + } message.type = MESSAGE_DAMAGE_RECEIVED; message.text = ss.str(); } diff --git a/src/lua/functions/creatures/monster/monster_functions.cpp b/src/lua/functions/creatures/monster/monster_functions.cpp index 77793f8508b..83b484d5e53 100644 --- a/src/lua/functions/creatures/monster/monster_functions.cpp +++ b/src/lua/functions/creatures/monster/monster_functions.cpp @@ -75,6 +75,9 @@ void MonsterFunctions::init(lua_State* L) { Lua::registerMethod(L, "Monster", "isDead", MonsterFunctions::luaMonsterIsDead); Lua::registerMethod(L, "Monster", "immune", MonsterFunctions::luaMonsterImmune); + Lua::registerMethod(L, "Monster", "criticalChance", MonsterFunctions::luaMonsterCriticalChance); + Lua::registerMethod(L, "Monster", "criticalDamage", MonsterFunctions::luaMonsterCriticalDamage); + CharmFunctions::init(L); LootFunctions::init(L); MonsterSpellFunctions::init(L); @@ -791,3 +794,37 @@ int MonsterFunctions::luaMonsterImmune(lua_State* L) { Lua::pushBoolean(L, monster->isImmune()); return 1; } + +int MonsterFunctions::luaMonsterCriticalChance(lua_State* L) { + // get: monster:criticalChance(); set: monster:criticalChance(critical) + const auto &monster = Lua::getUserdataShared(L, 1); + const auto critical = Lua::getNumber(L, 2, 0); + if (monster) { + if (lua_gettop(L) == 1) { + Lua::pushBoolean(L, monster->getCriticalChance()); + } else { + monster->setCriticalChance(critical); + Lua::pushBoolean(L, monster->getCriticalChance()); + } + } else { + lua_pushnil(L); + } + return 1; +} + +int MonsterFunctions::luaMonsterCriticalDamage(lua_State* L) { + // get: monster:criticalDamage(); set: monster:criticalDamage(damage) + const auto &monster = Lua::getUserdataShared(L, 1); + const auto damage = Lua::getNumber(L, 2, 0); + if (monster) { + if (lua_gettop(L) == 1) { + Lua::pushBoolean(L, monster->getCriticalDamage()); + } else { + monster->setCriticalDamage(damage); + Lua::pushBoolean(L, monster->getCriticalDamage()); + } + } else { + lua_pushnil(L); + } + return 1; +} diff --git a/src/lua/functions/creatures/monster/monster_functions.hpp b/src/lua/functions/creatures/monster/monster_functions.hpp index fdc45d7732c..ca8f5848271 100644 --- a/src/lua/functions/creatures/monster/monster_functions.hpp +++ b/src/lua/functions/creatures/monster/monster_functions.hpp @@ -80,6 +80,8 @@ class MonsterFunctions { static int luaMonsterIsDead(lua_State* L); static int luaMonsterImmune(lua_State* L); - + static int luaMonsterCriticalChance(lua_State* L); + static int luaMonsterCriticalDamage(lua_State* L); + friend class CreatureFunctions; }; From 25c48e2cd389df91b0f8460961d714a16f9bb0c0 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Mon, 9 Dec 2024 05:28:55 +0000 Subject: [PATCH 36/95] Code format - (Clang-format) --- src/creatures/monsters/monster.cpp | 4 ++-- src/lua/functions/creatures/monster/monster_functions.cpp | 2 +- src/lua/functions/creatures/monster/monster_functions.hpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/creatures/monsters/monster.cpp b/src/creatures/monsters/monster.cpp index e6f9b8d7d27..b689feb2f25 100644 --- a/src/creatures/monsters/monster.cpp +++ b/src/creatures/monsters/monster.cpp @@ -1143,7 +1143,7 @@ void Monster::doAttacking(uint32_t interval) { bool inRange = false; if (spellBlock.spell == nullptr || (spellBlock.isMelee && isFleeing())) { - continue; + continue; } if (canUseSpell(myPos, targetPos, spellBlock, interval, inRange, resetTicks)) { @@ -1157,7 +1157,7 @@ void Monster::doAttacking(uint32_t interval) { maxCombatValue = spellBlock.maxCombatValue; if (spellBlock.spell == nullptr) { - continue; + continue; } spellBlock.spell->castSpell(getMonster(), attackedCreature); diff --git a/src/lua/functions/creatures/monster/monster_functions.cpp b/src/lua/functions/creatures/monster/monster_functions.cpp index 83b484d5e53..e3802f02a64 100644 --- a/src/lua/functions/creatures/monster/monster_functions.cpp +++ b/src/lua/functions/creatures/monster/monster_functions.cpp @@ -77,7 +77,7 @@ void MonsterFunctions::init(lua_State* L) { Lua::registerMethod(L, "Monster", "criticalChance", MonsterFunctions::luaMonsterCriticalChance); Lua::registerMethod(L, "Monster", "criticalDamage", MonsterFunctions::luaMonsterCriticalDamage); - + CharmFunctions::init(L); LootFunctions::init(L); MonsterSpellFunctions::init(L); diff --git a/src/lua/functions/creatures/monster/monster_functions.hpp b/src/lua/functions/creatures/monster/monster_functions.hpp index ca8f5848271..a49b234f39c 100644 --- a/src/lua/functions/creatures/monster/monster_functions.hpp +++ b/src/lua/functions/creatures/monster/monster_functions.hpp @@ -82,6 +82,6 @@ class MonsterFunctions { static int luaMonsterImmune(lua_State* L); static int luaMonsterCriticalChance(lua_State* L); static int luaMonsterCriticalDamage(lua_State* L); - + friend class CreatureFunctions; }; From 14abc6043d4a46054d18f7d272bc911567ffbafd Mon Sep 17 00:00:00 2001 From: Pedro Henrique Alves Cruz Date: Sat, 4 Jan 2025 22:03:40 -0300 Subject: [PATCH 37/95] perf: condition --- .../spells/monster/soulpit_intensehex.lua | 2 ++ src/creatures/combat/condition.cpp | 23 +++---------------- 2 files changed, 5 insertions(+), 20 deletions(-) diff --git a/data-otservbr-global/scripts/spells/monster/soulpit_intensehex.lua b/data-otservbr-global/scripts/spells/monster/soulpit_intensehex.lua index 2f6b25b0d78..675a5f3b4fc 100644 --- a/data-otservbr-global/scripts/spells/monster/soulpit_intensehex.lua +++ b/data-otservbr-global/scripts/spells/monster/soulpit_intensehex.lua @@ -3,6 +3,8 @@ combat:setParameter(COMBAT_PARAM_EFFECT, CONST_ME_STUN) combat:setParameter(COMBAT_PARAM_DISTANCEEFFECT, CONST_ANI_NONE) local condition = Condition(CONDITION_INTENSEHEX) +condition:setParameter(CONDITION_PARAM_BUFF_DAMAGEDEALT, 50) +condition:setParameter(CONDITION_PARAM_BUFF_HEALINGRECEIVED, 50) condition:setParameter(CONDITION_PARAM_TICKS, 3000) combat:addCondition(condition) diff --git a/src/creatures/combat/condition.cpp b/src/creatures/combat/condition.cpp index 9bb69dac9bc..02ad9e8c035 100644 --- a/src/creatures/combat/condition.cpp +++ b/src/creatures/combat/condition.cpp @@ -206,8 +206,6 @@ bool Condition::executeCondition(const std::shared_ptr &creature, int3 } std::shared_ptr Condition::createCondition(ConditionId_t id, ConditionType_t type, int32_t ticks, int32_t param /* = 0*/, bool buff /* = false*/, uint32_t subId /* = 0*/, bool isPersistent /* = false*/) { - - std::shared_ptr conditionAttributes = nullptr; switch (type) { case CONDITION_POISON: case CONDITION_FIRE: @@ -238,6 +236,9 @@ std::shared_ptr Condition::createCondition(ConditionId_t id, Conditio case CONDITION_SOUL: return std::make_shared(id, type, ticks, buff, subId); + case CONDITION_LESSERHEX: + case CONDITION_INTENSEHEX: + case CONDITION_GREATERHEX: case CONDITION_ATTRIBUTES: return std::make_shared(id, type, ticks, buff, subId); @@ -253,24 +254,6 @@ std::shared_ptr Condition::createCondition(ConditionId_t id, Conditio case CONDITION_FEARED: return std::make_shared(id, type, ticks, buff, subId); - case CONDITION_LESSERHEX: - conditionAttributes = std::make_shared(id, type, ticks, buff, subId); - conditionAttributes->setParam(CONDITION_PARAM_BUFF_HEALINGRECEIVED, 50); - return conditionAttributes; - - case CONDITION_INTENSEHEX: - conditionAttributes = std::make_shared(id, type, ticks, buff, subId); - conditionAttributes->setParam(CONDITION_PARAM_BUFF_HEALINGRECEIVED, 50); - conditionAttributes->setParam(CONDITION_PARAM_BUFF_DAMAGEDEALT, 50); - return conditionAttributes; - - case CONDITION_GREATERHEX: - conditionAttributes = std::make_shared(id, type, ticks, buff, subId); - conditionAttributes->setParam(CONDITION_PARAM_BUFF_HEALINGRECEIVED, 50); - conditionAttributes->setParam(CONDITION_PARAM_BUFF_DAMAGEDEALT, 50); - conditionAttributes->setParam(CONDITION_PARAM_STAT_MAXHITPOINTSPERCENT, 60); - return conditionAttributes; - case CONDITION_ROOTED: case CONDITION_INFIGHT: case CONDITION_DRUNK: From 03d578c38437a280b612fe55623bc8b182b41d0e Mon Sep 17 00:00:00 2001 From: Pedro Henrique Alves Cruz Date: Tue, 7 Jan 2025 00:37:38 -0300 Subject: [PATCH 38/95] fix: instace monsters spells and some improvements --- data-otservbr-global/lib/others/soulpit.lua | 19 +------- .../scripts/actions/soulpit/soulpit_fight.lua | 12 +++-- .../monster/soulpit_creatureevents.lua | 4 -- .../spells/monster/soulpit_intensehex.lua | 7 +-- .../spells/monster/soulpit_opressor.lua | 7 +-- .../spells/monster/soulpit_powerless.lua | 7 +-- src/creatures/monsters/monster.cpp | 8 ++-- src/creatures/monsters/monster.hpp | 3 ++ src/creatures/monsters/monsters.hpp | 16 ------- .../functions/core/game/game_functions.cpp | 8 ---- .../creatures/monster/monster_functions.cpp | 47 +++++++++++++++++++ .../creatures/monster/monster_functions.hpp | 3 ++ 12 files changed, 72 insertions(+), 69 deletions(-) diff --git a/data-otservbr-global/lib/others/soulpit.lua b/data-otservbr-global/lib/others/soulpit.lua index 8ae31552317..09bc94af106 100644 --- a/data-otservbr-global/lib/others/soulpit.lua +++ b/data-otservbr-global/lib/others/soulpit.lua @@ -1,5 +1,4 @@ SoulPit = { - registeredMonstersSpell = {}, SoulCoresConfiguration = { chanceToGetSameMonsterSoulCore = 15, -- 15% chanceToDropSoulCore = 10, -- 10% @@ -34,27 +33,22 @@ SoulPit = { { pos = Position(32371, 31155, 8), teleport = Position(32373, 31138, 8), - effect = CONST_ME_TELEPORT, }, { pos = Position(32371, 31156, 8), teleport = Position(32373, 31138, 8), - effect = CONST_ME_TELEPORT, }, { pos = Position(32371, 31157, 8), teleport = Position(32373, 31138, 8), - effect = CONST_ME_TELEPORT, }, { pos = Position(32371, 31158, 8), teleport = Position(32373, 31138, 8), - effect = CONST_ME_TELEPORT, }, { pos = Position(32371, 31159, 8), teleport = Position(32373, 31138, 8), - effect = CONST_ME_TELEPORT, }, }, waves = { @@ -121,22 +115,11 @@ SoulPit = { { name = "soulpit intensehex", interval = 2000, chance = 15, minDamage = 0, maxDamage = 0 }, }, apply = function(monster) - monster:registerEvent("opressorSoulPit") - local monsterType = monster:getType() - local monsterTypeName = monsterType:name() - - -- Checking spells - if SoulPit.registeredMonstersSpell[monsterTypeName] then - return true - end - -- Applying spells for _, spell in pairs(SoulPit.bossAbilities.opressorSoulPit.spells) do - monsterType:addAttack(readSpell(spell, monsterType)) + monster:addAttackSpell(readSpell(spell, monster:getType())) end - SoulPit.registeredMonstersSpell[monsterTypeName] = true - return true end, }, diff --git a/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua b/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua index 0cb2bfd402b..e778386733d 100644 --- a/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua +++ b/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua @@ -116,7 +116,10 @@ function soulPitAction.onUse(player, item, fromPosition, target, toPosition, isH local position = stack ~= 40 and SoulPit.zone:randomPosition() or SoulPit.bossPosition for i = 1, SoulPit.timeToSpawnMonsters / 1000 do encounter:addEvent(function(position) - position:sendMagicEffect(SoulPit.effects[stack]) + local effect = SoulPit.effects[stack] + if effect then + position:sendMagicEffect(effect) + end end, i * 1000, position) end @@ -128,9 +131,12 @@ function soulPitAction.onUse(player, item, fromPosition, target, toPosition, isH if not monster then return false end - if stack == 40 then - bossAbility.apply(monster) + + if stack ~= 40 then + return false end + + bossAbility.apply(monster) end, SoulPit.timeToSpawnMonsters, monsterName, stack, position, randomAbility, chosenBossAbility) end end diff --git a/data-otservbr-global/scripts/creaturescripts/monster/soulpit_creatureevents.lua b/data-otservbr-global/scripts/creaturescripts/monster/soulpit_creatureevents.lua index e2bc9a27cea..118a190bf51 100644 --- a/data-otservbr-global/scripts/creaturescripts/monster/soulpit_creatureevents.lua +++ b/data-otservbr-global/scripts/creaturescripts/monster/soulpit_creatureevents.lua @@ -15,7 +15,3 @@ function enrage.onHealthChange(creature, attacker, primaryDamage, primaryType, s return primaryDamage, primaryType, secondaryDamage, secondaryType end enrage:register() - -local opressor = CreatureEvent("opressorSoulPit") -function opressor.onThink() end -opressor:register() diff --git a/data-otservbr-global/scripts/spells/monster/soulpit_intensehex.lua b/data-otservbr-global/scripts/spells/monster/soulpit_intensehex.lua index 675a5f3b4fc..9dc9f54dfbe 100644 --- a/data-otservbr-global/scripts/spells/monster/soulpit_intensehex.lua +++ b/data-otservbr-global/scripts/spells/monster/soulpit_intensehex.lua @@ -11,12 +11,7 @@ combat:addCondition(condition) local spell = Spell("instant") function spell.onCastSpell(creature, var) - local monster = creature:getMonster() - if monster and monster:soulPit() and not monster:getMaster() and table.contains(creature:getEvents(CREATURE_EVENT_THINK), "opressorSoulPit") then - return combat:execute(creature, var) - end - - return true + return combat:execute(creature, var) end spell:name("soulpit intensehex") diff --git a/data-otservbr-global/scripts/spells/monster/soulpit_opressor.lua b/data-otservbr-global/scripts/spells/monster/soulpit_opressor.lua index b504fcb0116..0b29b4944b5 100644 --- a/data-otservbr-global/scripts/spells/monster/soulpit_opressor.lua +++ b/data-otservbr-global/scripts/spells/monster/soulpit_opressor.lua @@ -26,11 +26,8 @@ local spell = Spell("instant") local combats = { combatRoot, combatFear } function spell.onCastSpell(creature, var) - local monster = creature:getMonster() - if monster and monster:soulPit() and not monster:getMaster() and table.contains(creature:getEvents(CREATURE_EVENT_THINK), "opressorSoulPit") then - for _, combat in pairs(combats) do - combat:execute(creature, var) - end + for _, combat in pairs(combats) do + combat:execute(creature, var) end return true diff --git a/data-otservbr-global/scripts/spells/monster/soulpit_powerless.lua b/data-otservbr-global/scripts/spells/monster/soulpit_powerless.lua index 13f2969eaac..36af89a9860 100644 --- a/data-otservbr-global/scripts/spells/monster/soulpit_powerless.lua +++ b/data-otservbr-global/scripts/spells/monster/soulpit_powerless.lua @@ -9,12 +9,7 @@ combat:addCondition(condition) local spell = Spell("instant") function spell.onCastSpell(creature, var) - local monster = creature:getMonster() - if monster and monster:soulPit() and not monster:getMaster() and table.contains(creature:getEvents(CREATURE_EVENT_THINK), "opressorSoulPit") then - return combat:execute(creature, var) - end - - return true + return combat:execute(creature, var) end spell:name("soulpit powerless") diff --git a/src/creatures/monsters/monster.cpp b/src/creatures/monsters/monster.cpp index b689feb2f25..1f014aabc50 100644 --- a/src/creatures/monsters/monster.cpp +++ b/src/creatures/monsters/monster.cpp @@ -47,6 +47,8 @@ Monster::Monster(const std::shared_ptr &mType) : internalLight = mType->info.light; hiddenHealth = mType->info.hiddenHealth; targetDistance = mType->info.targetDistance; + attackSpells = mType->info.attackSpells; + defenseSpells = mType->info.defenseSpells; // Register creature events for (const std::string &scriptName : mType->info.scripts) { @@ -1139,7 +1141,7 @@ void Monster::doAttacking(uint32_t interval) { const Position &myPos = getPosition(); const Position &targetPos = attackedCreature->getPosition(); - for (const spellBlock_t &spellBlock : mType->info.attackSpells) { + for (const spellBlock_t &spellBlock : attackSpells) { bool inRange = false; if (spellBlock.spell == nullptr || (spellBlock.isMelee && isFleeing())) { @@ -1191,7 +1193,7 @@ bool Monster::canUseAttack(const Position &pos, const std::shared_ptr if (isHostile()) { const Position &targetPos = target->getPosition(); uint32_t distance = std::max(Position::getDistanceX(pos, targetPos), Position::getDistanceY(pos, targetPos)); - for (const spellBlock_t &spellBlock : mType->info.attackSpells) { + for (const spellBlock_t &spellBlock : attackSpells) { if (spellBlock.range != 0 && distance <= spellBlock.range) { return g_game().isSightClear(pos, targetPos, true); } @@ -1286,7 +1288,7 @@ void Monster::onThinkDefense(uint32_t interval) { bool resetTicks = true; defenseTicks += interval; - for (const spellBlock_t &spellBlock : mType->info.defenseSpells) { + for (const spellBlock_t &spellBlock : defenseSpells) { if (spellBlock.speed > defenseTicks) { resetTicks = false; continue; diff --git a/src/creatures/monsters/monster.hpp b/src/creatures/monsters/monster.hpp index c6c0ac6aa1e..3c2a60e84f4 100644 --- a/src/creatures/monsters/monster.hpp +++ b/src/creatures/monsters/monster.hpp @@ -283,6 +283,9 @@ class Monster final : public Creature { std::unordered_map m_reflectElementMap; + std::vector attackSpells; + std::vector defenseSpells; + Position masterPos; bool isWalkingBack = false; diff --git a/src/creatures/monsters/monsters.hpp b/src/creatures/monsters/monsters.hpp index 979b2ffcc01..146f53e604c 100644 --- a/src/creatures/monsters/monsters.hpp +++ b/src/creatures/monsters/monsters.hpp @@ -30,22 +30,6 @@ class Loot { class BaseSpell; struct spellBlock_t { - constexpr spellBlock_t() = default; - ~spellBlock_t() = default; - spellBlock_t(const spellBlock_t &other) = delete; - spellBlock_t &operator=(const spellBlock_t &other) = delete; - spellBlock_t(spellBlock_t &&other) noexcept : - spell(std::move(other.spell)), - chance(other.chance), - speed(other.speed), - range(other.range), - minCombatValue(other.minCombatValue), - maxCombatValue(other.maxCombatValue), - combatSpell(other.combatSpell), - isMelee(other.isMelee) { - other.spell = nullptr; - } - std::shared_ptr spell = nullptr; uint32_t chance = 100; uint32_t speed = 2000; diff --git a/src/lua/functions/core/game/game_functions.cpp b/src/lua/functions/core/game/game_functions.cpp index 8182811bd9e..7e964ba9c83 100644 --- a/src/lua/functions/core/game/game_functions.cpp +++ b/src/lua/functions/core/game/game_functions.cpp @@ -572,14 +572,6 @@ int GameFunctions::luaGameCreateSoulPitMonster(lua_State* L) { if (g_game().placeCreature(monster, position, extended, force)) { monster->setSoulPitStack(stack); monster->onSpawn(); - const auto &mtype = monster->getMonsterType(); - if (mtype && mtype->info.raceid > 0 && mtype->info.bosstiaryRace == BosstiaryRarity_t::RARITY_ARCHFOE) { - for (const auto &spectator : Spectators().find(monster->getPosition(), true)) { - if (const auto &tmpPlayer = spectator->getPlayer()) { - tmpPlayer->sendBosstiaryCooldownTimer(); - } - } - } Lua::pushUserdata(L, monster); Lua::setMetatable(L, -1, "Monster"); diff --git a/src/lua/functions/creatures/monster/monster_functions.cpp b/src/lua/functions/creatures/monster/monster_functions.cpp index e3802f02a64..7d73d7bbea9 100644 --- a/src/lua/functions/creatures/monster/monster_functions.cpp +++ b/src/lua/functions/creatures/monster/monster_functions.cpp @@ -78,6 +78,9 @@ void MonsterFunctions::init(lua_State* L) { Lua::registerMethod(L, "Monster", "criticalChance", MonsterFunctions::luaMonsterCriticalChance); Lua::registerMethod(L, "Monster", "criticalDamage", MonsterFunctions::luaMonsterCriticalDamage); + Lua::registerMethod(L, "Monster", "addAttackSpell", MonsterFunctions::luaMonsterAddAttackSpell); + Lua::registerMethod(L, "Monster", "addDefenseSpell", MonsterFunctions::luaMonsterAddDefenseSpell); + CharmFunctions::init(L); LootFunctions::init(L); MonsterSpellFunctions::init(L); @@ -828,3 +831,47 @@ int MonsterFunctions::luaMonsterCriticalDamage(lua_State* L) { } return 1; } + +int MonsterFunctions::luaMonsterAddAttackSpell(lua_State* L) { + // monster:addAttackSpell(monsterspell) + const auto &monster = Lua::getUserdataShared(L, 1); + if (monster) { + const auto &spell = Lua::getUserdataShared(L, 2); + if (spell) { + spellBlock_t sb; + const auto &monsterName = monster->getName(); + if (g_monsters().deserializeSpell(spell, sb, monsterName)) { + monster->attackSpells.push_back(std::move(sb)); + } else { + g_logger().warn("Monster: {}, cant load spell: {}", monsterName, spell->name); + } + } else { + lua_pushnil(L); + } + } else { + lua_pushnil(L); + } + return 1; +} + +int MonsterFunctions::luaMonsterAddDefenseSpell(lua_State* L) { + // monster:addDefenseSpell(monsterspell) + const auto &monster = Lua::getUserdataShared(L, 1); + if (monster) { + const auto &spell = Lua::getUserdataShared(L, 2); + if (spell) { + spellBlock_t sb; + const auto &monsterName = monster->getName(); + if (g_monsters().deserializeSpell(spell, sb, monsterName)) { + monster->defenseSpells.push_back(std::move(sb)); + } else { + g_logger().warn("Monster: {}, Cant load spell: {}", monsterName, spell->name); + } + } else { + lua_pushnil(L); + } + } else { + lua_pushnil(L); + } + return 1; +} diff --git a/src/lua/functions/creatures/monster/monster_functions.hpp b/src/lua/functions/creatures/monster/monster_functions.hpp index a49b234f39c..a7c6a5269e1 100644 --- a/src/lua/functions/creatures/monster/monster_functions.hpp +++ b/src/lua/functions/creatures/monster/monster_functions.hpp @@ -83,5 +83,8 @@ class MonsterFunctions { static int luaMonsterCriticalChance(lua_State* L); static int luaMonsterCriticalDamage(lua_State* L); + static int luaMonsterAddAttackSpell(lua_State* L); + static int luaMonsterAddDefenseSpell(lua_State* L); + friend class CreatureFunctions; }; From 6e993dbb1923f10448738f138a03d785cc054d84 Mon Sep 17 00:00:00 2001 From: Pedro Henrique Alves Cruz Date: Tue, 7 Jan 2025 18:49:14 -0300 Subject: [PATCH 39/95] fix: bestiary race and stars monsterType to export in Game instead of MonsterType --- .../functions/core/game/game_functions.cpp | 33 +++++++++++++++++++ .../functions/core/game/game_functions.hpp | 3 ++ 2 files changed, 36 insertions(+) diff --git a/src/lua/functions/core/game/game_functions.cpp b/src/lua/functions/core/game/game_functions.cpp index 7e964ba9c83..98abe7a444e 100644 --- a/src/lua/functions/core/game/game_functions.cpp +++ b/src/lua/functions/core/game/game_functions.cpp @@ -110,6 +110,9 @@ void GameFunctions::init(lua_State* L) { Lua::registerMethod(L, "Game", "getAchievements", GameFunctions::luaGameGetAchievements); Lua::registerMethod(L, "Game", "getSoulCoreItems", GameFunctions::luaGameGetSoulCoreItems); + + Lua::registerMethod(L, "Game", "getMonstersByRace", GameFunctions::luaGameGetMonstersByRace); + Lua::registerMethod(L, "Game", "getMonstersByBestiaryStars", GameFunctions::luaGameGetMonstersByBestiaryStars); } // Game @@ -1055,3 +1058,33 @@ int GameFunctions::luaGameGetSoulCoreItems(lua_State* L) { return 1; } + +int GameFunctions::luaGameGetMonstersByRace(lua_State* L) { + // Game.getMonstersByRace(race) + const BestiaryType_t race = Lua::getNumber(L, 1); + const auto monstersByRace = g_monsters().getMonstersByRace(race); + + lua_createtable(L, monstersByRace.size(), 0); + int index = 0; + for (const auto &monsterType : monstersByRace) { + Lua::pushUserdata(L, monsterType); + Lua::setMetatable(L, -1, "MonsterType"); + lua_rawseti(L, -2, ++index); + } + return 1; +} + +int GameFunctions::luaGameGetMonstersByBestiaryStars(lua_State* L) { + // Game.getMonstersByBestiaryStars(stars) + const uint8_t stars = Lua::getNumber(L, 1); + const auto monstersByStars = g_monsters().getMonstersByBestiaryStars(stars); + + lua_createtable(L, monstersByStars.size(), 0); + int index = 0; + for (const auto &monsterType : monstersByStars) { + Lua::pushUserdata(L, monsterType); + Lua::setMetatable(L, -1, "MonsterType"); + lua_rawseti(L, -2, ++index); + } + return 1; +} diff --git a/src/lua/functions/core/game/game_functions.hpp b/src/lua/functions/core/game/game_functions.hpp index e70c7c0f6f1..b24b960368c 100644 --- a/src/lua/functions/core/game/game_functions.hpp +++ b/src/lua/functions/core/game/game_functions.hpp @@ -91,4 +91,7 @@ class GameFunctions { static int luaGameGetAchievements(lua_State* L); static int luaGameGetSoulCoreItems(lua_State* L); + + static int luaGameGetMonstersByRace(lua_State* L); + static int luaGameGetMonstersByBestiaryStars(lua_State* L); }; From be7e95465647b41748d0fae2a404677e0c20dd62 Mon Sep 17 00:00:00 2001 From: Felipe Paluco Date: Thu, 28 Nov 2024 14:11:33 -0300 Subject: [PATCH 40/95] feat: add getMonstersByRace and getMonstersByBestiaryStars --- src/creatures/monsters/monsters.cpp | 28 +++++++++++++++++ src/creatures/monsters/monsters.hpp | 2 ++ .../monster/monster_type_functions.cpp | 30 +++++++++++++++++++ .../monster/monster_type_functions.hpp | 2 ++ 4 files changed, 62 insertions(+) diff --git a/src/creatures/monsters/monsters.cpp b/src/creatures/monsters/monsters.cpp index cd33e12e248..a9f5a8f3dbb 100644 --- a/src/creatures/monsters/monsters.cpp +++ b/src/creatures/monsters/monsters.cpp @@ -382,3 +382,31 @@ bool Monsters::tryAddMonsterType(const std::string &name, const std::shared_ptr< monsters[lowerName] = mType; return true; } + + +std::vector> Monsters::getMonstersByRace(BestiaryType_t race) const { + std::vector> monstersByRace; + const auto& bestiaryList = g_game().getBestiaryList(); + + for (const auto& it : bestiaryList) { + const auto monsterType = g_monsters().getMonsterType(it.second); + if (monsterType && monsterType->info.bestiaryRace == race) { + monstersByRace.emplace_back(monsterType); + } + } + return monstersByRace; +} + +std::vector> Monsters::getMonstersByBestiaryStars(uint8_t stars) const { + std::vector> monstersByStars; + const auto& bestiaryList = g_game().getBestiaryList(); + + for (const auto& it : bestiaryList) { + const auto monsterType = g_monsters().getMonsterType(it.second); + if (monsterType && monsterType->info.bestiaryStars == stars) { + monstersByStars.emplace_back(monsterType); + } + } + return monstersByStars; +} + diff --git a/src/creatures/monsters/monsters.hpp b/src/creatures/monsters/monsters.hpp index 35c4d73050d..d9a7b35975e 100644 --- a/src/creatures/monsters/monsters.hpp +++ b/src/creatures/monsters/monsters.hpp @@ -265,6 +265,8 @@ class Monsters { std::shared_ptr getMonsterTypeByRaceId(uint16_t raceId, bool isBoss = false) const; bool tryAddMonsterType(const std::string &name, const std::shared_ptr &mType); bool deserializeSpell(const std::shared_ptr &spell, spellBlock_t &sb, const std::string &description = "") const; + std::vector> Monsters::getMonstersByRace(BestiaryType_t race) const; + std::vector> Monsters::getMonstersByBestiaryStars(uint8_t stars) const; std::unique_ptr scriptInterface; std::map> monsters; diff --git a/src/lua/functions/creatures/monster/monster_type_functions.cpp b/src/lua/functions/creatures/monster/monster_type_functions.cpp index 8b2f4a5f314..e436104b308 100644 --- a/src/lua/functions/creatures/monster/monster_type_functions.cpp +++ b/src/lua/functions/creatures/monster/monster_type_functions.cpp @@ -151,6 +151,8 @@ void MonsterTypeFunctions::init(lua_State* L) { Lua::registerMethod(L, "MonsterType", "deathSound", MonsterTypeFunctions::luaMonsterTypedeathSound); Lua::registerMethod(L, "MonsterType", "variant", MonsterTypeFunctions::luaMonsterTypeVariant); + Lua::registerMethod(L, "MonsterType", "getMonstersByRace", MonsterTypeFunctions::luaMonsterTypeGetMonstersByRace); + Lua::registerMethod(L, "MonsterType", "getMonstersByBestiaryStars", MonsterTypeFunctions::luaMonsterTypeGetMonstersByBestiaryStars); } void MonsterTypeFunctions::createMonsterTypeLootLuaTable(lua_State* L, const std::vector &lootList) { @@ -1857,3 +1859,31 @@ int MonsterTypeFunctions::luaMonsterTypeVariant(lua_State* L) { return 1; } + +int MonsterTypeFunctions::luaMonsterTypeGetMonstersByRace(lua_State* L) { + // monsterType:getMonstersByRace(race) + const BestiaryType_t race = Lua::getNumber(L, 1); + const auto monstersByRace = g_monsters().getMonstersByRace(race); + + lua_createtable(L, monstersByRace.size(), 0); + int index = 0; + for (const auto& monsterType : monstersByRace) { + Lua::pushUserdata(L, monsterType); + lua_rawseti(L, -2, ++index); + } + return 1; +} + +int MonsterTypeFunctions::luaMonsterTypeGetMonstersByBestiaryStars(lua_State* L) { + // monsterType:getMonstersByBestiaryStars(stars) + const uint8_t stars = Lua::getNumber(L, 1); + const auto monstersByStars = g_monsters().getMonstersByBestiaryStars(stars); + + lua_createtable(L, monstersByStars.size(), 0); + int index = 0; + for (const auto& monsterType : monstersByStars) { + Lua::pushUserdata(L, monsterType); + lua_rawseti(L, -2, ++index); + } + return 1; +} \ No newline at end of file diff --git a/src/lua/functions/creatures/monster/monster_type_functions.hpp b/src/lua/functions/creatures/monster/monster_type_functions.hpp index e272ea45cd7..2fb8640995d 100644 --- a/src/lua/functions/creatures/monster/monster_type_functions.hpp +++ b/src/lua/functions/creatures/monster/monster_type_functions.hpp @@ -139,4 +139,6 @@ class MonsterTypeFunctions { static int luaMonsterTypeCritChance(lua_State* L); static int luaMonsterTypeVariant(lua_State* L); + static int luaMonsterTypeGetMonstersByRace(lua_State* L); + static int luaMonsterTypeGetMonstersByBestiaryStars(lua_State* L); }; From db7be76d79d4f259486afd9c30727e6057c0e659 Mon Sep 17 00:00:00 2001 From: Felipe Paluco Date: Thu, 28 Nov 2024 14:48:14 -0300 Subject: [PATCH 41/95] feat: add method to capture soulcore id from monster type --- src/creatures/monsters/monsters.hpp | 2 ++ .../creatures/monster/monster_type_functions.cpp | 16 ++++++++++++++++ .../creatures/monster/monster_type_functions.hpp | 2 ++ 3 files changed, 20 insertions(+) diff --git a/src/creatures/monsters/monsters.hpp b/src/creatures/monsters/monsters.hpp index d9a7b35975e..434046e6866 100644 --- a/src/creatures/monsters/monsters.hpp +++ b/src/creatures/monsters/monsters.hpp @@ -94,6 +94,8 @@ class MonsterType { uint32_t maxSummons = 0; uint32_t changeTargetSpeed = 0; + uint32_t soulCore = 0; + std::bitset m_conditionImmunities; std::bitset m_damageImmunities; diff --git a/src/lua/functions/creatures/monster/monster_type_functions.cpp b/src/lua/functions/creatures/monster/monster_type_functions.cpp index e436104b308..bd6bb9a94d0 100644 --- a/src/lua/functions/creatures/monster/monster_type_functions.cpp +++ b/src/lua/functions/creatures/monster/monster_type_functions.cpp @@ -660,6 +660,22 @@ int MonsterTypeFunctions::luaMonsterTypeRaceid(lua_State* L) { return 1; } +int MonsterTypeFunctions::luaMonsterTypeSoulCore(lua_State* L) { + // get: monsterType:luaMonsterTypeSoulCore() set: monsterType:luaMonsterTypeSoulCore(id) + const auto &monsterType = Lua::getUserdataShared(L, 1); + if (monsterType) { + if (lua_gettop(L) == 1) { + lua_pushnumber(L, monsterType->info.soulCore); + } else { + monsterType->info.soulCore = Lua::getNumber(L, 2); + Lua::pushBoolean(L, true); + } + } else { + lua_pushnil(L); + } + return 1; +} + int MonsterTypeFunctions::luaMonsterTypeBestiarytoKill(lua_State* L) { // get: monsterType:BestiarytoKill() set: monsterType:BestiarytoKill(value) const auto &monsterType = Lua::getUserdataShared(L, 1); diff --git a/src/lua/functions/creatures/monster/monster_type_functions.hpp b/src/lua/functions/creatures/monster/monster_type_functions.hpp index 2fb8640995d..0994edaed23 100644 --- a/src/lua/functions/creatures/monster/monster_type_functions.hpp +++ b/src/lua/functions/creatures/monster/monster_type_functions.hpp @@ -131,6 +131,8 @@ class MonsterTypeFunctions { static int luaMonsterTypeBossRace(lua_State* L); static int luaMonsterTypeBossRaceId(lua_State* L); + static int luaMonsterTypeSoulCore(lua_State* L); + static int luaMonsterTypeSoundChance(lua_State* L); static int luaMonsterTypeSoundSpeedTicks(lua_State* L); static int luaMonsterTypeAddSound(lua_State* L); From 097c7b0f59cd009a12d9e87e80d1d9697c1cc4fd Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Thu, 28 Nov 2024 17:49:00 +0000 Subject: [PATCH 42/95] Code format - (Clang-format) --- src/creatures/monsters/monsters.cpp | 10 ++++------ .../creatures/monster/monster_type_functions.cpp | 6 +++--- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/creatures/monsters/monsters.cpp b/src/creatures/monsters/monsters.cpp index a9f5a8f3dbb..1155252bc7d 100644 --- a/src/creatures/monsters/monsters.cpp +++ b/src/creatures/monsters/monsters.cpp @@ -383,12 +383,11 @@ bool Monsters::tryAddMonsterType(const std::string &name, const std::shared_ptr< return true; } - std::vector> Monsters::getMonstersByRace(BestiaryType_t race) const { std::vector> monstersByRace; - const auto& bestiaryList = g_game().getBestiaryList(); + const auto &bestiaryList = g_game().getBestiaryList(); - for (const auto& it : bestiaryList) { + for (const auto &it : bestiaryList) { const auto monsterType = g_monsters().getMonsterType(it.second); if (monsterType && monsterType->info.bestiaryRace == race) { monstersByRace.emplace_back(monsterType); @@ -399,9 +398,9 @@ std::vector> Monsters::getMonstersByRace(BestiaryTy std::vector> Monsters::getMonstersByBestiaryStars(uint8_t stars) const { std::vector> monstersByStars; - const auto& bestiaryList = g_game().getBestiaryList(); + const auto &bestiaryList = g_game().getBestiaryList(); - for (const auto& it : bestiaryList) { + for (const auto &it : bestiaryList) { const auto monsterType = g_monsters().getMonsterType(it.second); if (monsterType && monsterType->info.bestiaryStars == stars) { monstersByStars.emplace_back(monsterType); @@ -409,4 +408,3 @@ std::vector> Monsters::getMonstersByBestiaryStars(u } return monstersByStars; } - diff --git a/src/lua/functions/creatures/monster/monster_type_functions.cpp b/src/lua/functions/creatures/monster/monster_type_functions.cpp index bd6bb9a94d0..acd4286c81f 100644 --- a/src/lua/functions/creatures/monster/monster_type_functions.cpp +++ b/src/lua/functions/creatures/monster/monster_type_functions.cpp @@ -1883,7 +1883,7 @@ int MonsterTypeFunctions::luaMonsterTypeGetMonstersByRace(lua_State* L) { lua_createtable(L, monstersByRace.size(), 0); int index = 0; - for (const auto& monsterType : monstersByRace) { + for (const auto &monsterType : monstersByRace) { Lua::pushUserdata(L, monsterType); lua_rawseti(L, -2, ++index); } @@ -1897,9 +1897,9 @@ int MonsterTypeFunctions::luaMonsterTypeGetMonstersByBestiaryStars(lua_State* L) lua_createtable(L, monstersByStars.size(), 0); int index = 0; - for (const auto& monsterType : monstersByStars) { + for (const auto &monsterType : monstersByStars) { Lua::pushUserdata(L, monsterType); lua_rawseti(L, -2, ++index); } return 1; -} \ No newline at end of file +} From 824e6ad1593d6d349c897722d9e95e8679a1e2d5 Mon Sep 17 00:00:00 2001 From: Pedro Henrique Alves Cruz Date: Sun, 1 Dec 2024 18:52:42 -0300 Subject: [PATCH 43/95] improve: references and iterations --- src/creatures/monsters/monsters.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/creatures/monsters/monsters.cpp b/src/creatures/monsters/monsters.cpp index 1155252bc7d..fd269da1709 100644 --- a/src/creatures/monsters/monsters.cpp +++ b/src/creatures/monsters/monsters.cpp @@ -387,8 +387,8 @@ std::vector> Monsters::getMonstersByRace(BestiaryTy std::vector> monstersByRace; const auto &bestiaryList = g_game().getBestiaryList(); - for (const auto &it : bestiaryList) { - const auto monsterType = g_monsters().getMonsterType(it.second); + for (const auto &[raceId, name] : bestiaryList) { + const auto &monsterType = g_monsters().getMonsterType(name); if (monsterType && monsterType->info.bestiaryRace == race) { monstersByRace.emplace_back(monsterType); } @@ -400,8 +400,8 @@ std::vector> Monsters::getMonstersByBestiaryStars(u std::vector> monstersByStars; const auto &bestiaryList = g_game().getBestiaryList(); - for (const auto &it : bestiaryList) { - const auto monsterType = g_monsters().getMonsterType(it.second); + for (const auto &[raceId, name] : bestiaryList) { + const auto &monsterType = g_monsters().getMonsterType(name); if (monsterType && monsterType->info.bestiaryStars == stars) { monstersByStars.emplace_back(monsterType); } From 5f11b7641751dc298186df23527eb70f9b29dc0b Mon Sep 17 00:00:00 2001 From: Pedro Henrique Alves Cruz Date: Sun, 1 Dec 2024 19:03:03 -0300 Subject: [PATCH 44/95] fix: hpp method declaration --- src/creatures/monsters/monsters.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/creatures/monsters/monsters.hpp b/src/creatures/monsters/monsters.hpp index 434046e6866..979b2ffcc01 100644 --- a/src/creatures/monsters/monsters.hpp +++ b/src/creatures/monsters/monsters.hpp @@ -267,8 +267,8 @@ class Monsters { std::shared_ptr getMonsterTypeByRaceId(uint16_t raceId, bool isBoss = false) const; bool tryAddMonsterType(const std::string &name, const std::shared_ptr &mType); bool deserializeSpell(const std::shared_ptr &spell, spellBlock_t &sb, const std::string &description = "") const; - std::vector> Monsters::getMonstersByRace(BestiaryType_t race) const; - std::vector> Monsters::getMonstersByBestiaryStars(uint8_t stars) const; + std::vector> getMonstersByRace(BestiaryType_t race) const; + std::vector> getMonstersByBestiaryStars(uint8_t stars) const; std::unique_ptr scriptInterface; std::map> monsters; From cf4eec0e4c04c9c2e997c58252fe7bfd8cae602f Mon Sep 17 00:00:00 2001 From: Pedro Henrique Alves Cruz Date: Mon, 2 Dec 2024 01:43:19 -0300 Subject: [PATCH 45/95] feat: initiating the soulpit stages --- .../scripts/actions/soulpit/soulpit_fight.lua | 145 ++++++++++++++++++ src/creatures/creatures_definitions.hpp | 1 + src/items/functions/item/item_parse.hpp | 2 +- src/items/items.hpp | 4 + .../functions/core/game/game_functions.cpp | 24 +++ .../functions/core/game/game_functions.hpp | 2 + 6 files changed, 177 insertions(+), 1 deletion(-) create mode 100644 data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua diff --git a/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua b/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua new file mode 100644 index 00000000000..46892d6f396 --- /dev/null +++ b/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua @@ -0,0 +1,145 @@ +local config = { + soulCores = Game.getSoulCoreItems(), + requiredLevel = 8, + playerPositions = { + { pos = Position(32371, 31155, 8), teleport = Position(32373, 31138, 8), effect = CONST_ME_TELEPORT }, + { pos = Position(32371, 31156, 8), teleport = Position(32373, 31138, 8), effect = CONST_ME_TELEPORT }, + { pos = Position(32371, 31157, 8), teleport = Position(32373, 31138, 8), effect = CONST_ME_TELEPORT }, + { pos = Position(32371, 31158, 8), teleport = Position(32373, 31138, 8), effect = CONST_ME_TELEPORT }, + { pos = Position(32371, 31159, 8), teleport = Position(32373, 31138, 8), effect = CONST_ME_TELEPORT }, + }, + waves = { + [1] = { + stacks = { + [1] = 7, + } + }, + [2] = { + stacks = { + [1] = 4, + [5] = 3, + } + }, + [3] = { + stacks = { + [1] = 5, + [15] = 2, + } + }, + [4] = { + stacks = { + [1] = 3, + [5] = 3, + [40] = 1, + } + }, + }, + effects = { + [1] = CONST_ME_TELEPORT, + [5] = CONST_ME_ORANGETELEPORT, + [15] = CONST_ME_REDTELEPORT, + [40] = CONST_ME_PURPLETELEPORT, + }, + checkMonstersDelay = 4.5 * 1000, -- 4.5 seconds | The check delay should never be less than the timeToSpawnMonsters. + timeToSpawnMonsters = 4 * 1000, -- 4 seconds + totalMonsters = 7, + bossPosition = Position(32372, 31135, 8), + exit = Position(33659, 32897, 14), + zone = Zone("soulpit"), +} + +config.zone:addArea(Position(32365, 31134, 8), Position(32382, 31152, 8)) + +local soulPitAction = Action() +function soulPitAction.onUse(player, item, fromPosition, target, toPosition, isHotkey) + if not target or target:getId() ~= 49174 then + return false + end + + local isParticipant = false + for _, v in ipairs(config.playerPositions) do + if Position(v.pos) == player:getPosition() then + isParticipant = true + end + end + + if not isParticipant then + return false + end + + local lever = Lever() + lever:setPositions(config.playerPositions) + lever:setCondition(function(creature) + if not creature or not creature:isPlayer() then + return true + end + + local isAccountNormal = creature:getAccountType() < ACCOUNT_TYPE_GAMEMASTER + if isAccountNormal and creature:getLevel() < config.requiredLevel then + local message = "All players need to be level " .. config.requiredLevel .. " or higher." + creature:sendTextMessage(MESSAGE_EVENT_ADVANCE, message) + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, message) + return false + end + + local infoPositions = lever:getInfoPositions() + return true + end) + lever:checkPositions() + if lever:checkConditions() then + lever:teleportPlayers() + end + + local monsterName = string.gsub(item:getName(), " soul core", "") + + logger.warn("monster name: " .. monsterName) + + config.zone:removeMonsters() + + local encounter = Encounter("Soulpit", { + zone = config.zone, + }) + + local function waveStart() + for stack, amount in pairs(config.waves[encounter.currentStage].stacks) do + logger.warn("stack: " .. stack) + logger.warn("amount: " .. amount) + for i = 1, amount do + local position = stack ~= 40 and config.zone:randomPosition() or config.bossPosition + for i = 1, config.timeToSpawnMonsters / 1000 do + encounter:addEvent(function(position) + position:sendMagicEffect(config.effects[stack]) + end, i * 1000, position) + end + + encounter:addEvent(function(name, stack, position) + local monster = Game.createMonster(name, position) + if not monster then + return false + end + monster:setForgeStack(stack) + local icon = stack <= 15 and CreatureIconModifications_ReducedHealth or CreatureIconModifications_ReducedHealthExclamation + monster:removeIcon("forge") + monster:setIcon("soulpit", CreatureIconCategory_Modifications, icon, stack <= 15 and stack or 0) + monster:setDropLoot(false) + end, config.timeToSpawnMonsters, monsterName, stack, position) + end + end + end + + for i = 1, #config.waves do + encounter:addStage({ + start = waveStart, + }):autoAdvance({ delay = config.checkMonstersDelay, monstersKilled = true }) + end + + encounter:start() + encounter:register() + + return true +end + +for _, itemType in pairs(config.soulCores) do + soulPitAction:id(itemType:getId()) +end +soulPitAction:register() diff --git a/src/creatures/creatures_definitions.hpp b/src/creatures/creatures_definitions.hpp index dd554c086b3..4fa8b1fdfbc 100644 --- a/src/creatures/creatures_definitions.hpp +++ b/src/creatures/creatures_definitions.hpp @@ -1332,6 +1332,7 @@ enum class CreatureIconModifications_t { Influenced, Fiendish, ReducedHealth, + ReducedHealthExclamation, }; enum class CreatureIconQuests_t { diff --git a/src/items/functions/item/item_parse.hpp b/src/items/functions/item/item_parse.hpp index 6fe6fbcccd3..43a4f79433c 100644 --- a/src/items/functions/item/item_parse.hpp +++ b/src/items/functions/item/item_parse.hpp @@ -179,7 +179,7 @@ const phmap::flat_hash_map ItemTypesMap = { { "food", ITEM_TYPE_FOOD }, { "valuable", ITEM_TYPE_VALUABLE }, { "potion", ITEM_TYPE_POTION }, - + { "soulcore", ITEM_TYPE_SOULCORES }, { "ladder", ITEM_TYPE_LADDER }, { "dummy", ITEM_TYPE_DUMMY }, }; diff --git a/src/items/items.hpp b/src/items/items.hpp index b977f0bf7a9..f4eae14df27 100644 --- a/src/items/items.hpp +++ b/src/items/items.hpp @@ -415,6 +415,10 @@ class Items { return items.size(); } + std::vector &getItems() { + return items; + } + NameMap nameToItems; void addLadderId(uint16_t newId) { diff --git a/src/lua/functions/core/game/game_functions.cpp b/src/lua/functions/core/game/game_functions.cpp index eec876eee7c..c09cc64d861 100644 --- a/src/lua/functions/core/game/game_functions.cpp +++ b/src/lua/functions/core/game/game_functions.cpp @@ -107,6 +107,8 @@ void GameFunctions::init(lua_State* L) { Lua::registerMethod(L, "Game", "getSecretAchievements", GameFunctions::luaGameGetSecretAchievements); Lua::registerMethod(L, "Game", "getPublicAchievements", GameFunctions::luaGameGetPublicAchievements); Lua::registerMethod(L, "Game", "getAchievements", GameFunctions::luaGameGetAchievements); + + Lua::registerMethod(L, "Game", "getSoulCoreItems", GameFunctions::luaGameGetSoulCoreItems); } // Game @@ -994,3 +996,25 @@ int GameFunctions::luaGameGetAchievements(lua_State* L) { } return 1; } + +int GameFunctions::luaGameGetSoulCoreItems(lua_State* L) { + // Game.getSoulCoreItems() + std::vector soulCoreItems; + + for (const auto &itemType : Item::items.getItems()) { + if (itemType.m_primaryType == "SoulCores" || itemType.type == ITEM_TYPE_SOULCORES) { + soulCoreItems.emplace_back(&itemType); + } + } + + lua_createtable(L, soulCoreItems.size(), 0); + + int index = 0; + for (const auto *itemType : soulCoreItems) { + Lua::pushUserdata(L, itemType); + Lua::setMetatable(L, -1, "ItemType"); + lua_rawseti(L, -2, ++index); + } + + return 1; +} diff --git a/src/lua/functions/core/game/game_functions.hpp b/src/lua/functions/core/game/game_functions.hpp index 6d332face9f..4a9521c826d 100644 --- a/src/lua/functions/core/game/game_functions.hpp +++ b/src/lua/functions/core/game/game_functions.hpp @@ -88,4 +88,6 @@ class GameFunctions { static int luaGameGetSecretAchievements(lua_State* L); static int luaGameGetPublicAchievements(lua_State* L); static int luaGameGetAchievements(lua_State* L); + + static int luaGameGetSoulCoreItems(lua_State* L); }; From 3b2f4ed61c4701bb7bd183e3f81d1774ba9de4cc Mon Sep 17 00:00:00 2001 From: Pedro Henrique Alves Cruz Date: Mon, 2 Dec 2024 18:35:59 -0300 Subject: [PATCH 46/95] fix: soulpit fight --- .../scripts/actions/soulpit/soulpit_fight.lua | 100 +++++++++++++++++- .../spells/monster/soulpit_intensehex.lua | 26 +++++ .../spells/monster/soulpit_opressor.lua | 43 ++++++++ .../spells/monster/soulpit_powerless.lua | 26 +++++ data/scripts/lib/register_spells.lua | 26 +++++ src/creatures/combat/condition.cpp | 21 ++++ src/creatures/creatures_definitions.hpp | 1 + src/enums/player_icons.hpp | 1 + 8 files changed, 243 insertions(+), 1 deletion(-) create mode 100644 data-otservbr-global/scripts/spells/monster/soulpit_intensehex.lua create mode 100644 data-otservbr-global/scripts/spells/monster/soulpit_opressor.lua create mode 100644 data-otservbr-global/scripts/spells/monster/soulpit_powerless.lua diff --git a/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua b/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua index 46892d6f396..ef44d20029f 100644 --- a/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua +++ b/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua @@ -40,16 +40,76 @@ local config = { [15] = CONST_ME_REDTELEPORT, [40] = CONST_ME_PURPLETELEPORT, }, + bossAbilitiesIndexes = {}, + bossAbilities = { + ["enrage"] = { + monsterEvents = { + "enrageSoulPit", + }, + }, + ["opressor"] = { + spells = { + { name = "soulpit opressor", interval = 2000, chance = 25, minDamage = 0, maxDamage = 0 }, + { name = "soulpit powerless", interval = 2000, chance = 30, minDamage = 0, maxDamage = 0 }, + { name = "soulpit hexer", interval = 2000, chance = 15, minDamage = 0, maxDamage = 0 }, + }, + }, + ["overpower"] = { + playerEvents = { + "overpowerSoulPit" + }, + }, + }, checkMonstersDelay = 4.5 * 1000, -- 4.5 seconds | The check delay should never be less than the timeToSpawnMonsters. timeToSpawnMonsters = 4 * 1000, -- 4 seconds totalMonsters = 7, bossPosition = Position(32372, 31135, 8), - exit = Position(33659, 32897, 14), + exit = Position(32371, 31164, 8), zone = Zone("soulpit"), } +for name, _ in pairs(config.bossAbilities) do + table.insert(config.bossAbilitiesIndexes, name) + logger.warn(name) +end + config.zone:addArea(Position(32365, 31134, 8), Position(32382, 31152, 8)) +local enrage = CreatureEvent("enrageSoulPit") +function enrage.onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType) + if not creature or not creature:isMonster() then + return true + end + + local healthPercentage = creature:getHealth() / creature:getMaxHealth() + + if healthPercentage >= 0.5 and healthPercentage <= 0.7 then + primaryDamage = primaryDamage * 0.9 -- 10% damage reduction + secondaryDamage = secondaryDamage * 0.9 -- 10% damage reduction + elseif healthPercentage >= 0.3 and healthPercentage < 0.5 then + primaryDamage = primaryDamage * 0.75 -- 25% damage reduction + secondaryDamage = secondaryDamage * 0.75 -- 25% damage reduction + elseif healthPercentage > 0 and healthPercentage < 0.3 then + primaryDamage = primaryDamage * 0.6 -- 40% damage reduction + secondaryDamage = secondaryDamage * 0.6 -- 40% damage reduction + end + + return primaryDamage, primaryType, secondaryDamage, secondaryType +end +enrage:register() + +local overpower = CreatureEvent("overpowerSoulPit") +function overpower.onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType) + if attacker:getForgeStack() == 40 then + primaryDamage = primaryDamage * 1.1 + secondaryDamage = secondaryDamage * 1.1 + creature:getPosition():sendMagicEffect(CONST_ME_CRITICAL_DAMAGE) + end + + return primaryDamage, primaryType, secondaryDamage, secondaryType +end +overpower:register() + local soulPitAction = Action() function soulPitAction.onUse(player, item, fromPosition, target, toPosition, isHotkey) if not target or target:getId() ~= 49174 then @@ -122,6 +182,30 @@ function soulPitAction.onUse(player, item, fromPosition, target, toPosition, isH monster:removeIcon("forge") monster:setIcon("soulpit", CreatureIconCategory_Modifications, icon, stack <= 15 and stack or 0) monster:setDropLoot(false) + if stack == 40 then + local monsterType = monster:getType() + local randomAbility = bossAbilitiesIndexes[math.random(#bossAbilitiesIndexes)] + logger.warn("the random ability chosen was: " .. randomAbility) + for _, ability in pairs(config.bossAbilities[randomAbility]) do + if ability.spells then + for _, spell in pairs(ability.spells) do + monsterType:addAttack(readSpell(spell, monsterType)) + end + end + if ability.monsterEvents then + for _, name in pairs(ability.monsterEvents) do + monster:registerEvent(name) + end + end + if ability.playerEvents then + for _, name in pairs(ability.playerEvents) do + for _, players in pairs(config.zone:getPlayers()) do + player:registerEvent(name) + end + end + end + end + end end, config.timeToSpawnMonsters, monsterName, stack, position) end end @@ -133,6 +217,20 @@ function soulPitAction.onUse(player, item, fromPosition, target, toPosition, isH }):autoAdvance({ delay = config.checkMonstersDelay, monstersKilled = true }) end + function encounter:onReset(position) + for _, player in pairs(config.zone:getPlayers()) do + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, string.format("You have defeated the core of the %s soul and unlocked its animus mastery!", monsterName)) + player:teleportTo(config.exit) + for abilityName, abilities in pairs(config.bossAbilities) do + if abilities.playerEvents then + for _, eventName in pairs(abilities.playerEvents) do + player:registerEvent(eventName) + end + end + end + end + end + encounter:start() encounter:register() diff --git a/data-otservbr-global/scripts/spells/monster/soulpit_intensehex.lua b/data-otservbr-global/scripts/spells/monster/soulpit_intensehex.lua new file mode 100644 index 00000000000..da9478bb7f5 --- /dev/null +++ b/data-otservbr-global/scripts/spells/monster/soulpit_intensehex.lua @@ -0,0 +1,26 @@ +local combat = Combat() +combat:setParameter(COMBAT_PARAM_EFFECT, CONST_ME_STUN) +combat:setParameter(COMBAT_PARAM_DISTANCEEFFECT, CONST_ANI_NONE) + +local condition = Condition(CONDITION_INTENSEHEX) +condition:setParameter(CONDITION_PARAM_TICKS, 3000) +combat:addCondition(condition) + +local spell = Spell("instant") + +local zone = Zone("soulpit") + +function spell.onCastSpell(creature, var) + if creature:getForgeStack() ~= 40 or not zone:isInZone(creature:getPosition()) then + return true + end + + return combat:execute(creature, var) +end + +spell:name("soulpit intensehex") +spell:words("###940") +spell:blockWalls(true) +spell:needTarget(false) +spell:needLearn(true) +spell:register() diff --git a/data-otservbr-global/scripts/spells/monster/soulpit_opressor.lua b/data-otservbr-global/scripts/spells/monster/soulpit_opressor.lua new file mode 100644 index 00000000000..ee50ace39df --- /dev/null +++ b/data-otservbr-global/scripts/spells/monster/soulpit_opressor.lua @@ -0,0 +1,43 @@ +-- ROOT +local combatRoot = Combat() +combatRoot:setParameter(COMBAT_PARAM_EFFECT, CONST_ME_ROOTS) + +local area = createCombatArea(AREA_WAVE11) +combatRoot:setArea(area) + +local condition = Condition(CONDITION_ROOTED) +condition:setParameter(CONDITION_PARAM_TICKS, 3000) +combatRoot:addCondition(condition) + +-- FEAR + +local combatFear = Combat() +combatFear:setParameter(COMBAT_PARAM_EFFECT, CONST_ME_BLUE_GHOST) + +local condition = Condition(CONDITION_FEARED) +condition:setParameter(CONDITION_PARAM_TICKS, 3000) +combatFear:addCondition(condition) + +local spell = Spell("instant") + +local combats = { combatRoot, combatFear } + +local zone = Zone("soulpit") + +function spell.onCastSpell(creature, var) + if creature:getForgeStack() ~= 40 or not zone:isInZone(creature:getPosition()) then + return true + end + + for _, combat in pairs(combats) do + combat:execute(creature, var) + end + return true +end + +spell:name("soulpit opressor") +spell:words("###938") +spell:blockWalls(true) +spell:needTarget(false) +spell:needLearn(true) +spell:register() diff --git a/data-otservbr-global/scripts/spells/monster/soulpit_powerless.lua b/data-otservbr-global/scripts/spells/monster/soulpit_powerless.lua new file mode 100644 index 00000000000..01d71c55121 --- /dev/null +++ b/data-otservbr-global/scripts/spells/monster/soulpit_powerless.lua @@ -0,0 +1,26 @@ +local combat = Combat() +combat:setParameter(COMBAT_PARAM_EFFECT, CONST_ME_EXPLOSIONHIT) +combat:setParameter(COMBAT_PARAM_DISTANCEEFFECT, CONST_ANI_NONE) + +local condition = Condition(CONDITION_ROOTED) +condition:setParameter(CONDITION_PARAM_TICKS, 3000) +combat:addCondition(condition) + +local spell = Spell("instant") + +local zone = Zone("soulpit") + +function spell.onCastSpell(creature, var) + if creature:getForgeStack() ~= 40 or not zone:isInZone(creature:getPosition()) then + return true + end + + return combat:execute(creature, var) +end + +spell:name("soulpit powerless") +spell:words("###939") +spell:blockWalls(true) +spell:needTarget(false) +spell:needLearn(true) +spell:register() diff --git a/data/scripts/lib/register_spells.lua b/data/scripts/lib/register_spells.lua index f2e7cacee3d..3d945661ae4 100644 --- a/data/scripts/lib/register_spells.lua +++ b/data/scripts/lib/register_spells.lua @@ -399,6 +399,32 @@ CrossBeamArea3X2 = { { 0, 3, 0 }, } +AREA_FEAR_OPRESSOR = { + { 0, 1, 1, 1, 0 }, + { 1, 1, 1, 1, 1 }, + { 1, 1, 3, 1, 1 }, + { 1, 1, 1, 1, 1 }, + { 0, 1, 1, 1, 0 }, +} + +AREA_ROOT_OPRESSOR = { + { 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0 }, + { 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0 }, + { 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0 }, + { 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0 }, + { 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0 }, + { 1, 1, 1, 1, 1, 0, 0, 3, 0, 0, 1, 1, 1, 1, 1 }, + { 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0 }, + { 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0 }, + { 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0 }, + { 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0 }, + { 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 }, +} + -- The numbered-keys represents the damage values, and their table -- contains the minimum and maximum number of rounds of those damage values. RANGE = { diff --git a/src/creatures/combat/condition.cpp b/src/creatures/combat/condition.cpp index adbb31606e9..52fc43ad767 100644 --- a/src/creatures/combat/condition.cpp +++ b/src/creatures/combat/condition.cpp @@ -261,6 +261,10 @@ std::shared_ptr Condition::createCondition(ConditionId_t id, Conditio case CONDITION_MUTED: case CONDITION_CHANNELMUTEDTICKS: case CONDITION_YELLTICKS: + case CONDITION_POWERLESS: + case CONDITION_LESSERHEX: + case CONDITION_INTENSEHEX: + case CONDITION_GREATERHEX: case CONDITION_PACIFIED: return ObjectPool::allocateShared(id, type, ticks, buff, subId); @@ -464,6 +468,23 @@ std::unordered_set ConditionGeneric::getIcons() const { case CONDITION_ROOTED: icons.insert(PlayerIcon::Rooted); break; + + case CONDITION_LESSERHEX: + icons.insert(PlayerIcon::LesserHex); + break; + + case CONDITION_INTENSEHEX: + icons.insert(PlayerIcon::IntenseHex); + break; + + case CONDITION_GREATERHEX: + icons.insert(PlayerIcon::GreaterHex); + break; + + case CONDITION_POWERLESS: + icons.insert(PlayerIcon::Powerless); + break; + case CONDITION_GOSHNARTAINT: switch (subId) { case 1: diff --git a/src/creatures/creatures_definitions.hpp b/src/creatures/creatures_definitions.hpp index 4fa8b1fdfbc..eba438ba44d 100644 --- a/src/creatures/creatures_definitions.hpp +++ b/src/creatures/creatures_definitions.hpp @@ -116,6 +116,7 @@ enum ConditionType_t : uint8_t { CONDITION_GREATERHEX = 33, CONDITION_BAKRAGORE = 34, CONDITION_GOSHNARTAINT = 35, + CONDITION_POWERLESS = 36, // Need the last ever CONDITION_COUNT diff --git a/src/enums/player_icons.hpp b/src/enums/player_icons.hpp index 7878d9e5037..ecab8a5d8ce 100644 --- a/src/enums/player_icons.hpp +++ b/src/enums/player_icons.hpp @@ -42,6 +42,7 @@ enum class PlayerIcon : uint8_t { GoshnarTaint5 = 25, NewManaShield = 26, Agony = 27, + Powerless = 28, // Must always be the last Count From 26bc575bc2a70e41efce30b05b5f323d9566518e Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Mon, 2 Dec 2024 04:44:15 +0000 Subject: [PATCH 47/95] Lua code format - (Stylua) --- .../scripts/actions/soulpit/soulpit_fight.lua | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua b/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua index ef44d20029f..d389b77fc0d 100644 --- a/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua +++ b/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua @@ -12,26 +12,26 @@ local config = { [1] = { stacks = { [1] = 7, - } + }, }, [2] = { stacks = { [1] = 4, [5] = 3, - } + }, }, [3] = { stacks = { [1] = 5, [15] = 2, - } + }, }, [4] = { stacks = { [1] = 3, [5] = 3, [40] = 1, - } + }, }, }, effects = { @@ -212,9 +212,11 @@ function soulPitAction.onUse(player, item, fromPosition, target, toPosition, isH end for i = 1, #config.waves do - encounter:addStage({ - start = waveStart, - }):autoAdvance({ delay = config.checkMonstersDelay, monstersKilled = true }) + encounter + :addStage({ + start = waveStart, + }) + :autoAdvance({ delay = config.checkMonstersDelay, monstersKilled = true }) end function encounter:onReset(position) From 8713f87e82dbd3215d0d4297c49967b4479354c2 Mon Sep 17 00:00:00 2001 From: Felipe Paluco Date: Mon, 2 Dec 2024 17:39:32 -0300 Subject: [PATCH 48/95] feat: add soul core item drop feature --- .../monster/ondroploot_soul_core.lua | 52 +++++++++++++++++++ .../monster/monster_type_functions.cpp | 1 + 2 files changed, 53 insertions(+) create mode 100644 data/scripts/eventcallbacks/monster/ondroploot_soul_core.lua diff --git a/data/scripts/eventcallbacks/monster/ondroploot_soul_core.lua b/data/scripts/eventcallbacks/monster/ondroploot_soul_core.lua new file mode 100644 index 00000000000..38bbd8ea670 --- /dev/null +++ b/data/scripts/eventcallbacks/monster/ondroploot_soul_core.lua @@ -0,0 +1,52 @@ +local callback = EventCallback("MonsterOnDropLootSoulCore") +local soulCores = Game.getSoulCoreItems() + +function callback.monsterOnDropLoot(monster, corpse) + if not monster or not corpse then + return + end + local player = Player(corpse:getCorpseOwner()) + if not player or not player:canReceiveLoot() then + return + end + if monster:getMonsterForgeClassification() == FORGE_NORMAL_MONSTER then + return + end + + local soulCoreId = nil + local trySameMonsterSoulCore = math.random() <= 0.3 -- 30% of chance to drop the same monster soul core | WIP: Externalize this to a lib like libs/soulpit.lua + local mType = monster:getType() + local lootTable = {} + + if math.random() < 0.5 then -- WIP: Externalize this to a lib like libs/soulpit.lua + if trySameMonsterSoulCore then + local itemName = monster:getName():lower() .. " soul core" + soulCoreId = getItemIdByName(itemName) + end + + if not soulCoreId and not trySameMonsterSoulCore then + local race = mType:Bestiaryrace() + local monstersInCategory = mType:getMonstersByRace(race) + + if monstersInCategory and #monstersInCategory > 0 then + local randomMonster = monstersInCategory[math.random(#monstersInCategory)] + local itemName = randomMonster:name():lower() .. " soul core" + soulCoreId = getItemIdByName(itemName) + logger.info("soulcoreId: " .. soulCoreId) + end + end + + if soulCoreId then + lootTable = { + [soulCoreId] = { + count = 1, + } + } + else + return {} + end + end + corpse:addLoot(mType:generateLootRoll({}, lootTable, player)) +end + +callback:register() diff --git a/src/lua/functions/creatures/monster/monster_type_functions.cpp b/src/lua/functions/creatures/monster/monster_type_functions.cpp index acd4286c81f..e6a54dd8066 100644 --- a/src/lua/functions/creatures/monster/monster_type_functions.cpp +++ b/src/lua/functions/creatures/monster/monster_type_functions.cpp @@ -1885,6 +1885,7 @@ int MonsterTypeFunctions::luaMonsterTypeGetMonstersByRace(lua_State* L) { int index = 0; for (const auto &monsterType : monstersByRace) { Lua::pushUserdata(L, monsterType); + Lua::setMetatable(L, -1, "MonsterType"); lua_rawseti(L, -2, ++index); } return 1; From 1de736c83017073495c2613607ae66496a6fd03c Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Mon, 2 Dec 2024 20:40:09 +0000 Subject: [PATCH 49/95] Code format - (Clang-format) --- src/lua/functions/core/game/game_functions.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lua/functions/core/game/game_functions.cpp b/src/lua/functions/core/game/game_functions.cpp index c09cc64d861..51f7f26c9c2 100644 --- a/src/lua/functions/core/game/game_functions.cpp +++ b/src/lua/functions/core/game/game_functions.cpp @@ -1010,7 +1010,7 @@ int GameFunctions::luaGameGetSoulCoreItems(lua_State* L) { lua_createtable(L, soulCoreItems.size(), 0); int index = 0; - for (const auto *itemType : soulCoreItems) { + for (const auto* itemType : soulCoreItems) { Lua::pushUserdata(L, itemType); Lua::setMetatable(L, -1, "ItemType"); lua_rawseti(L, -2, ++index); From 398793be22a000eb9f9394341c20397dfbb1df63 Mon Sep 17 00:00:00 2001 From: Pedro Henrique Alves Cruz Date: Mon, 2 Dec 2024 18:41:16 -0300 Subject: [PATCH 50/95] fix: spells --- .../scripts/spells/monster/soulpit_intensehex.lua | 4 ++-- .../scripts/spells/monster/soulpit_opressor.lua | 2 +- .../scripts/spells/monster/soulpit_powerless.lua | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/data-otservbr-global/scripts/spells/monster/soulpit_intensehex.lua b/data-otservbr-global/scripts/spells/monster/soulpit_intensehex.lua index da9478bb7f5..b737cfec107 100644 --- a/data-otservbr-global/scripts/spells/monster/soulpit_intensehex.lua +++ b/data-otservbr-global/scripts/spells/monster/soulpit_intensehex.lua @@ -11,7 +11,7 @@ local spell = Spell("instant") local zone = Zone("soulpit") function spell.onCastSpell(creature, var) - if creature:getForgeStack() ~= 40 or not zone:isInZone(creature:getPosition()) then + if not zone:isInZone(creature:getPosition()) or creature:getForgeStack() ~= 40 then return true end @@ -21,6 +21,6 @@ end spell:name("soulpit intensehex") spell:words("###940") spell:blockWalls(true) -spell:needTarget(false) +spell:needTarget(true) spell:needLearn(true) spell:register() diff --git a/data-otservbr-global/scripts/spells/monster/soulpit_opressor.lua b/data-otservbr-global/scripts/spells/monster/soulpit_opressor.lua index ee50ace39df..7fda90ef77a 100644 --- a/data-otservbr-global/scripts/spells/monster/soulpit_opressor.lua +++ b/data-otservbr-global/scripts/spells/monster/soulpit_opressor.lua @@ -25,7 +25,7 @@ local combats = { combatRoot, combatFear } local zone = Zone("soulpit") function spell.onCastSpell(creature, var) - if creature:getForgeStack() ~= 40 or not zone:isInZone(creature:getPosition()) then + if not zone:isInZone(creature:getPosition()) or creature:getForgeStack() ~= 40 then return true end diff --git a/data-otservbr-global/scripts/spells/monster/soulpit_powerless.lua b/data-otservbr-global/scripts/spells/monster/soulpit_powerless.lua index 01d71c55121..516bf0c7194 100644 --- a/data-otservbr-global/scripts/spells/monster/soulpit_powerless.lua +++ b/data-otservbr-global/scripts/spells/monster/soulpit_powerless.lua @@ -11,7 +11,7 @@ local spell = Spell("instant") local zone = Zone("soulpit") function spell.onCastSpell(creature, var) - if creature:getForgeStack() ~= 40 or not zone:isInZone(creature:getPosition()) then + if not zone:isInZone(creature:getPosition()) or creature:getForgeStack() ~= 40 then return true end @@ -21,6 +21,6 @@ end spell:name("soulpit powerless") spell:words("###939") spell:blockWalls(true) -spell:needTarget(false) +spell:needTarget(true) spell:needLearn(true) spell:register() From 602c733471e9db1c141a5a66ce1ddd2624cdfb49 Mon Sep 17 00:00:00 2001 From: Pedro Henrique Alves Cruz Date: Mon, 2 Dec 2024 21:32:18 -0300 Subject: [PATCH 51/95] fix: soulpit fight --- data-otservbr-global/lib/others/load.lua | 1 + data-otservbr-global/lib/others/soulpit.lua | 73 ++++++ .../monster/humanoids/orc_warlord.lua | 3 + .../scripts/actions/soulpit/soulpit_fight.lua | 220 +++++------------- .../spells/monster/soulpit_intensehex.lua | 8 +- .../spells/monster/soulpit_opressor.lua | 16 +- .../spells/monster/soulpit_powerless.lua | 10 +- src/creatures/monsters/monster.cpp | 20 +- src/creatures/monsters/monster.hpp | 2 + .../functions/core/game/game_functions.cpp | 49 ++++ .../functions/core/game/game_functions.hpp | 1 + 11 files changed, 219 insertions(+), 184 deletions(-) create mode 100644 data-otservbr-global/lib/others/soulpit.lua diff --git a/data-otservbr-global/lib/others/load.lua b/data-otservbr-global/lib/others/load.lua index 031c8fb2026..3422819b9f8 100644 --- a/data-otservbr-global/lib/others/load.lua +++ b/data-otservbr-global/lib/others/load.lua @@ -1 +1,2 @@ dofile(DATA_DIRECTORY .. "/lib/others/dawnport.lua") +dofile(DATA_DIRECTORY .. "/lib/others/soulpit.lua") diff --git a/data-otservbr-global/lib/others/soulpit.lua b/data-otservbr-global/lib/others/soulpit.lua new file mode 100644 index 00000000000..c716e8c7817 --- /dev/null +++ b/data-otservbr-global/lib/others/soulpit.lua @@ -0,0 +1,73 @@ +SoulPit = { + encounter = nil, + soulCores = Game.getSoulCoreItems(), + requiredLevel = 8, + playerPositions = { + { pos = Position(32371, 31155, 8), teleport = Position(32373, 31138, 8), effect = CONST_ME_TELEPORT }, + { pos = Position(32371, 31156, 8), teleport = Position(32373, 31138, 8), effect = CONST_ME_TELEPORT }, + { pos = Position(32371, 31157, 8), teleport = Position(32373, 31138, 8), effect = CONST_ME_TELEPORT }, + { pos = Position(32371, 31158, 8), teleport = Position(32373, 31138, 8), effect = CONST_ME_TELEPORT }, + { pos = Position(32371, 31159, 8), teleport = Position(32373, 31138, 8), effect = CONST_ME_TELEPORT }, + }, + waves = { + [1] = { + stacks = { + [1] = 6, + [40] = 1 + }, + }, + [2] = { + stacks = { + [1] = 4, + [5] = 3, + }, + }, + [3] = { + stacks = { + [1] = 5, + [15] = 2, + }, + }, + [4] = { + stacks = { + [1] = 3, + [5] = 3, + [40] = 1, + }, + }, + }, + effects = { + [1] = CONST_ME_TELEPORT, + [5] = CONST_ME_ORANGETELEPORT, + [15] = CONST_ME_REDTELEPORT, + [40] = CONST_ME_PURPLETELEPORT, + }, + possibleAbilities = { + "overpowerSoulPit", + "enrageSoulPit", + "opressorSoulPit", + }, + bossAbilities = { + ["overpowerSoulPit"] = { + player = true, + monster = false, + }, + ["enrageSoulPit"] = { + player = false, + monster = true, + }, + ["opressorSoulPit"] = { + player = false, + monster = true, + }, + }, + timeToKick = 3 * 1000, -- 3 seconds + checkMonstersDelay = 4.5 * 1000, -- 4.5 seconds | The check delay should never be less than the timeToSpawnMonsters. + timeToSpawnMonsters = 4 * 1000, -- 4 seconds + totalMonsters = 7, + bossPosition = Position(32372, 31135, 8), + exit = Position(32371, 31164, 8), + zone = Zone("soulpit"), +} + +SoulPit.zone:addArea(Position(32365, 31134, 8), Position(32382, 31152, 8)) diff --git a/data-otservbr-global/monster/humanoids/orc_warlord.lua b/data-otservbr-global/monster/humanoids/orc_warlord.lua index db5b0e20288..5b33843d971 100644 --- a/data-otservbr-global/monster/humanoids/orc_warlord.lua +++ b/data-otservbr-global/monster/humanoids/orc_warlord.lua @@ -107,6 +107,9 @@ monster.loot = { monster.attacks = { { name = "melee", interval = 2000, chance = 100, minDamage = 0, maxDamage = -250 }, { name = "combat", interval = 2000, chance = 20, type = COMBAT_PHYSICALDAMAGE, minDamage = 0, maxDamage = -200, range = 7, shootEffect = CONST_ANI_THROWINGSTAR, target = false }, + { name = "soulpit opressor", interval = 2000, chance = 25, minDamage = 0, maxDamage = 0 }, + { name = "soulpit powerless", interval = 2000, chance = 30, minDamage = 0, maxDamage = 0 }, + { name = "soulpit intensehex", interval = 2000, chance = 15, minDamage = 0, maxDamage = 0 }, } monster.defenses = { diff --git a/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua b/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua index d389b77fc0d..09303ccbd3a 100644 --- a/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua +++ b/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua @@ -1,114 +1,24 @@ -local config = { - soulCores = Game.getSoulCoreItems(), - requiredLevel = 8, - playerPositions = { - { pos = Position(32371, 31155, 8), teleport = Position(32373, 31138, 8), effect = CONST_ME_TELEPORT }, - { pos = Position(32371, 31156, 8), teleport = Position(32373, 31138, 8), effect = CONST_ME_TELEPORT }, - { pos = Position(32371, 31157, 8), teleport = Position(32373, 31138, 8), effect = CONST_ME_TELEPORT }, - { pos = Position(32371, 31158, 8), teleport = Position(32373, 31138, 8), effect = CONST_ME_TELEPORT }, - { pos = Position(32371, 31159, 8), teleport = Position(32373, 31138, 8), effect = CONST_ME_TELEPORT }, - }, - waves = { - [1] = { - stacks = { - [1] = 7, - }, - }, - [2] = { - stacks = { - [1] = 4, - [5] = 3, - }, - }, - [3] = { - stacks = { - [1] = 5, - [15] = 2, - }, - }, - [4] = { - stacks = { - [1] = 3, - [5] = 3, - [40] = 1, - }, - }, - }, - effects = { - [1] = CONST_ME_TELEPORT, - [5] = CONST_ME_ORANGETELEPORT, - [15] = CONST_ME_REDTELEPORT, - [40] = CONST_ME_PURPLETELEPORT, - }, - bossAbilitiesIndexes = {}, - bossAbilities = { - ["enrage"] = { - monsterEvents = { - "enrageSoulPit", - }, - }, - ["opressor"] = { - spells = { - { name = "soulpit opressor", interval = 2000, chance = 25, minDamage = 0, maxDamage = 0 }, - { name = "soulpit powerless", interval = 2000, chance = 30, minDamage = 0, maxDamage = 0 }, - { name = "soulpit hexer", interval = 2000, chance = 15, minDamage = 0, maxDamage = 0 }, - }, - }, - ["overpower"] = { - playerEvents = { - "overpowerSoulPit" - }, - }, - }, - checkMonstersDelay = 4.5 * 1000, -- 4.5 seconds | The check delay should never be less than the timeToSpawnMonsters. - timeToSpawnMonsters = 4 * 1000, -- 4 seconds - totalMonsters = 7, - bossPosition = Position(32372, 31135, 8), - exit = Position(32371, 31164, 8), - zone = Zone("soulpit"), -} - -for name, _ in pairs(config.bossAbilities) do - table.insert(config.bossAbilitiesIndexes, name) - logger.warn(name) -end - -config.zone:addArea(Position(32365, 31134, 8), Position(32382, 31152, 8)) - -local enrage = CreatureEvent("enrageSoulPit") -function enrage.onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType) - if not creature or not creature:isMonster() then - return true +local zoneEvent = ZoneEvent(SoulPit.zone) +function zoneEvent.afterLeave(zone, creature) + local player = creature:getPlayer() + if not player then + return false end - local healthPercentage = creature:getHealth() / creature:getMaxHealth() - - if healthPercentage >= 0.5 and healthPercentage <= 0.7 then - primaryDamage = primaryDamage * 0.9 -- 10% damage reduction - secondaryDamage = secondaryDamage * 0.9 -- 10% damage reduction - elseif healthPercentage >= 0.3 and healthPercentage < 0.5 then - primaryDamage = primaryDamage * 0.75 -- 25% damage reduction - secondaryDamage = secondaryDamage * 0.75 -- 25% damage reduction - elseif healthPercentage > 0 and healthPercentage < 0.3 then - primaryDamage = primaryDamage * 0.6 -- 40% damage reduction - secondaryDamage = secondaryDamage * 0.6 -- 40% damage reduction + if table.empty(zone:getPlayers()) then + if SoulPit.encounter then + SoulPit.encounter:reset() + SoulPit.encounter = nil + end end - return primaryDamage, primaryType, secondaryDamage, secondaryType -end -enrage:register() - -local overpower = CreatureEvent("overpowerSoulPit") -function overpower.onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType) - if attacker:getForgeStack() == 40 then - primaryDamage = primaryDamage * 1.1 - secondaryDamage = secondaryDamage * 1.1 - creature:getPosition():sendMagicEffect(CONST_ME_CRITICAL_DAMAGE) + for abilityName, abilityInfo in pairs(SoulPit.bossAbilities) do + if abilityInfo.player then + player:unregisterEvent(abilityName) + end end - - return primaryDamage, primaryType, secondaryDamage, secondaryType end -overpower:register() +zoneEvent:register() local soulPitAction = Action() function soulPitAction.onUse(player, item, fromPosition, target, toPosition, isHotkey) @@ -117,7 +27,7 @@ function soulPitAction.onUse(player, item, fromPosition, target, toPosition, isH end local isParticipant = false - for _, v in ipairs(config.playerPositions) do + for _, v in ipairs(SoulPit.playerPositions) do if Position(v.pos) == player:getPosition() then isParticipant = true end @@ -128,17 +38,22 @@ function soulPitAction.onUse(player, item, fromPosition, target, toPosition, isH end local lever = Lever() - lever:setPositions(config.playerPositions) + lever:setPositions(SoulPit.playerPositions) lever:setCondition(function(creature) if not creature or not creature:isPlayer() then return true end + if not table.empty(SoulPit.zone:getPlayers()) then + local message = "Someone is fighting in the arena!" + creature:sendTextMessage(MESSAGE_EVENT_ADVANCE, message) + return false + end + local isAccountNormal = creature:getAccountType() < ACCOUNT_TYPE_GAMEMASTER - if isAccountNormal and creature:getLevel() < config.requiredLevel then - local message = "All players need to be level " .. config.requiredLevel .. " or higher." + if isAccountNormal and creature:getLevel() < SoulPit.requiredLevel then + local message = string.format("All players need to be level %s or higher.", SoulPit.requiredLevel) creature:sendTextMessage(MESSAGE_EVENT_ADVANCE, message) - player:sendTextMessage(MESSAGE_EVENT_ADVANCE, message) return false end @@ -154,83 +69,74 @@ function soulPitAction.onUse(player, item, fromPosition, target, toPosition, isH logger.warn("monster name: " .. monsterName) - config.zone:removeMonsters() + SoulPit.zone:removeMonsters() + + if SoulPit.encounter ~= nil then + SoulPit.encounter:reset() + end local encounter = Encounter("Soulpit", { - zone = config.zone, + zone = SoulPit.zone, }) + SoulPit.encounter = encounter + local function waveStart() - for stack, amount in pairs(config.waves[encounter.currentStage].stacks) do + for stack, amount in pairs(SoulPit.waves[encounter.currentStage].stacks) do logger.warn("stack: " .. stack) logger.warn("amount: " .. amount) for i = 1, amount do - local position = stack ~= 40 and config.zone:randomPosition() or config.bossPosition - for i = 1, config.timeToSpawnMonsters / 1000 do + local position = stack ~= 40 and SoulPit.zone:randomPosition() or SoulPit.bossPosition + for i = 1, SoulPit.timeToSpawnMonsters / 1000 do encounter:addEvent(function(position) - position:sendMagicEffect(config.effects[stack]) + position:sendMagicEffect(SoulPit.effects[stack]) end, i * 1000, position) end - encounter:addEvent(function(name, stack, position) - local monster = Game.createMonster(name, position) + local randomAbility = SoulPit.possibleAbilities[math.random(1, #SoulPit.possibleAbilities)] + local chosenBossAbility = SoulPit.bossAbilities[randomAbility] + + encounter:addEvent(function(name, stack, position, bossAbilityName, bossAbility) + local monster = Game.createSoulPitMonster(name, position, stack) if not monster then return false end - monster:setForgeStack(stack) - local icon = stack <= 15 and CreatureIconModifications_ReducedHealth or CreatureIconModifications_ReducedHealthExclamation - monster:removeIcon("forge") - monster:setIcon("soulpit", CreatureIconCategory_Modifications, icon, stack <= 15 and stack or 0) - monster:setDropLoot(false) if stack == 40 then - local monsterType = monster:getType() - local randomAbility = bossAbilitiesIndexes[math.random(#bossAbilitiesIndexes)] - logger.warn("the random ability chosen was: " .. randomAbility) - for _, ability in pairs(config.bossAbilities[randomAbility]) do - if ability.spells then - for _, spell in pairs(ability.spells) do - monsterType:addAttack(readSpell(spell, monsterType)) - end - end - if ability.monsterEvents then - for _, name in pairs(ability.monsterEvents) do - monster:registerEvent(name) - end - end - if ability.playerEvents then - for _, name in pairs(ability.playerEvents) do - for _, players in pairs(config.zone:getPlayers()) do - player:registerEvent(name) - end - end - end + logger.warn("ability name: {}", bossAbilityName) + if bossAbility.monster then + monster:registerEvent(bossAbilityName) + end + if bossAbility.player then + player:registerEvent(bossAbilityName) end end - end, config.timeToSpawnMonsters, monsterName, stack, position) + end, SoulPit.timeToSpawnMonsters, monsterName, stack, position, randomAbility, chosenBossAbility) end end end - for i = 1, #config.waves do + for i = 1, #SoulPit.waves do encounter :addStage({ start = waveStart, }) - :autoAdvance({ delay = config.checkMonstersDelay, monstersKilled = true }) + :autoAdvance({ delay = SoulPit.checkMonstersDelay, monstersKilled = true }) end function encounter:onReset(position) - for _, player in pairs(config.zone:getPlayers()) do + SoulPit.zone:removeMonsters() + + for _, player in pairs(SoulPit.zone:getPlayers()) do player:sendTextMessage(MESSAGE_EVENT_ADVANCE, string.format("You have defeated the core of the %s soul and unlocked its animus mastery!", monsterName)) - player:teleportTo(config.exit) - for abilityName, abilities in pairs(config.bossAbilities) do - if abilities.playerEvents then - for _, eventName in pairs(abilities.playerEvents) do - player:registerEvent(eventName) - end - end - end + -- Add the monster animus mastery for the player. end + + addEvent(function() + SoulPit.encounter = nil + for _, player in pairs(SoulPit.zone:getPlayers()) do + player:teleportTo(SoulPit.exit) + end + end, SoulPit.timeToKick) end encounter:start() @@ -239,7 +145,7 @@ function soulPitAction.onUse(player, item, fromPosition, target, toPosition, isH return true end -for _, itemType in pairs(config.soulCores) do +for _, itemType in pairs(SoulPit.soulCores) do soulPitAction:id(itemType:getId()) end soulPitAction:register() diff --git a/data-otservbr-global/scripts/spells/monster/soulpit_intensehex.lua b/data-otservbr-global/scripts/spells/monster/soulpit_intensehex.lua index b737cfec107..b9ce99fdd89 100644 --- a/data-otservbr-global/scripts/spells/monster/soulpit_intensehex.lua +++ b/data-otservbr-global/scripts/spells/monster/soulpit_intensehex.lua @@ -8,14 +8,12 @@ combat:addCondition(condition) local spell = Spell("instant") -local zone = Zone("soulpit") - function spell.onCastSpell(creature, var) - if not zone:isInZone(creature:getPosition()) or creature:getForgeStack() ~= 40 then - return true + if table.contains(creature:getEvents(CREATURE_EVENT_THINK), "opressorSoulPit") then + return combat:execute(creature, var) end - return combat:execute(creature, var) + return true end spell:name("soulpit intensehex") diff --git a/data-otservbr-global/scripts/spells/monster/soulpit_opressor.lua b/data-otservbr-global/scripts/spells/monster/soulpit_opressor.lua index 7fda90ef77a..afe2dae554e 100644 --- a/data-otservbr-global/scripts/spells/monster/soulpit_opressor.lua +++ b/data-otservbr-global/scripts/spells/monster/soulpit_opressor.lua @@ -2,7 +2,7 @@ local combatRoot = Combat() combatRoot:setParameter(COMBAT_PARAM_EFFECT, CONST_ME_ROOTS) -local area = createCombatArea(AREA_WAVE11) +local area = createCombatArea(AREA_ROOT_OPRESSOR) combatRoot:setArea(area) local condition = Condition(CONDITION_ROOTED) @@ -14,6 +14,9 @@ combatRoot:addCondition(condition) local combatFear = Combat() combatFear:setParameter(COMBAT_PARAM_EFFECT, CONST_ME_BLUE_GHOST) +local area = createCombatArea(AREA_FEAR_OPRESSOR) +combatRoot:setArea(area) + local condition = Condition(CONDITION_FEARED) condition:setParameter(CONDITION_PARAM_TICKS, 3000) combatFear:addCondition(condition) @@ -22,16 +25,13 @@ local spell = Spell("instant") local combats = { combatRoot, combatFear } -local zone = Zone("soulpit") - function spell.onCastSpell(creature, var) - if not zone:isInZone(creature:getPosition()) or creature:getForgeStack() ~= 40 then - return true + if table.contains(creature:getEvents(CREATURE_EVENT_THINK), "opressorSoulPit") then + for _, combat in pairs(combats) do + combat:execute(creature, var) + end end - for _, combat in pairs(combats) do - combat:execute(creature, var) - end return true end diff --git a/data-otservbr-global/scripts/spells/monster/soulpit_powerless.lua b/data-otservbr-global/scripts/spells/monster/soulpit_powerless.lua index 516bf0c7194..2edbe0b6d8b 100644 --- a/data-otservbr-global/scripts/spells/monster/soulpit_powerless.lua +++ b/data-otservbr-global/scripts/spells/monster/soulpit_powerless.lua @@ -2,20 +2,18 @@ local combat = Combat() combat:setParameter(COMBAT_PARAM_EFFECT, CONST_ME_EXPLOSIONHIT) combat:setParameter(COMBAT_PARAM_DISTANCEEFFECT, CONST_ANI_NONE) -local condition = Condition(CONDITION_ROOTED) +local condition = Condition(CONDITION_POWERLESS) condition:setParameter(CONDITION_PARAM_TICKS, 3000) combat:addCondition(condition) local spell = Spell("instant") -local zone = Zone("soulpit") - function spell.onCastSpell(creature, var) - if not zone:isInZone(creature:getPosition()) or creature:getForgeStack() ~= 40 then - return true + if table.contains(creature:getEvents(CREATURE_EVENT_THINK), "opressorSoulPit") then + return combat:execute(creature, var) end - return combat:execute(creature, var) + return true end spell:name("soulpit powerless") diff --git a/src/creatures/monsters/monster.cpp b/src/creatures/monsters/monster.cpp index 845544d61e9..7588c07afa7 100644 --- a/src/creatures/monsters/monster.cpp +++ b/src/creatures/monsters/monster.cpp @@ -687,7 +687,8 @@ bool Monster::isOpponent(const std::shared_ptr &creature) const { } uint64_t Monster::getLostExperience() const { - return skillLoss ? mType->info.experience : 0; + float extraExperience = forgeStack <= 15 ? (forgeStack + 10) / 10 : 28; + return skillLoss ? static_cast(std::round(mType->info.experience * extraExperience)) : 0; } uint16_t Monster::getLookCorpse() const { @@ -2508,6 +2509,15 @@ void Monster::getPathSearchParams(const std::shared_ptr &creature, Fin } } +void Monster::applyStacks() { + // Change health based in stacks + const auto percentToIncrement = 1 + (15 * forgeStack + 35) / 100.f; + auto newHealth = static_cast(std::ceil(static_cast(healthMax) * percentToIncrement)); + + healthMax = newHealth; + health = newHealth; +} + void Monster::configureForgeSystem() { if (!canBeForgeMonster()) { return; @@ -2524,13 +2534,6 @@ void Monster::configureForgeSystem() { g_game().updateCreatureIcon(static_self_cast()); } - // Change health based in stacks - const auto percentToIncrement = 1 + (15 * forgeStack + 35) / 100.f; - auto newHealth = static_cast(std::ceil(static_cast(healthMax) * percentToIncrement)); - - healthMax = newHealth; - health = newHealth; - // Event to give Dusts const std::string &Eventname = "ForgeSystemMonster"; registerCreatureEvent(Eventname); @@ -2556,6 +2559,7 @@ uint16_t Monster::getForgeStack() const { void Monster::setForgeStack(uint16_t stack) { forgeStack = stack; + applyStacks(); } ForgeClassifications_t Monster::getMonsterForgeClassification() const { diff --git a/src/creatures/monsters/monster.hpp b/src/creatures/monsters/monster.hpp index 3661eea9bef..89e330dd750 100644 --- a/src/creatures/monsters/monster.hpp +++ b/src/creatures/monsters/monster.hpp @@ -187,6 +187,8 @@ class Monster final : public Creature { static uint32_t monsterAutoID; + void applyStacks(); + void configureForgeSystem(); bool canBeForgeMonster() const; diff --git a/src/lua/functions/core/game/game_functions.cpp b/src/lua/functions/core/game/game_functions.cpp index 51f7f26c9c2..de25dee29ec 100644 --- a/src/lua/functions/core/game/game_functions.cpp +++ b/src/lua/functions/core/game/game_functions.cpp @@ -66,6 +66,7 @@ void GameFunctions::init(lua_State* L) { Lua::registerMethod(L, "Game", "createItem", GameFunctions::luaGameCreateItem); Lua::registerMethod(L, "Game", "createContainer", GameFunctions::luaGameCreateContainer); Lua::registerMethod(L, "Game", "createMonster", GameFunctions::luaGameCreateMonster); + Lua::registerMethod(L, "Game", "createSoulPitMonster", GameFunctions::luaGameCreateSoulPitMonster); Lua::registerMethod(L, "Game", "createNpc", GameFunctions::luaGameCreateNpc); Lua::registerMethod(L, "Game", "generateNpc", GameFunctions::luaGameGenerateNpc); Lua::registerMethod(L, "Game", "createTile", GameFunctions::luaGameCreateTile); @@ -548,6 +549,54 @@ int GameFunctions::luaGameCreateMonster(lua_State* L) { return 1; } +int GameFunctions::luaGameCreateSoulPitMonster(lua_State* L) { + // Game.createSoulPitMonster(monsterName, position, [stack = 1, [, extended = false[, force = false[, master = nil]]]]) + const auto &monster = Monster::createMonster(Lua::getString(L, 1)); + if (!monster) { + lua_pushnil(L); + return 1; + } + + bool isSummon = false; + if (lua_gettop(L) >= 6) { + if (const auto &master = Lua::getCreature(L, 6)) { + monster->setMaster(master, true); + isSummon = true; + } + } + + const Position &position = Lua::getPosition(L, 2); + const uint8_t stack = Lua::getNumber(L, 3, 1); + const bool extended = Lua::getBoolean(L, 4, false); + const bool force = Lua::getBoolean(L, 5, false); + const CreatureIconModifications_t icon = stack < 40 ? CreatureIconModifications_t::ReducedHealth : CreatureIconModifications_t::ReducedHealthExclamation; + if (g_game().placeCreature(monster, position, extended, force)) { + monster->setSkillLoss(stack == 40); + monster->setForgeStack(stack); + monster->setDropLoot(false); + monster->setIcon("soulpit", CreatureIcon(icon, stack < 40 ? stack : 0)); + monster->onSpawn(); + const auto &mtype = monster->getMonsterType(); + if (mtype && mtype->info.raceid > 0 && mtype->info.bosstiaryRace == BosstiaryRarity_t::RARITY_ARCHFOE) { + for (const auto &spectator : Spectators().find(monster->getPosition(), true)) { + if (const auto &tmpPlayer = spectator->getPlayer()) { + tmpPlayer->sendBosstiaryCooldownTimer(); + } + } + } + + Lua::pushUserdata(L, monster); + Lua::setMetatable(L, -1, "Monster"); + } else { + if (isSummon) { + monster->setMaster(nullptr); + } else { + } + lua_pushnil(L); + } + return 1; +} + int GameFunctions::luaGameGenerateNpc(lua_State* L) { // Game.generateNpc(npcName) const auto &npc = Npc::createNpc(Lua::getString(L, 1)); diff --git a/src/lua/functions/core/game/game_functions.hpp b/src/lua/functions/core/game/game_functions.hpp index 4a9521c826d..e70c7c0f6f1 100644 --- a/src/lua/functions/core/game/game_functions.hpp +++ b/src/lua/functions/core/game/game_functions.hpp @@ -46,6 +46,7 @@ class GameFunctions { static int luaGameCreateItem(lua_State* L); static int luaGameCreateContainer(lua_State* L); static int luaGameCreateMonster(lua_State* L); + static int luaGameCreateSoulPitMonster(lua_State* L); static int luaGameGenerateNpc(lua_State* L); static int luaGameCreateNpc(lua_State* L); static int luaGameCreateTile(lua_State* L); From fe97ee57c86c622d565eac00826317ad12d05f7c Mon Sep 17 00:00:00 2001 From: Pedro Henrique Alves Cruz Date: Mon, 2 Dec 2024 23:57:00 -0300 Subject: [PATCH 52/95] fix: conditions and spells --- data-otservbr-global/lib/others/soulpit.lua | 1 + .../scripts/actions/soulpit/soulpit_fight.lua | 35 ++++++++++--------- .../spells/monster/soulpit_opressor.lua | 2 +- src/creatures/combat/combat.cpp | 4 +++ src/creatures/combat/condition.cpp | 28 +++++++++++++-- src/creatures/combat/spells.cpp | 10 +++++- src/creatures/combat/spells.hpp | 2 +- src/creatures/creatures_definitions.hpp | 1 + 8 files changed, 61 insertions(+), 22 deletions(-) diff --git a/data-otservbr-global/lib/others/soulpit.lua b/data-otservbr-global/lib/others/soulpit.lua index c716e8c7817..e3da921ef9a 100644 --- a/data-otservbr-global/lib/others/soulpit.lua +++ b/data-otservbr-global/lib/others/soulpit.lua @@ -1,5 +1,6 @@ SoulPit = { encounter = nil, + kickEvent = nil, soulCores = Game.getSoulCoreItems(), requiredLevel = 8, playerPositions = { diff --git a/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua b/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua index 09303ccbd3a..9abcc28e8e9 100644 --- a/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua +++ b/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua @@ -10,6 +10,9 @@ function zoneEvent.afterLeave(zone, creature) SoulPit.encounter:reset() SoulPit.encounter = nil end + if SoulPit.kickEvent then + stopEvent(SoulPit.kickEvent) + end end for abilityName, abilityInfo in pairs(SoulPit.bossAbilities) do @@ -79,6 +82,22 @@ function soulPitAction.onUse(player, item, fromPosition, target, toPosition, isH zone = SoulPit.zone, }) + function encounter:onReset(position) + SoulPit.zone:removeMonsters() + + for _, player in pairs(SoulPit.zone:getPlayers()) do + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, string.format("You have defeated the core of the %s soul and unlocked its animus mastery!", monsterName)) + -- Add the monster animus mastery for the player. + end + + SoulPit.kickEvent = addEvent(function() + SoulPit.encounter = nil + for _, player in pairs(SoulPit.zone:getPlayers()) do + player:teleportTo(SoulPit.exit) + end + end, SoulPit.timeToKick) + end + SoulPit.encounter = encounter local function waveStart() @@ -123,22 +142,6 @@ function soulPitAction.onUse(player, item, fromPosition, target, toPosition, isH :autoAdvance({ delay = SoulPit.checkMonstersDelay, monstersKilled = true }) end - function encounter:onReset(position) - SoulPit.zone:removeMonsters() - - for _, player in pairs(SoulPit.zone:getPlayers()) do - player:sendTextMessage(MESSAGE_EVENT_ADVANCE, string.format("You have defeated the core of the %s soul and unlocked its animus mastery!", monsterName)) - -- Add the monster animus mastery for the player. - end - - addEvent(function() - SoulPit.encounter = nil - for _, player in pairs(SoulPit.zone:getPlayers()) do - player:teleportTo(SoulPit.exit) - end - end, SoulPit.timeToKick) - end - encounter:start() encounter:register() diff --git a/data-otservbr-global/scripts/spells/monster/soulpit_opressor.lua b/data-otservbr-global/scripts/spells/monster/soulpit_opressor.lua index afe2dae554e..bfbadc8a714 100644 --- a/data-otservbr-global/scripts/spells/monster/soulpit_opressor.lua +++ b/data-otservbr-global/scripts/spells/monster/soulpit_opressor.lua @@ -15,7 +15,7 @@ local combatFear = Combat() combatFear:setParameter(COMBAT_PARAM_EFFECT, CONST_ME_BLUE_GHOST) local area = createCombatArea(AREA_FEAR_OPRESSOR) -combatRoot:setArea(area) +combatFear:setArea(area) local condition = Condition(CONDITION_FEARED) condition:setParameter(CONDITION_PARAM_TICKS, 3000) diff --git a/src/creatures/combat/combat.cpp b/src/creatures/combat/combat.cpp index 6b7667a71a5..a70ef43a63a 100644 --- a/src/creatures/combat/combat.cpp +++ b/src/creatures/combat/combat.cpp @@ -641,6 +641,10 @@ void Combat::CombatHealthFunc(const std::shared_ptr &caster, const std } } + if (targetPlayer && damage.primary.type == COMBAT_HEALING) { + damage.primary.value *= targetPlayer->getBuff(BUFF_HEALINGRECEIVED) / 100.; + } + damage.damageMultiplier += attackerPlayer->wheel()->getMajorStatConditional("Divine Empowerment", WheelMajor_t::DAMAGE); g_logger().trace("Wheel Divine Empowerment damage multiplier {}", damage.damageMultiplier); } diff --git a/src/creatures/combat/condition.cpp b/src/creatures/combat/condition.cpp index 52fc43ad767..fa578765d5c 100644 --- a/src/creatures/combat/condition.cpp +++ b/src/creatures/combat/condition.cpp @@ -207,6 +207,8 @@ bool Condition::executeCondition(const std::shared_ptr &creature, int3 } std::shared_ptr Condition::createCondition(ConditionId_t id, ConditionType_t type, int32_t ticks, int32_t param /* = 0*/, bool buff /* = false*/, uint32_t subId /* = 0*/, bool isPersistent /* = false*/) { + + std::shared_ptr conditionAttributes = nullptr; switch (type) { case CONDITION_POISON: case CONDITION_FIRE: @@ -252,6 +254,24 @@ std::shared_ptr Condition::createCondition(ConditionId_t id, Conditio case CONDITION_FEARED: return ObjectPool::allocateShared(id, type, ticks, buff, subId); + case CONDITION_LESSERHEX: + conditionAttributes = std::make_shared(id, type, ticks, buff, subId); + conditionAttributes->setParam(CONDITION_PARAM_BUFF_HEALINGRECEIVED, 50); + return conditionAttributes; + + case CONDITION_INTENSEHEX: + conditionAttributes = std::make_shared(id, type, ticks, buff, subId); + conditionAttributes->setParam(CONDITION_PARAM_BUFF_HEALINGRECEIVED, 50); + conditionAttributes->setParam(CONDITION_PARAM_BUFF_DAMAGEDEALT, 50); + return conditionAttributes; + + case CONDITION_GREATERHEX: + conditionAttributes = std::make_shared(id, type, ticks, buff, subId); + conditionAttributes->setParam(CONDITION_PARAM_BUFF_HEALINGRECEIVED, 50); + conditionAttributes->setParam(CONDITION_PARAM_BUFF_DAMAGEDEALT, 50); + conditionAttributes->setParam(CONDITION_PARAM_STAT_MAXHITPOINTSPERCENT, 60); + return conditionAttributes; + case CONDITION_ROOTED: case CONDITION_INFIGHT: case CONDITION_DRUNK: @@ -262,9 +282,6 @@ std::shared_ptr Condition::createCondition(ConditionId_t id, Conditio case CONDITION_CHANNELMUTEDTICKS: case CONDITION_YELLTICKS: case CONDITION_POWERLESS: - case CONDITION_LESSERHEX: - case CONDITION_INTENSEHEX: - case CONDITION_GREATERHEX: case CONDITION_PACIFIED: return ObjectPool::allocateShared(id, type, ticks, buff, subId); @@ -1025,6 +1042,11 @@ bool ConditionAttributes::setParam(ConditionParam_t param, int32_t value) { return true; } + case CONDITION_PARAM_BUFF_HEALINGRECEIVED: { + buffsPercent[BUFF_HEALINGRECEIVED] = std::max(0, value); + return true; + } + case CONDITION_PARAM_BUFF_DAMAGEDEALT: { buffsPercent[BUFF_DAMAGEDEALT] = std::max(0, value); return true; diff --git a/src/creatures/combat/spells.cpp b/src/creatures/combat/spells.cpp index f522eac86a6..8365428ef94 100644 --- a/src/creatures/combat/spells.cpp +++ b/src/creatures/combat/spells.cpp @@ -1053,11 +1053,15 @@ void Spell::setLockedPZ(bool b) { InstantSpell::InstantSpell() = default; -bool InstantSpell::playerCastInstant(const std::shared_ptr &player, std::string ¶m) const { +bool InstantSpell::playerCastInstant(const std::shared_ptr &player, std::string ¶m) { if (!playerSpellCheck(player)) { return false; } + if (player->hasCondition(CONDITION_POWERLESS) && getGroup() == SPELLGROUP_ATTACK) { + return false; + } + LuaVariant var; var.instantName = getName(); std::shared_ptr playerTarget = nullptr; @@ -1379,6 +1383,10 @@ bool RuneSpell::executeUse(const std::shared_ptr &player, const std::sha return false; } + if (player->hasCondition(CONDITION_POWERLESS) && getGroup() == SPELLGROUP_ATTACK) { + return false; + } + LuaVariant var; var.runeName = getName(); diff --git a/src/creatures/combat/spells.hpp b/src/creatures/combat/spells.hpp index 56ff1a330de..0dc9fa72efa 100644 --- a/src/creatures/combat/spells.hpp +++ b/src/creatures/combat/spells.hpp @@ -280,7 +280,7 @@ class Spell : public BaseSpell { class InstantSpell final : public Spell { public: InstantSpell(); - bool playerCastInstant(const std::shared_ptr &player, std::string ¶m) const; + bool playerCastInstant(const std::shared_ptr &player, std::string ¶m); bool castSpell(const std::shared_ptr &creature) override; bool castSpell(const std::shared_ptr &creature, const std::shared_ptr &target) override; diff --git a/src/creatures/creatures_definitions.hpp b/src/creatures/creatures_definitions.hpp index eba438ba44d..017a8db7c62 100644 --- a/src/creatures/creatures_definitions.hpp +++ b/src/creatures/creatures_definitions.hpp @@ -223,6 +223,7 @@ enum ConditionParam_t { CONDITION_PARAM_INCREASE_MANADRAINPERCENT = 80, CONDITION_PARAM_INCREASE_DROWNPERCENT = 81, CONDITION_PARAM_CHARM_CHANCE_MODIFIER = 82, + CONDITION_PARAM_BUFF_HEALINGRECEIVED = 83, }; enum stats_t { From e3b43545c70e0b84efe5ba8dbfa80451794f6939 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Tue, 3 Dec 2024 00:33:12 +0000 Subject: [PATCH 53/95] Lua code format - (Stylua) --- data-otservbr-global/lib/others/soulpit.lua | 2 +- data/scripts/eventcallbacks/monster/ondroploot_soul_core.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/data-otservbr-global/lib/others/soulpit.lua b/data-otservbr-global/lib/others/soulpit.lua index e3da921ef9a..16c1e739872 100644 --- a/data-otservbr-global/lib/others/soulpit.lua +++ b/data-otservbr-global/lib/others/soulpit.lua @@ -14,7 +14,7 @@ SoulPit = { [1] = { stacks = { [1] = 6, - [40] = 1 + [40] = 1, }, }, [2] = { diff --git a/data/scripts/eventcallbacks/monster/ondroploot_soul_core.lua b/data/scripts/eventcallbacks/monster/ondroploot_soul_core.lua index 38bbd8ea670..41678f28c0e 100644 --- a/data/scripts/eventcallbacks/monster/ondroploot_soul_core.lua +++ b/data/scripts/eventcallbacks/monster/ondroploot_soul_core.lua @@ -40,7 +40,7 @@ function callback.monsterOnDropLoot(monster, corpse) lootTable = { [soulCoreId] = { count = 1, - } + }, } else return {} From 9c99d3525a933d14c07f78aa6edd341020567415 Mon Sep 17 00:00:00 2001 From: Pedro Henrique Alves Cruz Date: Tue, 3 Dec 2024 20:46:11 -0300 Subject: [PATCH 54/95] improve: monsters soulpit creation --- data-otservbr-global/lib/others/soulpit.lua | 6 ++-- .../scripts/actions/soulpit/soulpit_fight.lua | 6 +++- src/creatures/monsters/monster.cpp | 32 +++++++++++++++---- src/creatures/monsters/monster.hpp | 6 ++++ .../functions/core/game/game_functions.cpp | 6 +--- 5 files changed, 42 insertions(+), 14 deletions(-) diff --git a/data-otservbr-global/lib/others/soulpit.lua b/data-otservbr-global/lib/others/soulpit.lua index 16c1e739872..3f17178354a 100644 --- a/data-otservbr-global/lib/others/soulpit.lua +++ b/data-otservbr-global/lib/others/soulpit.lua @@ -13,8 +13,7 @@ SoulPit = { waves = { [1] = { stacks = { - [1] = 6, - [40] = 1, + [1] = 7, }, }, [2] = { @@ -66,6 +65,9 @@ SoulPit = { checkMonstersDelay = 4.5 * 1000, -- 4.5 seconds | The check delay should never be less than the timeToSpawnMonsters. timeToSpawnMonsters = 4 * 1000, -- 4 seconds totalMonsters = 7, + obeliskActive = 49175, + obeliskInactive = 49174, + obeliskPosition = Position(32371, 31154, 8), bossPosition = Position(32372, 31135, 8), exit = Position(32371, 31164, 8), zone = Zone("soulpit"), diff --git a/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua b/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua index 9abcc28e8e9..0a803e4ec29 100644 --- a/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua +++ b/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua @@ -12,6 +12,7 @@ function zoneEvent.afterLeave(zone, creature) end if SoulPit.kickEvent then stopEvent(SoulPit.kickEvent) + SoulPit.obeliskPosition:transformItem(SoulPit.obeliskActive, SoulPit.obeliskInactive) end end @@ -25,7 +26,8 @@ zoneEvent:register() local soulPitAction = Action() function soulPitAction.onUse(player, item, fromPosition, target, toPosition, isHotkey) - if not target or target:getId() ~= 49174 then + logger.warn(item:getName()) + if not target or target:getId() ~= SoulPit.obeliskInactive then return false end @@ -66,6 +68,7 @@ function soulPitAction.onUse(player, item, fromPosition, target, toPosition, isH lever:checkPositions() if lever:checkConditions() then lever:teleportPlayers() + SoulPit.obeliskPosition:transformItem(SoulPit.obeliskInactive, SoulPit.obeliskActive) end local monsterName = string.gsub(item:getName(), " soul core", "") @@ -95,6 +98,7 @@ function soulPitAction.onUse(player, item, fromPosition, target, toPosition, isH for _, player in pairs(SoulPit.zone:getPlayers()) do player:teleportTo(SoulPit.exit) end + SoulPit.obeliskPosition:transformItem(SoulPit.obeliskActive, SoulPit.obeliskInactive) end, SoulPit.timeToKick) end diff --git a/src/creatures/monsters/monster.cpp b/src/creatures/monsters/monster.cpp index 7588c07afa7..51e2f6ff10c 100644 --- a/src/creatures/monsters/monster.cpp +++ b/src/creatures/monsters/monster.cpp @@ -1320,13 +1320,15 @@ void Monster::onThinkDefense(uint32_t interval) { } const auto &summon = Monster::createMonster(summonName); - if (summon) { - if (g_game().placeCreature(summon, getPosition(), false, summonForce)) { - summon->setMaster(static_self_cast(), true); - g_game().addMagicEffect(getPosition(), CONST_ME_MAGIC_BLUE); - g_game().addMagicEffect(summon->getPosition(), CONST_ME_TELEPORT); - g_game().sendSingleSoundEffect(summon->getPosition(), SoundEffect_t::MONSTER_SPELL_SUMMON, getMonster()); + if (summon && g_game().placeCreature(summon, getPosition(), false, summonForce)) { + if (getSoulPit()) { + const auto stack = getForgeStack(); + summon->setSoulPitStack(stack, true); } + summon->setMaster(static_self_cast(), true); + g_game().addMagicEffect(getPosition(), CONST_ME_MAGIC_BLUE); + g_game().addMagicEffect(summon->getPosition(), CONST_ME_TELEPORT); + g_game().sendSingleSoundEffect(summon->getPosition(), SoundEffect_t::MONSTER_SPELL_SUMMON, getMonster()); } } } @@ -2185,6 +2187,24 @@ void Monster::setHazardSystemDefenseBoost(bool value) { hazardDefenseBoost = value; } +bool Monster::getSoulPit() const { + return soulPit; +} + +void Monster::setSoulPit(bool value) { + soulPit = value; +} + +void Monster::setSoulPitStack(uint8_t stack, bool isSummon /* = false */) { + const bool isBoss = stack == 40; + const CreatureIconModifications_t icon = isBoss ? CreatureIconModifications_t::ReducedHealthExclamation : CreatureIconModifications_t::ReducedHealth; + setForgeStack(stack); + setIcon("soulpit", CreatureIcon(icon, isBoss ? 0 : stack)); + setSoulPit(true); + setDropLoot(false); + setSkillLoss(isBoss && !isSummon); +} + bool Monster::canWalkTo(Position pos, Direction moveDirection) { pos = getNextPosition(moveDirection, pos); if (isInSpawnRange(pos)) { diff --git a/src/creatures/monsters/monster.hpp b/src/creatures/monsters/monster.hpp index 89e330dd750..40214c92e83 100644 --- a/src/creatures/monsters/monster.hpp +++ b/src/creatures/monsters/monster.hpp @@ -179,6 +179,10 @@ class Monster final : public Creature { void setHazardSystemDefenseBoost(bool value); // Hazard end + bool getSoulPit() const; + void setSoulPit(bool value); + void setSoulPitStack(uint8_t stack, bool isSummon = false); + void updateTargetList(); void clearTargetList(); void clearFriendList(); @@ -292,6 +296,8 @@ class Monster final : public Creature { bool hazardDamageBoost = false; bool hazardDefenseBoost = false; + bool soulPit = false; + bool m_isDead = false; bool m_isImmune = false; diff --git a/src/lua/functions/core/game/game_functions.cpp b/src/lua/functions/core/game/game_functions.cpp index de25dee29ec..8619ff814f6 100644 --- a/src/lua/functions/core/game/game_functions.cpp +++ b/src/lua/functions/core/game/game_functions.cpp @@ -569,12 +569,8 @@ int GameFunctions::luaGameCreateSoulPitMonster(lua_State* L) { const uint8_t stack = Lua::getNumber(L, 3, 1); const bool extended = Lua::getBoolean(L, 4, false); const bool force = Lua::getBoolean(L, 5, false); - const CreatureIconModifications_t icon = stack < 40 ? CreatureIconModifications_t::ReducedHealth : CreatureIconModifications_t::ReducedHealthExclamation; if (g_game().placeCreature(monster, position, extended, force)) { - monster->setSkillLoss(stack == 40); - monster->setForgeStack(stack); - monster->setDropLoot(false); - monster->setIcon("soulpit", CreatureIcon(icon, stack < 40 ? stack : 0)); + monster->setSoulPitStack(stack); monster->onSpawn(); const auto &mtype = monster->getMonsterType(); if (mtype && mtype->info.raceid > 0 && mtype->info.bosstiaryRace == BosstiaryRarity_t::RARITY_ARCHFOE) { From 664a8067d4179c17417f070dfa37ccd196b7e6b8 Mon Sep 17 00:00:00 2001 From: Pedro Henrique Alves Cruz Date: Tue, 3 Dec 2024 23:47:07 -0300 Subject: [PATCH 55/95] fix: soulpit creatureevents --- .../monster/soulpit_creatureevents.lua | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 data-otservbr-global/scripts/creaturescripts/monster/soulpit_creatureevents.lua diff --git a/data-otservbr-global/scripts/creaturescripts/monster/soulpit_creatureevents.lua b/data-otservbr-global/scripts/creaturescripts/monster/soulpit_creatureevents.lua new file mode 100644 index 00000000000..2dcb067a7e0 --- /dev/null +++ b/data-otservbr-global/scripts/creaturescripts/monster/soulpit_creatureevents.lua @@ -0,0 +1,42 @@ +local enrage = CreatureEvent("enrageSoulPit") +function enrage.onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType) + if not creature or not creature:isMonster() then + return true + end + + local healthPercentage = creature:getHealth() / creature:getMaxHealth() + + if healthPercentage >= 0.6 and healthPercentage <= 0.8 then + primaryDamage = primaryDamage * 0.9 -- 10% damage reduction + secondaryDamage = secondaryDamage * 0.9 -- 10% damage reduction + elseif healthPercentage >= 0.4 and healthPercentage < 0.6 then + primaryDamage = primaryDamage * 0.75 -- 25% damage reduction + secondaryDamage = secondaryDamage * 0.75 -- 25% damage reduction + elseif healthPercentage >= 0.2 and healthPercentage < 0.4 then + primaryDamage = primaryDamage * 0.6 -- 40% damage reduction + secondaryDamage = secondaryDamage * 0.6 -- 40% damage reduction + elseif healthPercentage > 0 and healthPercentage < 0.2 then + primaryDamage = primaryDamage * 0.4 -- 60% damage reduction + secondaryDamage = secondaryDamage * 0.4 -- 60% damage reduction + end + + return primaryDamage, primaryType, secondaryDamage, secondaryType +end +enrage:register() + +local overpower = CreatureEvent("overpowerSoulPit") +function overpower.onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType) + if creature and attacker and attacker:isMonster() and attacker:getForgeStack() == 40 then + primaryDamage = primaryDamage * 1.1 + secondaryDamage = secondaryDamage * 1.1 + creature:getPosition():sendMagicEffect(CONST_ME_CRITICAL_DAMAGE) + end + + return primaryDamage, primaryType, secondaryDamage, secondaryType +end +overpower:register() + +local opressor = CreatureEvent("opressorSoulPit") +function opressor.onThink() +end +opressor:register() From a0f30c3a4906af76b7f7ccb7b20cfec9d64f982b Mon Sep 17 00:00:00 2001 From: Pedro Henrique Alves Cruz Date: Wed, 4 Dec 2024 14:01:58 -0300 Subject: [PATCH 56/95] fix: bestiary on battle and improvements --- .../spells/monster/soulpit_intensehex.lua | 3 ++- .../spells/monster/soulpit_opressor.lua | 3 ++- .../spells/monster/soulpit_powerless.lua | 3 ++- src/creatures/players/player.cpp | 4 +++- .../creatures/monster/monster_functions.cpp | 19 +++++++++++++++++++ .../creatures/monster/monster_functions.hpp | 2 ++ 6 files changed, 30 insertions(+), 4 deletions(-) diff --git a/data-otservbr-global/scripts/spells/monster/soulpit_intensehex.lua b/data-otservbr-global/scripts/spells/monster/soulpit_intensehex.lua index b9ce99fdd89..6b26cdd45d3 100644 --- a/data-otservbr-global/scripts/spells/monster/soulpit_intensehex.lua +++ b/data-otservbr-global/scripts/spells/monster/soulpit_intensehex.lua @@ -9,7 +9,8 @@ combat:addCondition(condition) local spell = Spell("instant") function spell.onCastSpell(creature, var) - if table.contains(creature:getEvents(CREATURE_EVENT_THINK), "opressorSoulPit") then + local monster = creature:getMonster() + if monster and monster:soulPit() and table.contains(creature:getEvents(CREATURE_EVENT_THINK), "opressorSoulPit") then return combat:execute(creature, var) end diff --git a/data-otservbr-global/scripts/spells/monster/soulpit_opressor.lua b/data-otservbr-global/scripts/spells/monster/soulpit_opressor.lua index bfbadc8a714..293c8859dea 100644 --- a/data-otservbr-global/scripts/spells/monster/soulpit_opressor.lua +++ b/data-otservbr-global/scripts/spells/monster/soulpit_opressor.lua @@ -26,7 +26,8 @@ local spell = Spell("instant") local combats = { combatRoot, combatFear } function spell.onCastSpell(creature, var) - if table.contains(creature:getEvents(CREATURE_EVENT_THINK), "opressorSoulPit") then + local monster = creature:getMonster() + if monster and monster:soulPit() and table.contains(creature:getEvents(CREATURE_EVENT_THINK), "opressorSoulPit") then for _, combat in pairs(combats) do combat:execute(creature, var) end diff --git a/data-otservbr-global/scripts/spells/monster/soulpit_powerless.lua b/data-otservbr-global/scripts/spells/monster/soulpit_powerless.lua index 2edbe0b6d8b..7ed71742f8f 100644 --- a/data-otservbr-global/scripts/spells/monster/soulpit_powerless.lua +++ b/data-otservbr-global/scripts/spells/monster/soulpit_powerless.lua @@ -9,7 +9,8 @@ combat:addCondition(condition) local spell = Spell("instant") function spell.onCastSpell(creature, var) - if table.contains(creature:getEvents(CREATURE_EVENT_THINK), "opressorSoulPit") then + local monster = creature:getMonster() + if monster and monster:soulPit() and table.contains(creature:getEvents(CREATURE_EVENT_THINK), "opressorSoulPit") then return combat:execute(creature, var) end diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index 979f5d9a74d..9068a9c6ec0 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -5762,7 +5762,9 @@ bool Player::onKilledMonster(const std::shared_ptr &monster) { return false; } addHuntingTaskKill(mType); - addBestiaryKill(mType); + if (!monster->getSoulPit()) { + addBestiaryKill(mType); + } addBosstiaryKill(mType); return false; } diff --git a/src/lua/functions/creatures/monster/monster_functions.cpp b/src/lua/functions/creatures/monster/monster_functions.cpp index 3f5d0c25ea8..77793f8508b 100644 --- a/src/lua/functions/creatures/monster/monster_functions.cpp +++ b/src/lua/functions/creatures/monster/monster_functions.cpp @@ -66,6 +66,8 @@ void MonsterFunctions::init(lua_State* L) { Lua::registerMethod(L, "Monster", "hazardDamageBoost", MonsterFunctions::luaMonsterHazardDamageBoost); Lua::registerMethod(L, "Monster", "hazardDefenseBoost", MonsterFunctions::luaMonsterHazardDefenseBoost); + Lua::registerMethod(L, "Monster", "soulPit", MonsterFunctions::luaMonsterSoulPit); + Lua::registerMethod(L, "Monster", "addReflectElement", MonsterFunctions::luaMonsterAddReflectElement); Lua::registerMethod(L, "Monster", "addDefense", MonsterFunctions::luaMonsterAddDefense); Lua::registerMethod(L, "Monster", "getDefense", MonsterFunctions::luaMonsterGetDefense); @@ -700,6 +702,23 @@ int MonsterFunctions::luaMonsterHazardDefenseBoost(lua_State* L) { return 1; } +int MonsterFunctions::luaMonsterSoulPit(lua_State* L) { + // get: monster:soulPit() ; set: monster:soulPit(hazard) + const auto &monster = Lua::getUserdataShared(L, 1); + const bool soulPit = Lua::getBoolean(L, 2, false); + if (monster) { + if (lua_gettop(L) == 1) { + Lua::pushBoolean(L, monster->getSoulPit()); + } else { + monster->setSoulPit(soulPit); + Lua::pushBoolean(L, monster->getSoulPit()); + } + } else { + lua_pushnil(L); + } + return 1; +} + int MonsterFunctions::luaMonsterAddReflectElement(lua_State* L) { // monster:addReflectElement(type, percent) const auto &monster = Lua::getUserdataShared(L, 1); diff --git a/src/lua/functions/creatures/monster/monster_functions.hpp b/src/lua/functions/creatures/monster/monster_functions.hpp index 4ba696e941b..fdc45d7732c 100644 --- a/src/lua/functions/creatures/monster/monster_functions.hpp +++ b/src/lua/functions/creatures/monster/monster_functions.hpp @@ -76,6 +76,8 @@ class MonsterFunctions { static int luaMonsterAddDefense(lua_State* L); static int luaMonsterGetDefense(lua_State* L); + static int luaMonsterSoulPit(lua_State* L); + static int luaMonsterIsDead(lua_State* L); static int luaMonsterImmune(lua_State* L); From 5de219c5ade486786b46fe1faad7793c820cab74 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Wed, 4 Dec 2024 02:48:04 +0000 Subject: [PATCH 57/95] Lua code format - (Stylua) --- .../scripts/creaturescripts/monster/soulpit_creatureevents.lua | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/data-otservbr-global/scripts/creaturescripts/monster/soulpit_creatureevents.lua b/data-otservbr-global/scripts/creaturescripts/monster/soulpit_creatureevents.lua index 2dcb067a7e0..6a228144942 100644 --- a/data-otservbr-global/scripts/creaturescripts/monster/soulpit_creatureevents.lua +++ b/data-otservbr-global/scripts/creaturescripts/monster/soulpit_creatureevents.lua @@ -37,6 +37,5 @@ end overpower:register() local opressor = CreatureEvent("opressorSoulPit") -function opressor.onThink() -end +function opressor.onThink() end opressor:register() From eb09ea72dbfee8c05baa7e7b77c485656087d4f8 Mon Sep 17 00:00:00 2001 From: Pedro Henrique Alves Cruz Date: Wed, 4 Dec 2024 14:06:46 -0300 Subject: [PATCH 58/95] fix: soulpit bosstiary and hunting task points --- src/creatures/players/player.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index 9068a9c6ec0..6cca45b3b1b 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -5761,11 +5761,11 @@ bool Player::onKilledMonster(const std::shared_ptr &monster) { g_logger().error("[{}] Monster type is null.", __FUNCTION__); return false; } - addHuntingTaskKill(mType); if (!monster->getSoulPit()) { + addHuntingTaskKill(mType); addBestiaryKill(mType); + addBosstiaryKill(mType); } - addBosstiaryKill(mType); return false; } From 1bfea71e1c620111f88daea1d10b5d8a86849ec7 Mon Sep 17 00:00:00 2001 From: Felipe Paluco Date: Wed, 4 Dec 2024 14:42:23 -0300 Subject: [PATCH 59/95] feat: add upgrade soul prism mechanic --- data-otservbr-global/lib/others/soulpit.lua | 91 +++++++++++---- .../scripts/actions/soulpit/soulpit_fight.lua | 4 +- data/items/items.xml | 4 + data/scripts/actions/items/soul_prism.lua | 106 ++++++++++++++++++ .../monster/ondroploot_soul_core.lua | 5 +- .../monster/monster_type_functions.cpp | 1 + 6 files changed, 184 insertions(+), 27 deletions(-) create mode 100644 data/scripts/actions/items/soul_prism.lua diff --git a/data-otservbr-global/lib/others/soulpit.lua b/data-otservbr-global/lib/others/soulpit.lua index 3f17178354a..d9554c2ef3f 100644 --- a/data-otservbr-global/lib/others/soulpit.lua +++ b/data-otservbr-global/lib/others/soulpit.lua @@ -1,65 +1,110 @@ SoulPit = { + SoulCoresConfiguration = { + chanceToGetSameMonsterSoulCore = 30, -- 30% + chanceToDropSoulCore = 50, -- 50% + chanceToGetOminousSoulCore = 5, -- 5% + monsterVariationsSoulCore = { + ["Horse"] = "horse soul core (taupe)", + ["Brown Horse"] = "horse soul core (brown)", + ["Grey Horse"] = "horse soul core (gray)", + ["Nomad"] = "nomad soul core (basic)", + ["Nomad Blue"] = "nomad soul core (blue)", + ["Nomad Female"] = "nomad soul core (female)", + ["Purple Butterfly"] = "butterfly soul core (purple)", + ["Butterfly"] = "butterfly soul core (blue)", + ["Blue Butterfly"] = "butterfly soul core (blue)", + ["Red Butterfly"] = "butterfly soul core (red)" + }, + monstersDifficulties = { + ["Harmless"] = 1, + ["Trivial"] = 2, + ["Easy"] = 3, + ["Medium"] = 4, + ["Hard"] = 5, + ["Challenge"] = 6 + } + }, encounter = nil, kickEvent = nil, soulCores = Game.getSoulCoreItems(), requiredLevel = 8, playerPositions = { - { pos = Position(32371, 31155, 8), teleport = Position(32373, 31138, 8), effect = CONST_ME_TELEPORT }, - { pos = Position(32371, 31156, 8), teleport = Position(32373, 31138, 8), effect = CONST_ME_TELEPORT }, - { pos = Position(32371, 31157, 8), teleport = Position(32373, 31138, 8), effect = CONST_ME_TELEPORT }, - { pos = Position(32371, 31158, 8), teleport = Position(32373, 31138, 8), effect = CONST_ME_TELEPORT }, - { pos = Position(32371, 31159, 8), teleport = Position(32373, 31138, 8), effect = CONST_ME_TELEPORT }, + { + pos = Position(32371, 31155, 8), + teleport = Position(32373, 31138, 8), + effect = CONST_ME_TELEPORT + }, + { + pos = Position(32371, 31156, 8), + teleport = Position(32373, 31138, 8), + effect = CONST_ME_TELEPORT + }, + { + pos = Position(32371, 31157, 8), + teleport = Position(32373, 31138, 8), + effect = CONST_ME_TELEPORT + }, + { + pos = Position(32371, 31158, 8), + teleport = Position(32373, 31138, 8), + effect = CONST_ME_TELEPORT + }, + { + pos = Position(32371, 31159, 8), + teleport = Position(32373, 31138, 8), + effect = CONST_ME_TELEPORT + } }, waves = { [1] = { stacks = { - [1] = 7, - }, + [1] = 7 + } }, [2] = { stacks = { [1] = 4, - [5] = 3, - }, + [5] = 3 + } }, [3] = { stacks = { [1] = 5, - [15] = 2, - }, + [15] = 2 + } }, [4] = { stacks = { [1] = 3, [5] = 3, - [40] = 1, - }, - }, + [40] = 1 + } + } }, effects = { [1] = CONST_ME_TELEPORT, [5] = CONST_ME_ORANGETELEPORT, [15] = CONST_ME_REDTELEPORT, - [40] = CONST_ME_PURPLETELEPORT, + [40] = CONST_ME_PURPLETELEPORT }, possibleAbilities = { "overpowerSoulPit", "enrageSoulPit", - "opressorSoulPit", + "opressorSoulPit" }, bossAbilities = { - ["overpowerSoulPit"] = { + overpowerSoulPit = { player = true, - monster = false, + monster = false }, - ["enrageSoulPit"] = { + enrageSoulPit = { player = false, - monster = true, + monster = true }, - ["opressorSoulPit"] = { + opressorSoulPit = { player = false, - monster = true, - }, + monster = true + } }, timeToKick = 3 * 1000, -- 3 seconds checkMonstersDelay = 4.5 * 1000, -- 4.5 seconds | The check delay should never be less than the timeToSpawnMonsters. diff --git a/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua b/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua index 0a803e4ec29..d4ba27b4735 100644 --- a/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua +++ b/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua @@ -153,6 +153,8 @@ function soulPitAction.onUse(player, item, fromPosition, target, toPosition, isH end for _, itemType in pairs(SoulPit.soulCores) do - soulPitAction:id(itemType:getId()) + if itemType:getId() ~= 49164 then -- TO-DO: currently Game.getSoulCoreItems() it's returning soul prism item in the results, we don't want this. + soulPitAction:id(itemType:getId()) + end end soulPitAction:register() diff --git a/data/items/items.xml b/data/items/items.xml index 379095e5d9c..b90fff0bb4b 100644 --- a/data/items/items.xml +++ b/data/items/items.xml @@ -80126,5 +80126,9 @@ Granted by TibiaGoals.com"/> + + + + diff --git a/data/scripts/actions/items/soul_prism.lua b/data/scripts/actions/items/soul_prism.lua new file mode 100644 index 00000000000..b4db7df71a9 --- /dev/null +++ b/data/scripts/actions/items/soul_prism.lua @@ -0,0 +1,106 @@ +local soulPrism = Action() + +local function getNextDifficultyLevel(currentLevel) + for level, value in pairs(SoulPit.SoulCoresConfiguration.monstersDifficulties) do + if value == currentLevel + 1 then + return level + end + end + return nil +end + +local function getPreviousDifficultyLevel(currentLevel) + for level, value in pairs(SoulPit.SoulCoresConfiguration.monstersDifficulties) do + if value == currentLevel - 1 then + return level + end + end + return nil +end + +local function getSoulCoreItemForMonster(monsterName) + local lowerMonsterName = monsterName:lower() + local soulCoreName = SoulPit.SoulCoresConfiguration.monsterVariationsSoulCore[monsterName] + + if soulCoreName then + local newSoulCoreId = getItemIdByName(soulCoreName) + if newSoulCoreId then + return newSoulCoreId + end + else + local newMonsterSoulCore = monsterName .. " soul core" + local newSoulCoreId = getItemIdByName(newMonsterSoulCore) + if newSoulCoreId then + return newSoulCoreId + end + end + + return false +end + +function soulPrism.onUse(player, item, fromPosition, target, toPosition, isHotkey) + local itemName = target:getName() + local monsterName = itemName:match("^(.-) soul core") + + if not monsterName then + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You can only use Soul Prism with a Soul Core.") + player:getPosition():sendMagicEffect(CONST_ME_POFF) + return false + end + + local monsterType = MonsterType(monsterName) + if not monsterType then + player:sendTextMessage(MESSAGE_GAME_HIGHLIGHT, "Invalid monster type. Please contact an administrator.") + player:getPosition():sendMagicEffect(CONST_ME_POFF) + return false + end + + local currentDifficulty = monsterType:getBestiaryStars() + local nextDifficultyLevel = getNextDifficultyLevel(currentDifficulty) + local nextDifficultyMonsters = nil + + if nextDifficultyLevel then + nextDifficultyMonsters = monsterType:getMonstersByBestiaryStars(SoulPit.SoulCoresConfiguration.monstersDifficulties[nextDifficultyLevel]) + else + nextDifficultyLevel = currentDifficulty + nextDifficultyMonsters = monsterType:getMonstersByBestiaryStars(SoulPit.SoulCoresConfiguration.monstersDifficulties[currentDifficulty]) + end + + if #nextDifficultyMonsters == 0 then + player:sendTextMessage(MESSAGE_GAME_HIGHLIGHT, "No monsters available for the next difficulty level. Please contact an administrator.") + player:getPosition():sendMagicEffect(CONST_ME_POFF) + return false + end + + local newMonsterType = nextDifficultyMonsters[math.random(#nextDifficultyMonsters)] + local newSoulCoreItem = getSoulCoreItemForMonster(newMonsterType:getName()) + if not newSoulCoreItem then -- Retry a second time. + newSoulCoreItem = getSoulCoreItemForMonster(newMonsterType:getName()) + if not newSoulCoreItem then + player:sendTextMessage(MESSAGE_GAME_HIGHLIGHT, "Failed to generate a Soul Core. Please contact an administrator.") + player:getPosition():sendMagicEffect(CONST_ME_POFF) + return false + end + end + + if player:getFreeCapacity() < ItemType(newSoulCoreItem):getWeight() then + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You do not have enough capacity.") + player:getPosition():sendMagicEffect(CONST_ME_POFF) + return false + end + + if math.random(100) <= SoulPit.SoulCoresConfiguration.chanceToGetOminousSoulCore then + player:addItem(49163, 1) + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You have received an Ominous Soul Core.") + else + player:addItem(newSoulCoreItem, 1) + player:removeItem(target:getId(), 1) + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You have received a " .. newMonsterType:getName() .. " soul core.") + end + player:removeItem(item:getId(), 1) + player:getPosition():sendMagicEffect(CONST_ME_MAGIC_BLUE) + return true +end + +soulPrism:id(49164) +soulPrism:register() \ No newline at end of file diff --git a/data/scripts/eventcallbacks/monster/ondroploot_soul_core.lua b/data/scripts/eventcallbacks/monster/ondroploot_soul_core.lua index 41678f28c0e..eafa067bc98 100644 --- a/data/scripts/eventcallbacks/monster/ondroploot_soul_core.lua +++ b/data/scripts/eventcallbacks/monster/ondroploot_soul_core.lua @@ -1,5 +1,4 @@ local callback = EventCallback("MonsterOnDropLootSoulCore") -local soulCores = Game.getSoulCoreItems() function callback.monsterOnDropLoot(monster, corpse) if not monster or not corpse then @@ -14,11 +13,11 @@ function callback.monsterOnDropLoot(monster, corpse) end local soulCoreId = nil - local trySameMonsterSoulCore = math.random() <= 0.3 -- 30% of chance to drop the same monster soul core | WIP: Externalize this to a lib like libs/soulpit.lua + local trySameMonsterSoulCore = math.random(100) <= SoulPit.SoulCoresConfiguration.chanceToGetSameMonsterSoulCore local mType = monster:getType() local lootTable = {} - if math.random() < 0.5 then -- WIP: Externalize this to a lib like libs/soulpit.lua + if math.random(100) < SoulPit.SoulCoresConfiguration.chanceToDropSoulCore then if trySameMonsterSoulCore then local itemName = monster:getName():lower() .. " soul core" soulCoreId = getItemIdByName(itemName) diff --git a/src/lua/functions/creatures/monster/monster_type_functions.cpp b/src/lua/functions/creatures/monster/monster_type_functions.cpp index e6a54dd8066..03ade91f273 100644 --- a/src/lua/functions/creatures/monster/monster_type_functions.cpp +++ b/src/lua/functions/creatures/monster/monster_type_functions.cpp @@ -1900,6 +1900,7 @@ int MonsterTypeFunctions::luaMonsterTypeGetMonstersByBestiaryStars(lua_State* L) int index = 0; for (const auto &monsterType : monstersByStars) { Lua::pushUserdata(L, monsterType); + Lua::setMetatable(L, -1, "MonsterType"); lua_rawseti(L, -2, ++index); } return 1; From 8b87f4ab5c0b621297b1ced9d67f336f5db67108 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Wed, 4 Dec 2024 17:43:05 +0000 Subject: [PATCH 60/95] Lua code format - (Stylua) --- data-otservbr-global/lib/others/soulpit.lua | 48 +++--- data/scripts/actions/items/soul_prism.lua | 168 ++++++++++---------- 2 files changed, 108 insertions(+), 108 deletions(-) diff --git a/data-otservbr-global/lib/others/soulpit.lua b/data-otservbr-global/lib/others/soulpit.lua index d9554c2ef3f..2628c1307be 100644 --- a/data-otservbr-global/lib/others/soulpit.lua +++ b/data-otservbr-global/lib/others/soulpit.lua @@ -13,7 +13,7 @@ SoulPit = { ["Purple Butterfly"] = "butterfly soul core (purple)", ["Butterfly"] = "butterfly soul core (blue)", ["Blue Butterfly"] = "butterfly soul core (blue)", - ["Red Butterfly"] = "butterfly soul core (red)" + ["Red Butterfly"] = "butterfly soul core (red)", }, monstersDifficulties = { ["Harmless"] = 1, @@ -21,8 +21,8 @@ SoulPit = { ["Easy"] = 3, ["Medium"] = 4, ["Hard"] = 5, - ["Challenge"] = 6 - } + ["Challenge"] = 6, + }, }, encounter = nil, kickEvent = nil, @@ -32,79 +32,79 @@ SoulPit = { { pos = Position(32371, 31155, 8), teleport = Position(32373, 31138, 8), - effect = CONST_ME_TELEPORT + effect = CONST_ME_TELEPORT, }, { pos = Position(32371, 31156, 8), teleport = Position(32373, 31138, 8), - effect = CONST_ME_TELEPORT + effect = CONST_ME_TELEPORT, }, { pos = Position(32371, 31157, 8), teleport = Position(32373, 31138, 8), - effect = CONST_ME_TELEPORT + effect = CONST_ME_TELEPORT, }, { pos = Position(32371, 31158, 8), teleport = Position(32373, 31138, 8), - effect = CONST_ME_TELEPORT + effect = CONST_ME_TELEPORT, }, { pos = Position(32371, 31159, 8), teleport = Position(32373, 31138, 8), - effect = CONST_ME_TELEPORT - } + effect = CONST_ME_TELEPORT, + }, }, waves = { [1] = { stacks = { - [1] = 7 - } + [1] = 7, + }, }, [2] = { stacks = { [1] = 4, - [5] = 3 - } + [5] = 3, + }, }, [3] = { stacks = { [1] = 5, - [15] = 2 - } + [15] = 2, + }, }, [4] = { stacks = { [1] = 3, [5] = 3, - [40] = 1 - } - } + [40] = 1, + }, + }, }, effects = { [1] = CONST_ME_TELEPORT, [5] = CONST_ME_ORANGETELEPORT, [15] = CONST_ME_REDTELEPORT, - [40] = CONST_ME_PURPLETELEPORT + [40] = CONST_ME_PURPLETELEPORT, }, possibleAbilities = { "overpowerSoulPit", "enrageSoulPit", - "opressorSoulPit" + "opressorSoulPit", }, bossAbilities = { overpowerSoulPit = { player = true, - monster = false + monster = false, }, enrageSoulPit = { player = false, - monster = true + monster = true, }, opressorSoulPit = { player = false, - monster = true - } + monster = true, + }, }, timeToKick = 3 * 1000, -- 3 seconds checkMonstersDelay = 4.5 * 1000, -- 4.5 seconds | The check delay should never be less than the timeToSpawnMonsters. diff --git a/data/scripts/actions/items/soul_prism.lua b/data/scripts/actions/items/soul_prism.lua index b4db7df71a9..7c092b8bdb0 100644 --- a/data/scripts/actions/items/soul_prism.lua +++ b/data/scripts/actions/items/soul_prism.lua @@ -1,106 +1,106 @@ local soulPrism = Action() local function getNextDifficultyLevel(currentLevel) - for level, value in pairs(SoulPit.SoulCoresConfiguration.monstersDifficulties) do - if value == currentLevel + 1 then - return level - end - end - return nil + for level, value in pairs(SoulPit.SoulCoresConfiguration.monstersDifficulties) do + if value == currentLevel + 1 then + return level + end + end + return nil end local function getPreviousDifficultyLevel(currentLevel) - for level, value in pairs(SoulPit.SoulCoresConfiguration.monstersDifficulties) do - if value == currentLevel - 1 then - return level - end - end - return nil + for level, value in pairs(SoulPit.SoulCoresConfiguration.monstersDifficulties) do + if value == currentLevel - 1 then + return level + end + end + return nil end local function getSoulCoreItemForMonster(monsterName) - local lowerMonsterName = monsterName:lower() - local soulCoreName = SoulPit.SoulCoresConfiguration.monsterVariationsSoulCore[monsterName] - - if soulCoreName then - local newSoulCoreId = getItemIdByName(soulCoreName) - if newSoulCoreId then - return newSoulCoreId - end - else - local newMonsterSoulCore = monsterName .. " soul core" - local newSoulCoreId = getItemIdByName(newMonsterSoulCore) - if newSoulCoreId then - return newSoulCoreId - end - end - - return false + local lowerMonsterName = monsterName:lower() + local soulCoreName = SoulPit.SoulCoresConfiguration.monsterVariationsSoulCore[monsterName] + + if soulCoreName then + local newSoulCoreId = getItemIdByName(soulCoreName) + if newSoulCoreId then + return newSoulCoreId + end + else + local newMonsterSoulCore = monsterName .. " soul core" + local newSoulCoreId = getItemIdByName(newMonsterSoulCore) + if newSoulCoreId then + return newSoulCoreId + end + end + + return false end function soulPrism.onUse(player, item, fromPosition, target, toPosition, isHotkey) - local itemName = target:getName() - local monsterName = itemName:match("^(.-) soul core") - - if not monsterName then - player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You can only use Soul Prism with a Soul Core.") - player:getPosition():sendMagicEffect(CONST_ME_POFF) - return false - end + local itemName = target:getName() + local monsterName = itemName:match("^(.-) soul core") + + if not monsterName then + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You can only use Soul Prism with a Soul Core.") + player:getPosition():sendMagicEffect(CONST_ME_POFF) + return false + end - local monsterType = MonsterType(monsterName) - if not monsterType then - player:sendTextMessage(MESSAGE_GAME_HIGHLIGHT, "Invalid monster type. Please contact an administrator.") - player:getPosition():sendMagicEffect(CONST_ME_POFF) - return false - end + local monsterType = MonsterType(monsterName) + if not monsterType then + player:sendTextMessage(MESSAGE_GAME_HIGHLIGHT, "Invalid monster type. Please contact an administrator.") + player:getPosition():sendMagicEffect(CONST_ME_POFF) + return false + end - local currentDifficulty = monsterType:getBestiaryStars() - local nextDifficultyLevel = getNextDifficultyLevel(currentDifficulty) - local nextDifficultyMonsters = nil + local currentDifficulty = monsterType:getBestiaryStars() + local nextDifficultyLevel = getNextDifficultyLevel(currentDifficulty) + local nextDifficultyMonsters = nil - if nextDifficultyLevel then - nextDifficultyMonsters = monsterType:getMonstersByBestiaryStars(SoulPit.SoulCoresConfiguration.monstersDifficulties[nextDifficultyLevel]) - else - nextDifficultyLevel = currentDifficulty - nextDifficultyMonsters = monsterType:getMonstersByBestiaryStars(SoulPit.SoulCoresConfiguration.monstersDifficulties[currentDifficulty]) - end + if nextDifficultyLevel then + nextDifficultyMonsters = monsterType:getMonstersByBestiaryStars(SoulPit.SoulCoresConfiguration.monstersDifficulties[nextDifficultyLevel]) + else + nextDifficultyLevel = currentDifficulty + nextDifficultyMonsters = monsterType:getMonstersByBestiaryStars(SoulPit.SoulCoresConfiguration.monstersDifficulties[currentDifficulty]) + end - if #nextDifficultyMonsters == 0 then - player:sendTextMessage(MESSAGE_GAME_HIGHLIGHT, "No monsters available for the next difficulty level. Please contact an administrator.") - player:getPosition():sendMagicEffect(CONST_ME_POFF) - return false - end + if #nextDifficultyMonsters == 0 then + player:sendTextMessage(MESSAGE_GAME_HIGHLIGHT, "No monsters available for the next difficulty level. Please contact an administrator.") + player:getPosition():sendMagicEffect(CONST_ME_POFF) + return false + end - local newMonsterType = nextDifficultyMonsters[math.random(#nextDifficultyMonsters)] - local newSoulCoreItem = getSoulCoreItemForMonster(newMonsterType:getName()) - if not newSoulCoreItem then -- Retry a second time. - newSoulCoreItem = getSoulCoreItemForMonster(newMonsterType:getName()) - if not newSoulCoreItem then - player:sendTextMessage(MESSAGE_GAME_HIGHLIGHT, "Failed to generate a Soul Core. Please contact an administrator.") - player:getPosition():sendMagicEffect(CONST_ME_POFF) - return false - end - end + local newMonsterType = nextDifficultyMonsters[math.random(#nextDifficultyMonsters)] + local newSoulCoreItem = getSoulCoreItemForMonster(newMonsterType:getName()) + if not newSoulCoreItem then -- Retry a second time. + newSoulCoreItem = getSoulCoreItemForMonster(newMonsterType:getName()) + if not newSoulCoreItem then + player:sendTextMessage(MESSAGE_GAME_HIGHLIGHT, "Failed to generate a Soul Core. Please contact an administrator.") + player:getPosition():sendMagicEffect(CONST_ME_POFF) + return false + end + end - if player:getFreeCapacity() < ItemType(newSoulCoreItem):getWeight() then - player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You do not have enough capacity.") - player:getPosition():sendMagicEffect(CONST_ME_POFF) - return false - end + if player:getFreeCapacity() < ItemType(newSoulCoreItem):getWeight() then + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You do not have enough capacity.") + player:getPosition():sendMagicEffect(CONST_ME_POFF) + return false + end - if math.random(100) <= SoulPit.SoulCoresConfiguration.chanceToGetOminousSoulCore then - player:addItem(49163, 1) - player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You have received an Ominous Soul Core.") - else - player:addItem(newSoulCoreItem, 1) - player:removeItem(target:getId(), 1) - player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You have received a " .. newMonsterType:getName() .. " soul core.") - end - player:removeItem(item:getId(), 1) - player:getPosition():sendMagicEffect(CONST_ME_MAGIC_BLUE) - return true + if math.random(100) <= SoulPit.SoulCoresConfiguration.chanceToGetOminousSoulCore then + player:addItem(49163, 1) + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You have received an Ominous Soul Core.") + else + player:addItem(newSoulCoreItem, 1) + player:removeItem(target:getId(), 1) + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You have received a " .. newMonsterType:getName() .. " soul core.") + end + player:removeItem(item:getId(), 1) + player:getPosition():sendMagicEffect(CONST_ME_MAGIC_BLUE) + return true end soulPrism:id(49164) -soulPrism:register() \ No newline at end of file +soulPrism:register() From 9501b443ac7b109dfb2cc5db34edd11e69b027a2 Mon Sep 17 00:00:00 2001 From: Pedro Henrique Alves Cruz Date: Wed, 4 Dec 2024 15:33:45 -0300 Subject: [PATCH 61/95] improve: soulprism code --- data/scripts/actions/items/soul_prism.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/data/scripts/actions/items/soul_prism.lua b/data/scripts/actions/items/soul_prism.lua index 7c092b8bdb0..4241f8277e5 100644 --- a/data/scripts/actions/items/soul_prism.lua +++ b/data/scripts/actions/items/soul_prism.lua @@ -28,7 +28,7 @@ local function getSoulCoreItemForMonster(monsterName) return newSoulCoreId end else - local newMonsterSoulCore = monsterName .. " soul core" + local newMonsterSoulCore = string.format("%s soul core", monsterName) local newSoulCoreId = getItemIdByName(newMonsterSoulCore) if newSoulCoreId then return newSoulCoreId @@ -67,7 +67,7 @@ function soulPrism.onUse(player, item, fromPosition, target, toPosition, isHotke end if #nextDifficultyMonsters == 0 then - player:sendTextMessage(MESSAGE_GAME_HIGHLIGHT, "No monsters available for the next difficulty level. Please contact an administrator.") + player:sendTextMessage(MESSAGE_GAME_HIGHLIGHT, "No monsters available for the next difficulty level.") player:getPosition():sendMagicEffect(CONST_ME_POFF) return false end @@ -77,7 +77,7 @@ function soulPrism.onUse(player, item, fromPosition, target, toPosition, isHotke if not newSoulCoreItem then -- Retry a second time. newSoulCoreItem = getSoulCoreItemForMonster(newMonsterType:getName()) if not newSoulCoreItem then - player:sendTextMessage(MESSAGE_GAME_HIGHLIGHT, "Failed to generate a Soul Core. Please contact an administrator.") + player:sendTextMessage(MESSAGE_GAME_HIGHLIGHT, "Failed to generate a Soul Core.") player:getPosition():sendMagicEffect(CONST_ME_POFF) return false end @@ -95,7 +95,7 @@ function soulPrism.onUse(player, item, fromPosition, target, toPosition, isHotke else player:addItem(newSoulCoreItem, 1) player:removeItem(target:getId(), 1) - player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You have received a " .. newMonsterType:getName() .. " soul core.") + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, string.format("You have received a %s soul core.", newMonsterType:getName())) end player:removeItem(item:getId(), 1) player:getPosition():sendMagicEffect(CONST_ME_MAGIC_BLUE) From 319d742b18dda95694c2b8645221d361c98eddbb Mon Sep 17 00:00:00 2001 From: Pedro Henrique Alves Cruz Date: Thu, 5 Dec 2024 19:04:38 -0300 Subject: [PATCH 62/95] fix: summons inheriting boss abilities and some improvements --- data-otservbr-global/lib/others/soulpit.lua | 11 +++++++++ .../scripts/actions/soulpit/soulpit_fight.lua | 23 ++++++++----------- .../monster/soulpit_creatureevents.lua | 4 ++-- .../spells/monster/soulpit_intensehex.lua | 2 +- .../spells/monster/soulpit_opressor.lua | 2 +- .../spells/monster/soulpit_powerless.lua | 2 +- 6 files changed, 25 insertions(+), 19 deletions(-) diff --git a/data-otservbr-global/lib/others/soulpit.lua b/data-otservbr-global/lib/others/soulpit.lua index 2628c1307be..8eeb80faa7e 100644 --- a/data-otservbr-global/lib/others/soulpit.lua +++ b/data-otservbr-global/lib/others/soulpit.lua @@ -116,6 +116,17 @@ SoulPit = { bossPosition = Position(32372, 31135, 8), exit = Position(32371, 31164, 8), zone = Zone("soulpit"), + + getMonsterVariationNameBySoulCore = function(searchName) + for mTypeName, soulCoreName in pairs(SoulPit.SoulCoresConfiguration.monsterVariationsSoulCore) do + if soulCoreName == searchName then + return mTypeName + end + end + + return nil + end, } SoulPit.zone:addArea(Position(32365, 31134, 8), Position(32382, 31152, 8)) +SoulPit.zone:setRemoveDestination(SoulPit.exit) diff --git a/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua b/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua index d4ba27b4735..1d3ddd21f66 100644 --- a/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua +++ b/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua @@ -1,3 +1,5 @@ +SoulPit.zone:blockFamiliars() + local zoneEvent = ZoneEvent(SoulPit.zone) function zoneEvent.afterLeave(zone, creature) local player = creature:getPlayer() @@ -27,6 +29,10 @@ zoneEvent:register() local soulPitAction = Action() function soulPitAction.onUse(player, item, fromPosition, target, toPosition, isHotkey) logger.warn(item:getName()) + if target and target:getId() == SoulPit.obeliskActive then + creature:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Someone is fighting in the soulpit!") + return false + end if not target or target:getId() ~= SoulPit.obeliskInactive then return false end @@ -49,12 +55,6 @@ function soulPitAction.onUse(player, item, fromPosition, target, toPosition, isH return true end - if not table.empty(SoulPit.zone:getPlayers()) then - local message = "Someone is fighting in the arena!" - creature:sendTextMessage(MESSAGE_EVENT_ADVANCE, message) - return false - end - local isAccountNormal = creature:getAccountType() < ACCOUNT_TYPE_GAMEMASTER if isAccountNormal and creature:getLevel() < SoulPit.requiredLevel then local message = string.format("All players need to be level %s or higher.", SoulPit.requiredLevel) @@ -72,8 +72,8 @@ function soulPitAction.onUse(player, item, fromPosition, target, toPosition, isH end local monsterName = string.gsub(item:getName(), " soul core", "") - - logger.warn("monster name: " .. monsterName) + local monsterVariationName = SoulPit.getMonsterVariationNameBySoulCore(item:getName()) + monsterName = monsterVariationName and monsterVariationName or monsterName SoulPit.zone:removeMonsters() @@ -95,9 +95,7 @@ function soulPitAction.onUse(player, item, fromPosition, target, toPosition, isH SoulPit.kickEvent = addEvent(function() SoulPit.encounter = nil - for _, player in pairs(SoulPit.zone:getPlayers()) do - player:teleportTo(SoulPit.exit) - end + SoulPit.zone:removePlayers() SoulPit.obeliskPosition:transformItem(SoulPit.obeliskActive, SoulPit.obeliskInactive) end, SoulPit.timeToKick) end @@ -106,8 +104,6 @@ function soulPitAction.onUse(player, item, fromPosition, target, toPosition, isH local function waveStart() for stack, amount in pairs(SoulPit.waves[encounter.currentStage].stacks) do - logger.warn("stack: " .. stack) - logger.warn("amount: " .. amount) for i = 1, amount do local position = stack ~= 40 and SoulPit.zone:randomPosition() or SoulPit.bossPosition for i = 1, SoulPit.timeToSpawnMonsters / 1000 do @@ -125,7 +121,6 @@ function soulPitAction.onUse(player, item, fromPosition, target, toPosition, isH return false end if stack == 40 then - logger.warn("ability name: {}", bossAbilityName) if bossAbility.monster then monster:registerEvent(bossAbilityName) end diff --git a/data-otservbr-global/scripts/creaturescripts/monster/soulpit_creatureevents.lua b/data-otservbr-global/scripts/creaturescripts/monster/soulpit_creatureevents.lua index 6a228144942..1810bab01fe 100644 --- a/data-otservbr-global/scripts/creaturescripts/monster/soulpit_creatureevents.lua +++ b/data-otservbr-global/scripts/creaturescripts/monster/soulpit_creatureevents.lua @@ -1,6 +1,6 @@ local enrage = CreatureEvent("enrageSoulPit") function enrage.onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType) - if not creature or not creature:isMonster() then + if not creature or not creature:isMonster() and creature:getMaster() then return true end @@ -26,7 +26,7 @@ enrage:register() local overpower = CreatureEvent("overpowerSoulPit") function overpower.onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType) - if creature and attacker and attacker:isMonster() and attacker:getForgeStack() == 40 then + if creature and attacker and attacker:isMonster() and attacker:getForgeStack() == 40 and not attacker:getMaster() then primaryDamage = primaryDamage * 1.1 secondaryDamage = secondaryDamage * 1.1 creature:getPosition():sendMagicEffect(CONST_ME_CRITICAL_DAMAGE) diff --git a/data-otservbr-global/scripts/spells/monster/soulpit_intensehex.lua b/data-otservbr-global/scripts/spells/monster/soulpit_intensehex.lua index 6b26cdd45d3..51ccca2d979 100644 --- a/data-otservbr-global/scripts/spells/monster/soulpit_intensehex.lua +++ b/data-otservbr-global/scripts/spells/monster/soulpit_intensehex.lua @@ -10,7 +10,7 @@ local spell = Spell("instant") function spell.onCastSpell(creature, var) local monster = creature:getMonster() - if monster and monster:soulPit() and table.contains(creature:getEvents(CREATURE_EVENT_THINK), "opressorSoulPit") then + if monster and monster:soulPit() and not monster:getMaster() and table.contains(creature:getEvents(CREATURE_EVENT_THINK), "opressorSoulPit") then return combat:execute(creature, var) end diff --git a/data-otservbr-global/scripts/spells/monster/soulpit_opressor.lua b/data-otservbr-global/scripts/spells/monster/soulpit_opressor.lua index 293c8859dea..05493618461 100644 --- a/data-otservbr-global/scripts/spells/monster/soulpit_opressor.lua +++ b/data-otservbr-global/scripts/spells/monster/soulpit_opressor.lua @@ -27,7 +27,7 @@ local combats = { combatRoot, combatFear } function spell.onCastSpell(creature, var) local monster = creature:getMonster() - if monster and monster:soulPit() and table.contains(creature:getEvents(CREATURE_EVENT_THINK), "opressorSoulPit") then + if monster and monster:soulPit() and not monster:getMaster() and table.contains(creature:getEvents(CREATURE_EVENT_THINK), "opressorSoulPit") then for _, combat in pairs(combats) do combat:execute(creature, var) end diff --git a/data-otservbr-global/scripts/spells/monster/soulpit_powerless.lua b/data-otservbr-global/scripts/spells/monster/soulpit_powerless.lua index 7ed71742f8f..f2fe9d3a4a9 100644 --- a/data-otservbr-global/scripts/spells/monster/soulpit_powerless.lua +++ b/data-otservbr-global/scripts/spells/monster/soulpit_powerless.lua @@ -10,7 +10,7 @@ local spell = Spell("instant") function spell.onCastSpell(creature, var) local monster = creature:getMonster() - if monster and monster:soulPit() and table.contains(creature:getEvents(CREATURE_EVENT_THINK), "opressorSoulPit") then + if monster and monster:soulPit() and not monster:getMaster() and table.contains(creature:getEvents(CREATURE_EVENT_THINK), "opressorSoulPit") then return combat:execute(creature, var) end From 9f654d05c6a72287b773da521deb7331150c3c8f Mon Sep 17 00:00:00 2001 From: Pedro Henrique Alves Cruz Date: Thu, 5 Dec 2024 19:58:12 -0300 Subject: [PATCH 63/95] improve: customizable bosses abilities --- data-otservbr-global/lib/others/soulpit.lua | 7 +++++++ .../monster/soulpit_creatureevents.lua | 21 +++++++------------ 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/data-otservbr-global/lib/others/soulpit.lua b/data-otservbr-global/lib/others/soulpit.lua index 8eeb80faa7e..0b1d35ebdb7 100644 --- a/data-otservbr-global/lib/others/soulpit.lua +++ b/data-otservbr-global/lib/others/soulpit.lua @@ -94,10 +94,17 @@ SoulPit = { }, bossAbilities = { overpowerSoulPit = { + bonusCriticalDamage = 1.1, player = true, monster = false, }, enrageSoulPit = { + bounds = { + [{0.8, 0.6}] = 0.9, -- 10% damage reduction + [{0.6, 0.4}] = 0.75, -- 25% damage reduction + [{0.4, 0.2}] = 0.6, -- 40% damage reduction + [{0.0, 0.2}] = 0.4, -- 60% damage reduction + }, player = false, monster = true, }, diff --git a/data-otservbr-global/scripts/creaturescripts/monster/soulpit_creatureevents.lua b/data-otservbr-global/scripts/creaturescripts/monster/soulpit_creatureevents.lua index 1810bab01fe..c399ec7fb0a 100644 --- a/data-otservbr-global/scripts/creaturescripts/monster/soulpit_creatureevents.lua +++ b/data-otservbr-global/scripts/creaturescripts/monster/soulpit_creatureevents.lua @@ -6,18 +6,10 @@ function enrage.onHealthChange(creature, attacker, primaryDamage, primaryType, s local healthPercentage = creature:getHealth() / creature:getMaxHealth() - if healthPercentage >= 0.6 and healthPercentage <= 0.8 then - primaryDamage = primaryDamage * 0.9 -- 10% damage reduction - secondaryDamage = secondaryDamage * 0.9 -- 10% damage reduction - elseif healthPercentage >= 0.4 and healthPercentage < 0.6 then - primaryDamage = primaryDamage * 0.75 -- 25% damage reduction - secondaryDamage = secondaryDamage * 0.75 -- 25% damage reduction - elseif healthPercentage >= 0.2 and healthPercentage < 0.4 then - primaryDamage = primaryDamage * 0.6 -- 40% damage reduction - secondaryDamage = secondaryDamage * 0.6 -- 40% damage reduction - elseif healthPercentage > 0 and healthPercentage < 0.2 then - primaryDamage = primaryDamage * 0.4 -- 60% damage reduction - secondaryDamage = secondaryDamage * 0.4 -- 60% damage reduction + for bounds, reduction in pairs(SoulPit.bossAbilities.enrageSoulPit.bounds) do + if healthPercentage > bounds[2] and healthPercentage <= bounds[1] then + return primaryDamage * reduction, primaryType, secondaryDamage * reduction, secondaryType + end end return primaryDamage, primaryType, secondaryDamage, secondaryType @@ -27,8 +19,9 @@ enrage:register() local overpower = CreatureEvent("overpowerSoulPit") function overpower.onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType) if creature and attacker and attacker:isMonster() and attacker:getForgeStack() == 40 and not attacker:getMaster() then - primaryDamage = primaryDamage * 1.1 - secondaryDamage = secondaryDamage * 1.1 + local bonusCriticalDamage = SoulPit.bossAbilities.overpowerSoulPit.bonusCriticalDamage + primaryDamage = primaryDamage * bonusCriticalDamage + secondaryDamage = secondaryDamage * bonusCriticalDamage creature:getPosition():sendMagicEffect(CONST_ME_CRITICAL_DAMAGE) end From c4e539f8273701990f0bad5be07b66c6b1f8437a Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Thu, 5 Dec 2024 22:59:37 +0000 Subject: [PATCH 64/95] Lua code format - (Stylua) --- data-otservbr-global/lib/others/soulpit.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/data-otservbr-global/lib/others/soulpit.lua b/data-otservbr-global/lib/others/soulpit.lua index 0b1d35ebdb7..4eb4dac4d53 100644 --- a/data-otservbr-global/lib/others/soulpit.lua +++ b/data-otservbr-global/lib/others/soulpit.lua @@ -100,10 +100,10 @@ SoulPit = { }, enrageSoulPit = { bounds = { - [{0.8, 0.6}] = 0.9, -- 10% damage reduction - [{0.6, 0.4}] = 0.75, -- 25% damage reduction - [{0.4, 0.2}] = 0.6, -- 40% damage reduction - [{0.0, 0.2}] = 0.4, -- 60% damage reduction + [{ 0.8, 0.6 }] = 0.9, -- 10% damage reduction + [{ 0.6, 0.4 }] = 0.75, -- 25% damage reduction + [{ 0.4, 0.2 }] = 0.6, -- 40% damage reduction + [{ 0.0, 0.2 }] = 0.4, -- 60% damage reduction }, player = false, monster = true, From 5d91df46dec75ff83ca08847173fbdd25aa029f0 Mon Sep 17 00:00:00 2001 From: Pedro Henrique Alves Cruz Date: Fri, 6 Dec 2024 13:51:35 -0300 Subject: [PATCH 65/95] fix: soulprimsm being returned --- src/lua/functions/core/game/game_functions.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/lua/functions/core/game/game_functions.cpp b/src/lua/functions/core/game/game_functions.cpp index 8619ff814f6..7ca0bd6333b 100644 --- a/src/lua/functions/core/game/game_functions.cpp +++ b/src/lua/functions/core/game/game_functions.cpp @@ -1047,6 +1047,9 @@ int GameFunctions::luaGameGetSoulCoreItems(lua_State* L) { std::vector soulCoreItems; for (const auto &itemType : Item::items.getItems()) { + if (itemType.m_primaryType.empty() && itemType.type == ITEM_TYPE_NONE) { + continue; + } if (itemType.m_primaryType == "SoulCores" || itemType.type == ITEM_TYPE_SOULCORES) { soulCoreItems.emplace_back(&itemType); } From 9333f5f3adbebc566f82f47be3f133c522b90225 Mon Sep 17 00:00:00 2001 From: Felipe Paluco Date: Fri, 6 Dec 2024 13:37:23 -0300 Subject: [PATCH 66/95] feat: add downgrade exalted core mechanic --- data/scripts/actions/items/exalted_core.lua | 92 +++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 data/scripts/actions/items/exalted_core.lua diff --git a/data/scripts/actions/items/exalted_core.lua b/data/scripts/actions/items/exalted_core.lua new file mode 100644 index 00000000000..a45633a3af7 --- /dev/null +++ b/data/scripts/actions/items/exalted_core.lua @@ -0,0 +1,92 @@ +local exaltedCore = Action() + +local function getPreviousDifficultyLevel(currentLevel) + for level, value in pairs(SoulPit.SoulCoresConfiguration.monstersDifficulties) do + if value == currentLevel - 1 then + return level + end + end + return nil +end + +local function getSoulCoreItemForMonster(monsterName) + local lowerMonsterName = monsterName:lower() + local soulCoreName = SoulPit.SoulCoresConfiguration.monsterVariationsSoulCore[monsterName] + + if soulCoreName then + local newSoulCoreId = getItemIdByName(soulCoreName) + if newSoulCoreId then + return newSoulCoreId + end + else + local newMonsterSoulCore = string.format("%s soul core", monsterName) + local newSoulCoreId = getItemIdByName(newMonsterSoulCore) + if newSoulCoreId then + return newSoulCoreId + end + end + + return false +end + +function exaltedCore.onUse(player, item, fromPosition, target, toPosition, isHotkey) + local itemName = target:getName() + local monsterName = itemName:match("^(.-) soul core") + + if not monsterName then + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You can only use Exalted Core with a Soul Core.") + player:getPosition():sendMagicEffect(CONST_ME_POFF) + return false + end + + local monsterType = MonsterType(monsterName) + if not monsterType then + player:sendTextMessage(MESSAGE_GAME_HIGHLIGHT, "Invalid monster type. Please contact an administrator.") + player:getPosition():sendMagicEffect(CONST_ME_POFF) + return false + end + + local currentDifficulty = monsterType:getBestiaryStars() + local previousDifficultyLevel = getPreviousDifficultyLevel(currentDifficulty) + local previousDifficultyMonsters = nil + + if previousDifficultyLevel then + previousDifficultyMonsters = monsterType:getMonstersByBestiaryStars(SoulPit.SoulCoresConfiguration.monstersDifficulties[previousDifficultyLevel]) + else + previousDifficultyLevel = currentDifficulty + previousDifficultyMonsters = monsterType:getMonstersByBestiaryStars(SoulPit.SoulCoresConfiguration.monstersDifficulties[currentDifficulty]) + end + + if #previousDifficultyMonsters == 0 then + player:sendTextMessage(MESSAGE_GAME_HIGHLIGHT, "No monsters available for the previous difficulty level.") + player:getPosition():sendMagicEffect(CONST_ME_POFF) + return false + end + + local newMonsterType = previousDifficultyMonsters[math.random(#previousDifficultyMonsters)] + local newSoulCoreItem = getSoulCoreItemForMonster(newMonsterType:getName()) + if not newSoulCoreItem then -- Retry a second time. + newSoulCoreItem = getSoulCoreItemForMonster(newMonsterType:getName()) + if not newSoulCoreItem then + player:sendTextMessage(MESSAGE_GAME_HIGHLIGHT, "Failed to generate a Soul Core.") + player:getPosition():sendMagicEffect(CONST_ME_POFF) + return false + end + end + + if player:getFreeCapacity() < ItemType(newSoulCoreItem):getWeight() then + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You do not have enough capacity.") + player:getPosition():sendMagicEffect(CONST_ME_POFF) + return false + end + + player:addItem(newSoulCoreItem, 1) + player:removeItem(target:getId(), 1) + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, string.format("You have received a %s soul core.", newMonsterType:getName())) + player:removeItem(item:getId(), 1) + player:getPosition():sendMagicEffect(CONST_ME_MAGIC_BLUE) + return true +end + +exaltedCore:id(37110) +exaltedCore:register() From b582c8f14055f837323c8e0607b27ff8fafbfbda Mon Sep 17 00:00:00 2001 From: Felipe Paluco Date: Fri, 6 Dec 2024 14:33:58 -0300 Subject: [PATCH 67/95] feat: add fusion possibility through soul cores --- data-otservbr-global/lib/others/soulpit.lua | 19 +++++++++++++++++++ .../scripts/actions/soulpit/soulpit_fight.lua | 12 ++++++++---- data/items/items.xml | 6 ++++++ data/scripts/actions/items/exalted_core.lua | 2 +- data/scripts/actions/items/soul_prism.lua | 2 +- 5 files changed, 35 insertions(+), 6 deletions(-) diff --git a/data-otservbr-global/lib/others/soulpit.lua b/data-otservbr-global/lib/others/soulpit.lua index 4eb4dac4d53..bbaf0bf48c9 100644 --- a/data-otservbr-global/lib/others/soulpit.lua +++ b/data-otservbr-global/lib/others/soulpit.lua @@ -133,6 +133,25 @@ SoulPit = { return nil end, + getSoulCoreMonster = function(name) + return name:match("^(.-) soul core") + end, + onFuseSoulCores = function(player, item, target) + local itemName = item:getName() + local targetItemName = target:getName() + + if SoulPit.getSoulCoreMonster(itemName) and SoulPit.getSoulCoreMonster(targetItemName) then + local randomSoulCore = SoulPit.soulCores[math.random(#SoulPit.soulCores)] + player:addItem(randomSoulCore:getId(), 1) + player:getPosition():sendMagicEffect(CONST_ME_MAGIC_BLUE) + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, string.format("You have received a %s soul core.", randomSoulCore:getName())) + item:remove(1) + target:remove(1) + return true + end + + return false + end } SoulPit.zone:addArea(Position(32365, 31134, 8), Position(32382, 31152, 8)) diff --git a/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua b/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua index 1d3ddd21f66..fff8a92267d 100644 --- a/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua +++ b/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua @@ -27,7 +27,11 @@ end zoneEvent:register() local soulPitAction = Action() -function soulPitAction.onUse(player, item, fromPosition, target, toPosition, isHotkey) +function soulPitAction.onUse(player, item, fromPosition, target, toPosition, isHotkey) + if SoulPit.onFuseSoulCores(player, item, target) then + return true + end + logger.warn(item:getName()) if target and target:getId() == SoulPit.obeliskActive then creature:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Someone is fighting in the soulpit!") @@ -148,8 +152,8 @@ function soulPitAction.onUse(player, item, fromPosition, target, toPosition, isH end for _, itemType in pairs(SoulPit.soulCores) do - if itemType:getId() ~= 49164 then -- TO-DO: currently Game.getSoulCoreItems() it's returning soul prism item in the results, we don't want this. - soulPitAction:id(itemType:getId()) - end + -- if itemType:getId() ~= 49164 then -- TO-DO: currently Game.getSoulCoreItems() it's returning soul prism item in the results, we don't want this. + soulPitAction:id(itemType:getId()) + -- end end soulPitAction:register() diff --git a/data/items/items.xml b/data/items/items.xml index b90fff0bb4b..cb62f819edc 100644 --- a/data/items/items.xml +++ b/data/items/items.xml @@ -80130,5 +80130,11 @@ Granted by TibiaGoals.com"/> + + + + + + diff --git a/data/scripts/actions/items/exalted_core.lua b/data/scripts/actions/items/exalted_core.lua index a45633a3af7..7a4c778589a 100644 --- a/data/scripts/actions/items/exalted_core.lua +++ b/data/scripts/actions/items/exalted_core.lua @@ -31,7 +31,7 @@ end function exaltedCore.onUse(player, item, fromPosition, target, toPosition, isHotkey) local itemName = target:getName() - local monsterName = itemName:match("^(.-) soul core") + local monsterName = SoulPit.getSoulCoreMonster(itemName) if not monsterName then player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You can only use Exalted Core with a Soul Core.") diff --git a/data/scripts/actions/items/soul_prism.lua b/data/scripts/actions/items/soul_prism.lua index 4241f8277e5..190ad086098 100644 --- a/data/scripts/actions/items/soul_prism.lua +++ b/data/scripts/actions/items/soul_prism.lua @@ -40,7 +40,7 @@ end function soulPrism.onUse(player, item, fromPosition, target, toPosition, isHotkey) local itemName = target:getName() - local monsterName = itemName:match("^(.-) soul core") + local monsterName = SoulPit.getSoulCoreMonster(itemName) if not monsterName then player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You can only use Soul Prism with a Soul Core.") From 0b0452a5295bb9bf31fdd7381041e94a6de9416a Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Fri, 6 Dec 2024 17:34:49 +0000 Subject: [PATCH 68/95] Lua code format - (Stylua) --- data-otservbr-global/lib/others/soulpit.lua | 2 +- data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/data-otservbr-global/lib/others/soulpit.lua b/data-otservbr-global/lib/others/soulpit.lua index bbaf0bf48c9..752a207f401 100644 --- a/data-otservbr-global/lib/others/soulpit.lua +++ b/data-otservbr-global/lib/others/soulpit.lua @@ -151,7 +151,7 @@ SoulPit = { end return false - end + end, } SoulPit.zone:addArea(Position(32365, 31134, 8), Position(32382, 31152, 8)) diff --git a/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua b/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua index fff8a92267d..c326eb3b772 100644 --- a/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua +++ b/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua @@ -27,7 +27,7 @@ end zoneEvent:register() local soulPitAction = Action() -function soulPitAction.onUse(player, item, fromPosition, target, toPosition, isHotkey) +function soulPitAction.onUse(player, item, fromPosition, target, toPosition, isHotkey) if SoulPit.onFuseSoulCores(player, item, target) then return true end From a14c4bf3f3a4d2d95788a8b91e7b8136aff9a1fd Mon Sep 17 00:00:00 2001 From: Pedro Henrique Alves Cruz Date: Fri, 6 Dec 2024 16:54:33 -0300 Subject: [PATCH 69/95] feat: animus mastery --- config.lua.dist | 11 +++++ .../scripts/actions/soulpit/soulpit_fight.lua | 2 +- src/config/config_enums.hpp | 4 ++ src/config/configmanager.cpp | 4 ++ src/creatures/CMakeLists.txt | 1 + .../players/animus_mastery/animus_mastery.cpp | 47 +++++++++++++++++++ .../players/animus_mastery/animus_mastery.hpp | 36 ++++++++++++++ src/creatures/players/player.cpp | 21 +++++++-- src/creatures/players/player.hpp | 8 +++- .../functions/core/game/game_functions.cpp | 3 -- .../creatures/player/player_functions.cpp | 44 +++++++++++++++++ .../creatures/player/player_functions.hpp | 4 ++ 12 files changed, 175 insertions(+), 10 deletions(-) create mode 100644 src/creatures/players/animus_mastery/animus_mastery.cpp create mode 100644 src/creatures/players/animus_mastery/animus_mastery.hpp diff --git a/config.lua.dist b/config.lua.dist index 60c4b770063..0a5c2280584 100644 --- a/config.lua.dist +++ b/config.lua.dist @@ -86,6 +86,17 @@ maxItem = 5000 maxContainer = 500 maxContainerDepth = 200 +-- Animus Mastery - SoulPit (Get more info in: TBD) +-- NOTE: animusMasteryMaxMonsterXpMultiplier is the maximum experience the multiplier can be. +-- NOTE: animusMasteryMonsterXpMultiplier is the monster experience multiplier that has the animus mastery unlocked. +-- NOTE: animusMasteryMonstersXpMultiplier is the multiplier for each 'animusMasteryMonstersToIncreaseXpMultiplier' monsters that +-- the player has the animus mastery unlocked. +-- NOTE: animusMasteryMonstersToIncreaseXpMultiplier is the amount of monster to increase the experience multiplier by 'animusMasteryMonstersXpMultiplier'. +animusMasteryMaxMonsterXpMultiplier = 4.0 +animusMasteryMonsterXpMultiplier = 2.0 +animusMasteryMonstersXpMultiplier = 0.1 +animusMasteryMonstersToIncreaseXpMultiplier = 10 + -- Augments System (Get more info in: https://github.com/opentibiabr/canary/pull/2602) -- NOTE: the following values are for all weapons and equipments that have type of "increase damage", "powerful impact" and "strong impact". -- To customize the percentage of a particular item with these augment types, please add to the item "augments" section on items.xml as the example above. diff --git a/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua b/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua index c326eb3b772..a4036c702d0 100644 --- a/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua +++ b/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua @@ -93,8 +93,8 @@ function soulPitAction.onUse(player, item, fromPosition, target, toPosition, isH SoulPit.zone:removeMonsters() for _, player in pairs(SoulPit.zone:getPlayers()) do + player:addAnimusMastery(monsterName) player:sendTextMessage(MESSAGE_EVENT_ADVANCE, string.format("You have defeated the core of the %s soul and unlocked its animus mastery!", monsterName)) - -- Add the monster animus mastery for the player. end SoulPit.kickEvent = addEvent(function() diff --git a/src/config/config_enums.hpp b/src/config/config_enums.hpp index d180cc668c2..62f5f6e257c 100644 --- a/src/config/config_enums.hpp +++ b/src/config/config_enums.hpp @@ -16,6 +16,10 @@ enum ConfigKey_t : uint16_t { AIMBOT_HOTKEY_ENABLED, ALLOW_CHANGEOUTFIT, ALLOW_RELOAD, + ANIMUS_MASTERY_MAX_MONSTER_XP_MULTIPLIER, + ANIMUS_MASTERY_MONSTER_XP_MULTIPLIER, + ANIMUS_MASTERY_MONSTERS_XP_MULTIPLIER, + ANIMUS_MASTERY_MONSTERS_TO_INCREASE_XP_MULTIPLIER, AUGMENT_INCREASED_DAMAGE_PERCENT, AUGMENT_POWERFUL_IMPACT_PERCENT, AUGMENT_STRONG_IMPACT_PERCENT, diff --git a/src/config/configmanager.cpp b/src/config/configmanager.cpp index 5ef93d5461b..1b6410a94d0 100644 --- a/src/config/configmanager.cpp +++ b/src/config/configmanager.cpp @@ -199,6 +199,9 @@ bool ConfigManager::load() { loadFloatConfig(L, TRANSCENDANCE_CHANCE_FORMULA_A, "transcendanceChanceFormulaA", 0.0127); loadFloatConfig(L, TRANSCENDANCE_CHANCE_FORMULA_B, "transcendanceChanceFormulaB", 0.1070); loadFloatConfig(L, TRANSCENDANCE_CHANCE_FORMULA_C, "transcendanceChanceFormulaC", 0.0073); + loadFloatConfig(L, ANIMUS_MASTERY_MAX_MONSTER_XP_MULTIPLIER, "animusMasteryMaxMonsterXpMultiplier", 4.0); + loadFloatConfig(L, ANIMUS_MASTERY_MONSTER_XP_MULTIPLIER, "animusMasteryMonsterXpMultiplier", 2.0); + loadFloatConfig(L, ANIMUS_MASTERY_MONSTERS_XP_MULTIPLIER, "animusMasteryMonstersXpMultiplier", 0.1); loadIntConfig(L, ACTIONS_DELAY_INTERVAL, "timeBetweenActions", 200); loadIntConfig(L, ADVENTURERSBLESSING_LEVEL, "adventurersBlessingLevel", 21); @@ -344,6 +347,7 @@ bool ConfigManager::load() { loadIntConfig(L, AUGMENT_INCREASED_DAMAGE_PERCENT, "augmentIncreasedDamagePercent", 5); loadIntConfig(L, AUGMENT_POWERFUL_IMPACT_PERCENT, "augmentPowerfulImpactPercent", 10); loadIntConfig(L, AUGMENT_STRONG_IMPACT_PERCENT, "augmentStrongImpactPercent", 7); + loadIntConfig(L, ANIMUS_MASTERY_MONSTERS_TO_INCREASE_XP_MULTIPLIER, "animusMasteryMonstersToIncreaseXpMultiplier", 10); loadStringConfig(L, CORE_DIRECTORY, "coreDirectory", "data"); loadStringConfig(L, DATA_DIRECTORY, "dataPackDirectory", "data-otservbr-global"); diff --git a/src/creatures/CMakeLists.txt b/src/creatures/CMakeLists.txt index c87a45a0aa0..b48b9c42844 100644 --- a/src/creatures/CMakeLists.txt +++ b/src/creatures/CMakeLists.txt @@ -12,6 +12,7 @@ target_sources(${PROJECT_NAME}_lib PRIVATE npcs/npc.cpp npcs/npcs.cpp npcs/spawns/spawn_npc.cpp + players/animus_mastery/animus_mastery.cpp players/grouping/familiars.cpp players/grouping/groups.cpp players/grouping/guild.cpp diff --git a/src/creatures/players/animus_mastery/animus_mastery.cpp b/src/creatures/players/animus_mastery/animus_mastery.cpp new file mode 100644 index 00000000000..62c0f03d99e --- /dev/null +++ b/src/creatures/players/animus_mastery/animus_mastery.cpp @@ -0,0 +1,47 @@ +/** + * Canary - A free and open-source MMORPG server emulator + * Copyright (©) 2019-2024 OpenTibiaBR + * Repository: https://github.com/opentibiabr/canary + * License: https://github.com/opentibiabr/canary/blob/main/LICENSE + * Contributors: https://github.com/opentibiabr/canary/graphs/contributors + * Website: https://docs.opentibiabr.com/ + */ + +#include "creatures/players/animus_mastery/animus_mastery.hpp" +#include "creatures/players/player.hpp" +#include "config/configmanager.hpp" + +AnimusMastery::AnimusMastery(Player &player) : + m_player(player), + maxMonsterXpMultiplier(g_configManager().getFloat(ANIMUS_MASTERY_MAX_MONSTER_XP_MULTIPLIER)), + monsterXpMultiplier(g_configManager().getFloat(ANIMUS_MASTERY_MONSTER_XP_MULTIPLIER)), + monstersXpMultiplier(g_configManager().getFloat(ANIMUS_MASTERY_MONSTERS_XP_MULTIPLIER)), + monstersAmountToMultiply(g_configManager().getFloat(ANIMUS_MASTERY_MONSTERS_TO_INCREASE_XP_MULTIPLIER)) { } + +void AnimusMastery::add(std::string_view addMonsterType) { + if (!has(addMonsterType)) { + animusMasteries.emplace_back(addMonsterType); + } +} + +void AnimusMastery::remove(std::string_view removeMonsterType) { + std::erase_if(animusMasteries, [removeMonsterType](const std::string &monsterType) { + return monsterType == removeMonsterType; + }); +} + +bool AnimusMastery::has(std::string_view searchMonsterType) const { + auto it = std::ranges::find(animusMasteries, searchMonsterType); + + return it != animusMasteries.end(); +} + +float AnimusMastery::getExperienceMultiplier() const { + uint16_t monsterAmountMultiplier = animusMasteries.size() / monstersAmountToMultiply; + + return std::min(maxMonsterXpMultiplier, 1 + (monsterAmountMultiplier * monstersXpMultiplier)); +} + +const std::vector &AnimusMastery::getAnimusMasteries() const { + return animusMasteries; +} diff --git a/src/creatures/players/animus_mastery/animus_mastery.hpp b/src/creatures/players/animus_mastery/animus_mastery.hpp new file mode 100644 index 00000000000..c6eab7ac1da --- /dev/null +++ b/src/creatures/players/animus_mastery/animus_mastery.hpp @@ -0,0 +1,36 @@ +/** + * Canary - A free and open-source MMORPG server emulator + * Copyright (©) 2019-2024 OpenTibiaBR + * Repository: https://github.com/opentibiabr/canary + * License: https://github.com/opentibiabr/canary/blob/main/LICENSE + * Contributors: https://github.com/opentibiabr/canary/graphs/contributors + * Website: https://docs.opentibiabr.com/ + */ + +#pragma once + +class Player; + +class AnimusMastery { +public: + explicit AnimusMastery(Player &player); + + void add(std::string_view addMonsterType); + void remove(std::string_view removeMonsterType); + + bool has(std::string_view searchMonsterType) const; + + float getExperienceMultiplier() const; + + const std::vector &getAnimusMasteries() const; + +private: + Player &m_player; + + float maxMonsterXpMultiplier = 4.0; + float monsterXpMultiplier = 2.0; + float monstersXpMultiplier = 0.1; + uint16_t monstersAmountToMultiply = 10; + + std::vector animusMasteries; +}; diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index 6cca45b3b1b..5ed3bc1c88a 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -19,6 +19,7 @@ #include "creatures/monsters/monster.hpp" #include "creatures/monsters/monsters.hpp" #include "creatures/npcs/npc.hpp" +#include "creatures/players/animus_mastery/animus_mastery.hpp" #include "creatures/players/wheel/player_wheel.hpp" #include "creatures/players/wheel/wheel_gems.hpp" #include "creatures/players/achievement/player_achievement.hpp" @@ -78,6 +79,7 @@ Player::Player(std::shared_ptr p) : m_playerBadge = std::make_unique(*this); m_playerCyclopedia = std::make_unique(*this); m_playerTitle = std::make_unique(*this); + m_animusMastery = std::make_unique(*this); } Player::~Player() { @@ -10266,6 +10268,15 @@ const std::unique_ptr &Player::title() const { return m_playerTitle; } +// Cyclopedia interface +std::unique_ptr &Player::cyclopedia() { + return m_playerCyclopedia; +} + +const std::unique_ptr &Player::cyclopedia() const { + return m_playerCyclopedia; +} + // VIP interface std::unique_ptr &Player::vip() { return m_playerVIP; @@ -10275,13 +10286,13 @@ const std::unique_ptr &Player::vip() const { return m_playerVIP; } -// Cyclopedia -std::unique_ptr &Player::cyclopedia() { - return m_playerCyclopedia; +// Animus Mastery interface +std::unique_ptr &Player::animusMastery() { + return m_animusMastery; } -const std::unique_ptr &Player::cyclopedia() const { - return m_playerCyclopedia; +const std::unique_ptr &Player::animusMastery() const { + return m_animusMastery; } void Player::sendLootMessage(const std::string &message) const { diff --git a/src/creatures/players/player.hpp b/src/creatures/players/player.hpp index 1b52607f2d8..8b2dbfb448a 100644 --- a/src/creatures/players/player.hpp +++ b/src/creatures/players/player.hpp @@ -17,6 +17,7 @@ #include "game/movement/position.hpp" #include "creatures/creatures_definitions.hpp" +class AnimusMastery; class House; class NetworkMessage; class Weapon; @@ -1257,7 +1258,7 @@ class Player final : public Creature, public Cylinder, public Bankable { std::unique_ptr &title(); const std::unique_ptr &title() const; - // Player summary interface + // Player cyclopedia interface std::unique_ptr &cyclopedia(); const std::unique_ptr &cyclopedia() const; @@ -1265,6 +1266,10 @@ class Player final : public Creature, public Cylinder, public Bankable { std::unique_ptr &vip(); const std::unique_ptr &vip() const; + // Player animusMastery interface + std::unique_ptr &animusMastery(); + const std::unique_ptr &animusMastery() const; + void sendLootMessage(const std::string &message) const; std::shared_ptr getLootPouch(); @@ -1636,6 +1641,7 @@ class Player final : public Creature, public Cylinder, public Bankable { std::unique_ptr m_playerCyclopedia; std::unique_ptr m_playerTitle; std::unique_ptr m_playerVIP; + std::unique_ptr m_animusMastery; std::mutex quickLootMutex; diff --git a/src/lua/functions/core/game/game_functions.cpp b/src/lua/functions/core/game/game_functions.cpp index 7ca0bd6333b..8619ff814f6 100644 --- a/src/lua/functions/core/game/game_functions.cpp +++ b/src/lua/functions/core/game/game_functions.cpp @@ -1047,9 +1047,6 @@ int GameFunctions::luaGameGetSoulCoreItems(lua_State* L) { std::vector soulCoreItems; for (const auto &itemType : Item::items.getItems()) { - if (itemType.m_primaryType.empty() && itemType.type == ITEM_TYPE_NONE) { - continue; - } if (itemType.m_primaryType == "SoulCores" || itemType.type == ITEM_TYPE_SOULCORES) { soulCoreItems.emplace_back(&itemType); } diff --git a/src/lua/functions/creatures/player/player_functions.cpp b/src/lua/functions/creatures/player/player_functions.cpp index 08045f1de00..a158ae21139 100644 --- a/src/lua/functions/creatures/player/player_functions.cpp +++ b/src/lua/functions/creatures/player/player_functions.cpp @@ -15,6 +15,7 @@ #include "creatures/creature.hpp" #include "creatures/interactions/chat.hpp" #include "creatures/monsters/monsters.hpp" +#include "creatures/players/animus_mastery/animus_mastery.hpp" #include "creatures/players/achievement/player_achievement.hpp" #include "creatures/players/cyclopedia/player_cyclopedia.hpp" #include "creatures/players/cyclopedia/player_title.hpp" @@ -403,6 +404,10 @@ void PlayerFunctions::init(lua_State* L) { Lua::registerMethod(L, "Player", "removeIconBakragore", PlayerFunctions::luaPlayerRemoveIconBakragore); Lua::registerMethod(L, "Player", "sendCreatureAppear", PlayerFunctions::luaPlayerSendCreatureAppear); + Lua::registerMethod(L, "Player", "addAnimusMastery", PlayerFunctions::luaPlayerAddAnimusMastery); + Lua::registerMethod(L, "Player", "removeAnimusMastery", PlayerFunctions::luaPlayerRemoveAnimusMastery); + Lua::registerMethod(L, "Player", "hasAnimusMastery", PlayerFunctions::luaPlayerHasAnimusMastery); + GroupFunctions::init(L); GuildFunctions::init(L); MountFunctions::init(L); @@ -4849,3 +4854,42 @@ int PlayerFunctions::luaPlayerSendCreatureAppear(lua_State* L) { Lua::pushBoolean(L, true); return 1; } + +int PlayerFunctions::luaPlayerAddAnimusMastery(lua_State* L) { + auto player = Lua::getUserdataShared(L, 1); + if (!player) { + Lua::reportErrorFunc(Lua::getErrorDesc(LUA_ERROR_PLAYER_NOT_FOUND)); + return 1; + } + + const std::string &monsterType = Lua::getString(L, 2); + player->animusMastery()->add(monsterType); + + return 1; +} +int PlayerFunctions::luaPlayerRemoveAnimusMastery(lua_State* L) { + auto player = Lua::getUserdataShared(L, 1); + if (!player) { + Lua::reportErrorFunc(Lua::getErrorDesc(LUA_ERROR_PLAYER_NOT_FOUND)); + return 1; + } + + const std::string &monsterType = Lua::getString(L, 2); + player->animusMastery()->remove(monsterType); + + return 1; +} +int PlayerFunctions::luaPlayerHasAnimusMastery(lua_State* L) { + auto player = Lua::getUserdataShared(L, 1); + if (!player) { + Lua::reportErrorFunc(Lua::getErrorDesc(LUA_ERROR_PLAYER_NOT_FOUND)); + return 1; + } + + const std::string &monsterType = Lua::getString(L, 2); + + bool has = player->animusMastery()->has(monsterType); + Lua::pushBoolean(L, has); + + return 1; +} diff --git a/src/lua/functions/creatures/player/player_functions.hpp b/src/lua/functions/creatures/player/player_functions.hpp index ab617c35132..992d0f66b45 100644 --- a/src/lua/functions/creatures/player/player_functions.hpp +++ b/src/lua/functions/creatures/player/player_functions.hpp @@ -383,5 +383,9 @@ class PlayerFunctions { static int luaPlayerSendCreatureAppear(lua_State* L); + static int luaPlayerAddAnimusMastery(lua_State* L); + static int luaPlayerRemoveAnimusMastery(lua_State* L); + static int luaPlayerHasAnimusMastery(lua_State* L); + friend class CreatureFunctions; }; From 78e9ae0c674e7e356e046c750599a23fa8851417 Mon Sep 17 00:00:00 2001 From: Pedro Henrique Alves Cruz Date: Fri, 6 Dec 2024 21:43:37 -0300 Subject: [PATCH 70/95] feat: gain animus mastery experience and exp message update --- .../players/animus_mastery/animus_mastery.cpp | 47 ++++++++++++++----- .../players/animus_mastery/animus_mastery.hpp | 13 +++-- src/creatures/players/player.cpp | 12 +++++ src/io/functions/iologindata_load_player.cpp | 15 ++++++ src/io/functions/iologindata_load_player.hpp | 1 + src/io/functions/iologindata_save_player.cpp | 9 ++++ src/io/iologindata.cpp | 3 ++ src/server/network/protocol/protocolgame.cpp | 5 +- 8 files changed, 87 insertions(+), 18 deletions(-) diff --git a/src/creatures/players/animus_mastery/animus_mastery.cpp b/src/creatures/players/animus_mastery/animus_mastery.cpp index 62c0f03d99e..d6f7b2f0459 100644 --- a/src/creatures/players/animus_mastery/animus_mastery.cpp +++ b/src/creatures/players/animus_mastery/animus_mastery.cpp @@ -12,26 +12,30 @@ #include "config/configmanager.hpp" AnimusMastery::AnimusMastery(Player &player) : - m_player(player), - maxMonsterXpMultiplier(g_configManager().getFloat(ANIMUS_MASTERY_MAX_MONSTER_XP_MULTIPLIER)), - monsterXpMultiplier(g_configManager().getFloat(ANIMUS_MASTERY_MONSTER_XP_MULTIPLIER)), - monstersXpMultiplier(g_configManager().getFloat(ANIMUS_MASTERY_MONSTERS_XP_MULTIPLIER)), - monstersAmountToMultiply(g_configManager().getFloat(ANIMUS_MASTERY_MONSTERS_TO_INCREASE_XP_MULTIPLIER)) { } + m_player(player) { + maxMonsterXpMultiplier = g_configManager().getFloat(ANIMUS_MASTERY_MAX_MONSTER_XP_MULTIPLIER); + monsterXpMultiplier = g_configManager().getFloat(ANIMUS_MASTERY_MONSTER_XP_MULTIPLIER); + monstersXpMultiplier = g_configManager().getFloat(ANIMUS_MASTERY_MONSTERS_XP_MULTIPLIER); + monstersAmountToMultiply = std::clamp(g_configManager().getNumber(ANIMUS_MASTERY_MONSTERS_TO_INCREASE_XP_MULTIPLIER), 1, std::numeric_limits::max()); +} -void AnimusMastery::add(std::string_view addMonsterType) { +void AnimusMastery::add(const std::string &addMonsterType) { if (!has(addMonsterType)) { - animusMasteries.emplace_back(addMonsterType); + const std::string &lowerMonsterName = asLowerCaseString(addMonsterType); + animusMasteries.emplace_back(lowerMonsterName); } } -void AnimusMastery::remove(std::string_view removeMonsterType) { - std::erase_if(animusMasteries, [removeMonsterType](const std::string &monsterType) { - return monsterType == removeMonsterType; +void AnimusMastery::remove(const std::string &removeMonsterType) { + const std::string &lowerMonsterName = asLowerCaseString(removeMonsterType); + std::erase_if(animusMasteries, [lowerMonsterName](const std::string &monsterType) { + return asLowerCaseString(monsterType) == lowerMonsterName; }); } -bool AnimusMastery::has(std::string_view searchMonsterType) const { - auto it = std::ranges::find(animusMasteries, searchMonsterType); +bool AnimusMastery::has(const std::string &searchMonsterType) const { + const std::string &lowerMonsterName = asLowerCaseString(searchMonsterType); + auto it = std::ranges::find(animusMasteries, lowerMonsterName); return it != animusMasteries.end(); } @@ -39,9 +43,26 @@ bool AnimusMastery::has(std::string_view searchMonsterType) const { float AnimusMastery::getExperienceMultiplier() const { uint16_t monsterAmountMultiplier = animusMasteries.size() / monstersAmountToMultiply; - return std::min(maxMonsterXpMultiplier, 1 + (monsterAmountMultiplier * monstersXpMultiplier)); + return std::min(maxMonsterXpMultiplier, 1 + (monsterXpMultiplier + (monsterAmountMultiplier * monstersXpMultiplier)) / 100); +} + +uint16_t AnimusMastery::getPoints() const { + return animusMasteries.size(); } const std::vector &AnimusMastery::getAnimusMasteries() const { return animusMasteries; } + +void AnimusMastery::serialize(PropWriteStream &propWriteStream) const { + std::ranges::for_each(animusMasteries, [&propWriteStream](const std::string &monsterName) { + propWriteStream.writeString(monsterName); + }); +} + +bool AnimusMastery::unserialize(PropStream &propStream) { + std::string monsterName; + while (propStream.readString(monsterName)) { + animusMasteries.emplace_back(monsterName); + } +} diff --git a/src/creatures/players/animus_mastery/animus_mastery.hpp b/src/creatures/players/animus_mastery/animus_mastery.hpp index c6eab7ac1da..e0b9625b4f2 100644 --- a/src/creatures/players/animus_mastery/animus_mastery.hpp +++ b/src/creatures/players/animus_mastery/animus_mastery.hpp @@ -10,20 +10,27 @@ #pragma once class Player; +class PropStream; +class PropWriteStream; class AnimusMastery { public: explicit AnimusMastery(Player &player); - void add(std::string_view addMonsterType); - void remove(std::string_view removeMonsterType); + void add(const std::string &addMonsterType); + void remove(const std::string &removeMonsterType); - bool has(std::string_view searchMonsterType) const; + bool has(const std::string &searchMonsterType) const; float getExperienceMultiplier() const; + uint16_t getPoints() const; + const std::vector &getAnimusMasteries() const; + void serialize(PropWriteStream &propWriteStream) const; + bool unserialize(PropStream &propStream); + private: Player &m_player; diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index 5ed3bc1c88a..a7b9e5e192c 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -3095,6 +3095,14 @@ void Player::addExperience(const std::shared_ptr &target, uint64_t exp exp += (exp * (1.75 * getHazardSystemPoints() * g_configManager().getFloat(HAZARD_EXP_BONUS_MULTIPLIER))) / 100.; } + const bool handleAnimusMastery = monster && animusMastery()->has(monster->getMonsterType()->name); + float animusMasteryMultiplier = 0; + + if (handleAnimusMastery) { + animusMasteryMultiplier = animusMastery()->getExperienceMultiplier(); + exp *= animusMasteryMultiplier; + } + experience += exp; if (sendText) { @@ -3106,6 +3114,10 @@ void Player::addExperience(const std::shared_ptr &target, uint64_t exp } } + if (handleAnimusMastery) { + expString = fmt::format("{} (animus mastery bonus {:.1f}%)", expString, (animusMasteryMultiplier - 1) * 100); + } + TextMessage message(MESSAGE_EXPERIENCE, "You gained " + expString + (handleHazardExperience ? " (Hazard)" : "")); message.position = position; message.primary.value = exp; diff --git a/src/io/functions/iologindata_load_player.cpp b/src/io/functions/iologindata_load_player.cpp index 21d251c6dbe..df79ee92737 100644 --- a/src/io/functions/iologindata_load_player.cpp +++ b/src/io/functions/iologindata_load_player.cpp @@ -14,6 +14,7 @@ #include "creatures/combat/condition.hpp" #include "database/database.hpp" #include "creatures/monsters/monsters.hpp" +#include "creatures/players/animus_mastery/animus_mastery.hpp" #include "creatures/players/achievement/player_achievement.hpp" #include "creatures/players/cyclopedia/player_badge.hpp" #include "creatures/players/cyclopedia/player_cyclopedia.hpp" @@ -275,6 +276,20 @@ void IOLoginDataLoad::loadPlayerConditions(const std::shared_ptr &player } } +void IOLoginDataLoad::loadPlayerAnimusMastery(const std::shared_ptr &player, const DBResult_ptr &result) { + if (!result || !player) { + g_logger().warn("[{}] - Player or Result nullptr", __FUNCTION__); + return; + } + + unsigned long attrSize; + const char* attr = result->getStream("animus_mastery", attrSize); + PropStream propStream; + propStream.init(attr, attrSize); + + player->animusMastery()->unserialize(propStream); +} + void IOLoginDataLoad::loadPlayerDefaultOutfit(const std::shared_ptr &player, const DBResult_ptr &result) { if (!result || !player) { g_logger().warn("[{}] - Player or Result nullptr", __FUNCTION__); diff --git a/src/io/functions/iologindata_load_player.hpp b/src/io/functions/iologindata_load_player.hpp index ca05bdcb1b8..08d31e93072 100644 --- a/src/io/functions/iologindata_load_player.hpp +++ b/src/io/functions/iologindata_load_player.hpp @@ -22,6 +22,7 @@ class IOLoginDataLoad : public IOLoginData { static void loadPlayerExperience(const std::shared_ptr &player, const DBResult_ptr &result); static void loadPlayerBlessings(const std::shared_ptr &player, const DBResult_ptr &result); static void loadPlayerConditions(const std::shared_ptr &player, const DBResult_ptr &result); + static void loadPlayerAnimusMastery(const std::shared_ptr &player, const DBResult_ptr &result); static void loadPlayerDefaultOutfit(const std::shared_ptr &player, const DBResult_ptr &result); static void loadPlayerSkullSystem(const std::shared_ptr &player, const DBResult_ptr &result); static void loadPlayerSkill(const std::shared_ptr &player, const DBResult_ptr &result); diff --git a/src/io/functions/iologindata_save_player.cpp b/src/io/functions/iologindata_save_player.cpp index 613fdac1cb0..47b2c92fee0 100644 --- a/src/io/functions/iologindata_save_player.cpp +++ b/src/io/functions/iologindata_save_player.cpp @@ -10,6 +10,7 @@ #include "io/functions/iologindata_save_player.hpp" #include "config/configmanager.hpp" +#include "creatures/players/animus_mastery/animus_mastery.hpp" #include "creatures/combat/condition.hpp" #include "creatures/monsters/monsters.hpp" #include "game/game.hpp" @@ -246,6 +247,14 @@ bool IOLoginDataSave::savePlayerFirst(const std::shared_ptr &player) { query << "`conditions` = " << db.escapeBlob(attributes, static_cast(attributesSize)) << ","; + // serialize animus mastery + PropWriteStream propAnimusMasteryStream; + player->animusMastery()->serialize(propAnimusMasteryStream); + size_t animusMasterySize; + const char* animusMastery = propAnimusMasteryStream.getStream(animusMasterySize); + + query << "`animus_mastery` = " << db.escapeBlob(animusMastery, static_cast(animusMasterySize)) << ","; + if (g_game().getWorldType() != WORLD_TYPE_PVP_ENFORCED) { int64_t skullTime = 0; diff --git a/src/io/iologindata.cpp b/src/io/iologindata.cpp index 37ec4a4dec1..aea837beec5 100644 --- a/src/io/iologindata.cpp +++ b/src/io/iologindata.cpp @@ -113,6 +113,9 @@ bool IOLoginData::loadPlayer(const std::shared_ptr &player, const DBResu // load conditions IOLoginDataLoad::loadPlayerConditions(player, result); + // load animus mastery + IOLoginDataLoad::loadPlayerAnimusMastery(player, result); + // load default outfit IOLoginDataLoad::loadPlayerDefaultOutfit(player, result); diff --git a/src/server/network/protocol/protocolgame.cpp b/src/server/network/protocol/protocolgame.cpp index 60e29847477..4322b56a968 100644 --- a/src/server/network/protocol/protocolgame.cpp +++ b/src/server/network/protocol/protocolgame.cpp @@ -19,6 +19,7 @@ #include "creatures/monsters/monster.hpp" #include "creatures/monsters/monsters.hpp" #include "creatures/npcs/npc.hpp" +#include "creatures/players/animus_mastery/animus_mastery.hpp" #include "creatures/players/achievement/player_achievement.hpp" #include "creatures/players/cyclopedia/player_badge.hpp" #include "creatures/players/cyclopedia/player_cyclopedia.hpp" @@ -2383,8 +2384,8 @@ void ProtocolGame::parseBestiarysendMonsterData(NetworkMessage &msg) { newmsg.addByte(currentLevel); - newmsg.add(0); // Animus Mastery Bonus - newmsg.add(0); // Animus Mastery Points + newmsg.add(static_cast((player->animusMastery()->getExperienceMultiplier() - 1) * 1000)); // Animus Mastery Bonus + newmsg.add(player->animusMastery()->getPoints()); // Animus Mastery Points newmsg.add(killCounter); From c263ad3b47f39913d12c72e0ba9b4baf0033f3c9 Mon Sep 17 00:00:00 2001 From: Pedro Henrique Alves Cruz Date: Mon, 9 Dec 2024 02:27:47 -0300 Subject: [PATCH 71/95] fix: final adjustments --- data-otservbr-global/lib/others/soulpit.lua | 43 +++++++++++++---- .../monster/humanoids/orc_warlord.lua | 3 -- .../scripts/actions/soulpit/soulpit_fight.lua | 47 +++++++++---------- .../monster/soulpit_creatureevents.lua | 13 ----- .../spells/monster/soulpit_intensehex.lua | 1 + .../spells/monster/soulpit_opressor.lua | 1 + .../spells/monster/soulpit_powerless.lua | 1 + src/creatures/combat/combat.cpp | 3 +- src/creatures/monsters/monster.cpp | 20 ++++++-- src/creatures/monsters/monster.hpp | 10 +++- src/game/game.cpp | 14 +++++- .../creatures/monster/monster_functions.cpp | 37 +++++++++++++++ .../creatures/monster/monster_functions.hpp | 4 +- 13 files changed, 140 insertions(+), 57 deletions(-) diff --git a/data-otservbr-global/lib/others/soulpit.lua b/data-otservbr-global/lib/others/soulpit.lua index 752a207f401..ad5d7076053 100644 --- a/data-otservbr-global/lib/others/soulpit.lua +++ b/data-otservbr-global/lib/others/soulpit.lua @@ -1,4 +1,5 @@ SoulPit = { + registeredMonstersSpell = {}, SoulCoresConfiguration = { chanceToGetSameMonsterSoulCore = 30, -- 30% chanceToDropSoulCore = 50, -- 50% @@ -94,9 +95,12 @@ SoulPit = { }, bossAbilities = { overpowerSoulPit = { - bonusCriticalDamage = 1.1, - player = true, - monster = false, + criticalChance = 50, -- 50% + criticalDamage = 25, -- 25% + apply = function(monster) + monster:criticalChance(SoulPit.bossAbilities.overpowerSoulPit.criticalChance) + monster:criticalDamage(SoulPit.bossAbilities.overpowerSoulPit.criticalDamage) + end, }, enrageSoulPit = { bounds = { @@ -105,15 +109,38 @@ SoulPit = { [{ 0.4, 0.2 }] = 0.6, -- 40% damage reduction [{ 0.0, 0.2 }] = 0.4, -- 60% damage reduction }, - player = false, - monster = true, + apply = function(monster) + monster:registerEvent("enrageSoulPit") + end, }, opressorSoulPit = { - player = false, - monster = true, + spells = { + { name = "soulpit opressor", interval = 2000, chance = 25, minDamage = 0, maxDamage = 0 }, + { name = "soulpit powerless", interval = 2000, chance = 30, minDamage = 0, maxDamage = 0 }, + { name = "soulpit intensehex", interval = 2000, chance = 15, minDamage = 0, maxDamage = 0 }, + }, + apply = function(monster) + monster:registerEvent("opressorSoulPit") + local monsterType = monster:getType() + local monsterTypeName = monsterType:name() + + -- Checking spells + if SoulPit.registeredMonstersSpell[monsterTypeName] then + return true + end + + -- Applying spells + for _, spell in pairs(SoulPit.bossAbilities.opressorSoulPit.spells) do + monsterType:addAttack(readSpell(spell, monsterType)) + end + + SoulPit.registeredMonstersSpell[monsterTypeName] = true + + return true + end, }, }, - timeToKick = 3 * 1000, -- 3 seconds + timeToKick = 10 * 60 * 1000, -- 10 minutes checkMonstersDelay = 4.5 * 1000, -- 4.5 seconds | The check delay should never be less than the timeToSpawnMonsters. timeToSpawnMonsters = 4 * 1000, -- 4 seconds totalMonsters = 7, diff --git a/data-otservbr-global/monster/humanoids/orc_warlord.lua b/data-otservbr-global/monster/humanoids/orc_warlord.lua index 5b33843d971..db5b0e20288 100644 --- a/data-otservbr-global/monster/humanoids/orc_warlord.lua +++ b/data-otservbr-global/monster/humanoids/orc_warlord.lua @@ -107,9 +107,6 @@ monster.loot = { monster.attacks = { { name = "melee", interval = 2000, chance = 100, minDamage = 0, maxDamage = -250 }, { name = "combat", interval = 2000, chance = 20, type = COMBAT_PHYSICALDAMAGE, minDamage = 0, maxDamage = -200, range = 7, shootEffect = CONST_ANI_THROWINGSTAR, target = false }, - { name = "soulpit opressor", interval = 2000, chance = 25, minDamage = 0, maxDamage = 0 }, - { name = "soulpit powerless", interval = 2000, chance = 30, minDamage = 0, maxDamage = 0 }, - { name = "soulpit intensehex", interval = 2000, chance = 15, minDamage = 0, maxDamage = 0 }, } monster.defenses = { diff --git a/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua b/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua index a4036c702d0..0cb2bfd402b 100644 --- a/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua +++ b/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua @@ -17,12 +17,6 @@ function zoneEvent.afterLeave(zone, creature) SoulPit.obeliskPosition:transformItem(SoulPit.obeliskActive, SoulPit.obeliskInactive) end end - - for abilityName, abilityInfo in pairs(SoulPit.bossAbilities) do - if abilityInfo.player then - player:unregisterEvent(abilityName) - end - end end zoneEvent:register() @@ -32,7 +26,6 @@ function soulPitAction.onUse(player, item, fromPosition, target, toPosition, isH return true end - logger.warn(item:getName()) if target and target:getId() == SoulPit.obeliskActive then creature:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Someone is fighting in the soulpit!") return false @@ -69,12 +62,27 @@ function soulPitAction.onUse(player, item, fromPosition, target, toPosition, isH local infoPositions = lever:getInfoPositions() return true end) + lever:checkPositions() - if lever:checkConditions() then - lever:teleportPlayers() - SoulPit.obeliskPosition:transformItem(SoulPit.obeliskInactive, SoulPit.obeliskActive) + if not lever:checkConditions() then + return true + end + + item:remove(1) + + if SoulPit.kickEvent then + stopEvent(SoulPit.kickEvent) end + lever:teleportPlayers() + SoulPit.obeliskPosition:transformItem(SoulPit.obeliskInactive, SoulPit.obeliskActive) + SoulPit.kickEvent = addEvent(function() + SoulPit.kickEvent = nil + SoulPit.encounter = nil + SoulPit.zone:removePlayers() + SoulPit.obeliskPosition:transformItem(SoulPit.obeliskActive, SoulPit.obeliskInactive) + end, SoulPit.timeToKick) + local monsterName = string.gsub(item:getName(), " soul core", "") local monsterVariationName = SoulPit.getMonsterVariationNameBySoulCore(item:getName()) monsterName = monsterVariationName and monsterVariationName or monsterName @@ -97,11 +105,7 @@ function soulPitAction.onUse(player, item, fromPosition, target, toPosition, isH player:sendTextMessage(MESSAGE_EVENT_ADVANCE, string.format("You have defeated the core of the %s soul and unlocked its animus mastery!", monsterName)) end - SoulPit.kickEvent = addEvent(function() - SoulPit.encounter = nil - SoulPit.zone:removePlayers() - SoulPit.obeliskPosition:transformItem(SoulPit.obeliskActive, SoulPit.obeliskInactive) - end, SoulPit.timeToKick) + SoulPit.zone:removePlayers() end SoulPit.encounter = encounter @@ -125,12 +129,7 @@ function soulPitAction.onUse(player, item, fromPosition, target, toPosition, isH return false end if stack == 40 then - if bossAbility.monster then - monster:registerEvent(bossAbilityName) - end - if bossAbility.player then - player:registerEvent(bossAbilityName) - end + bossAbility.apply(monster) end end, SoulPit.timeToSpawnMonsters, monsterName, stack, position, randomAbility, chosenBossAbility) end @@ -152,8 +151,8 @@ function soulPitAction.onUse(player, item, fromPosition, target, toPosition, isH end for _, itemType in pairs(SoulPit.soulCores) do - -- if itemType:getId() ~= 49164 then -- TO-DO: currently Game.getSoulCoreItems() it's returning soul prism item in the results, we don't want this. - soulPitAction:id(itemType:getId()) - -- end + if itemType:getId() ~= 49164 then -- Exclude soul prism + soulPitAction:id(itemType:getId()) + end end soulPitAction:register() diff --git a/data-otservbr-global/scripts/creaturescripts/monster/soulpit_creatureevents.lua b/data-otservbr-global/scripts/creaturescripts/monster/soulpit_creatureevents.lua index c399ec7fb0a..e2bc9a27cea 100644 --- a/data-otservbr-global/scripts/creaturescripts/monster/soulpit_creatureevents.lua +++ b/data-otservbr-global/scripts/creaturescripts/monster/soulpit_creatureevents.lua @@ -16,19 +16,6 @@ function enrage.onHealthChange(creature, attacker, primaryDamage, primaryType, s end enrage:register() -local overpower = CreatureEvent("overpowerSoulPit") -function overpower.onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType) - if creature and attacker and attacker:isMonster() and attacker:getForgeStack() == 40 and not attacker:getMaster() then - local bonusCriticalDamage = SoulPit.bossAbilities.overpowerSoulPit.bonusCriticalDamage - primaryDamage = primaryDamage * bonusCriticalDamage - secondaryDamage = secondaryDamage * bonusCriticalDamage - creature:getPosition():sendMagicEffect(CONST_ME_CRITICAL_DAMAGE) - end - - return primaryDamage, primaryType, secondaryDamage, secondaryType -end -overpower:register() - local opressor = CreatureEvent("opressorSoulPit") function opressor.onThink() end opressor:register() diff --git a/data-otservbr-global/scripts/spells/monster/soulpit_intensehex.lua b/data-otservbr-global/scripts/spells/monster/soulpit_intensehex.lua index 51ccca2d979..2f6b25b0d78 100644 --- a/data-otservbr-global/scripts/spells/monster/soulpit_intensehex.lua +++ b/data-otservbr-global/scripts/spells/monster/soulpit_intensehex.lua @@ -22,4 +22,5 @@ spell:words("###940") spell:blockWalls(true) spell:needTarget(true) spell:needLearn(true) +spell:isAggressive(true) spell:register() diff --git a/data-otservbr-global/scripts/spells/monster/soulpit_opressor.lua b/data-otservbr-global/scripts/spells/monster/soulpit_opressor.lua index 05493618461..b504fcb0116 100644 --- a/data-otservbr-global/scripts/spells/monster/soulpit_opressor.lua +++ b/data-otservbr-global/scripts/spells/monster/soulpit_opressor.lua @@ -41,4 +41,5 @@ spell:words("###938") spell:blockWalls(true) spell:needTarget(false) spell:needLearn(true) +spell:isAggressive(true) spell:register() diff --git a/data-otservbr-global/scripts/spells/monster/soulpit_powerless.lua b/data-otservbr-global/scripts/spells/monster/soulpit_powerless.lua index f2fe9d3a4a9..13f2969eaac 100644 --- a/data-otservbr-global/scripts/spells/monster/soulpit_powerless.lua +++ b/data-otservbr-global/scripts/spells/monster/soulpit_powerless.lua @@ -22,4 +22,5 @@ spell:words("###939") spell:blockWalls(true) spell:needTarget(true) spell:needLearn(true) +spell:isAggressive(true) spell:register() diff --git a/src/creatures/combat/combat.cpp b/src/creatures/combat/combat.cpp index a70ef43a63a..273eb3d382d 100644 --- a/src/creatures/combat/combat.cpp +++ b/src/creatures/combat/combat.cpp @@ -2254,7 +2254,8 @@ void Combat::applyExtensions(const std::shared_ptr &caster, const std: } } } else if (monster) { - chance = monster->critChance() * 100; + chance = monster->getCriticalChance() * 100; + bonus = monster->getCriticalDamage() * 100; } bonus += damage.criticalDamage; diff --git a/src/creatures/monsters/monster.cpp b/src/creatures/monsters/monster.cpp index 51e2f6ff10c..965ea5805bf 100644 --- a/src/creatures/monsters/monster.cpp +++ b/src/creatures/monsters/monster.cpp @@ -249,8 +249,20 @@ bool Monster::canSeeInvisibility() const { return isImmune(CONDITION_INVISIBLE); } -uint16_t Monster::critChance() const { - return mType->info.critChance; +void Monster::setCriticalDamage(uint16_t damage) { + criticalDamage = damage; +} + +uint16_t Monster::getCriticalDamage() const { + return criticalDamage; +} + +void Monster::setCriticalChance(uint16_t chance) { + criticalChance = chance; +} + +uint16_t Monster::getCriticalChance() const { + return mType->info.critChance + criticalChance; } uint32_t Monster::getManaCost() const { @@ -1127,7 +1139,7 @@ void Monster::doAttacking(uint32_t interval) { bool inRange = false; if (spellBlock.spell == nullptr || (spellBlock.isMelee && isFleeing())) { - continue; + continue; } if (canUseSpell(myPos, targetPos, spellBlock, interval, inRange, resetTicks)) { @@ -1141,7 +1153,7 @@ void Monster::doAttacking(uint32_t interval) { maxCombatValue = spellBlock.maxCombatValue; if (spellBlock.spell == nullptr) { - continue; + continue; } spellBlock.spell->castSpell(getMonster(), attackedCreature); diff --git a/src/creatures/monsters/monster.hpp b/src/creatures/monsters/monster.hpp index 40214c92e83..b904769130c 100644 --- a/src/creatures/monsters/monster.hpp +++ b/src/creatures/monsters/monster.hpp @@ -77,7 +77,6 @@ class Monster final : public Creature { bool isHostile() const; bool isFamiliar() const; bool canSeeInvisibility() const override; - uint16_t critChance() const; uint32_t getManaCost() const; RespawnType getRespawnType() const; void setSpawnMonster(const std::shared_ptr &newSpawnMonster); @@ -231,6 +230,12 @@ class Monster final : public Creature { void setDead(bool isDead); + void setCriticalChance(uint16_t chance); + uint16_t getCriticalChance() const; + + void setCriticalDamage(uint16_t damage); + uint16_t getCriticalDamage() const; + protected: void onExecuteAsyncTasks() override; @@ -264,6 +269,9 @@ class Monster final : public Creature { uint16_t totalPlayersOnScreen = 0; + uint16_t criticalChance = 0; + uint16_t criticalDamage = 0; + uint32_t attackTicks = 0; uint32_t targetChangeTicks = 0; uint32_t defenseTicks = 0; diff --git a/src/game/game.cpp b/src/game/game.cpp index 5ce501c8288..540cd922bbe 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -7625,8 +7625,15 @@ void Game::buildMessageAsTarget( const std::string &damageString ) const { ss.str({}); - auto attackMsg = damage.critical ? "critical " : ""; - auto article = damage.critical ? "a" : "an"; + const auto &monster = attacker->getMonster(); + bool handleSoulPit = monster ? monster->getSoulPit() && monster->getForgeStack() == 40 : false; + + std::string attackMsg = ""; + std::string article = "an"; + if (!handleSoulPit && damage.critical) { + attackMsg = "critical"; + article = "a"; + } ss << "You lose " << damageString; if (!attacker) { ss << '.'; @@ -7638,6 +7645,9 @@ void Game::buildMessageAsTarget( if (damage.extension) { ss << " " << damage.exString; } + if (handleSoulPit && damage.critical) { + ss << " (Soulpit Crit)"; + } message.type = MESSAGE_DAMAGE_RECEIVED; message.text = ss.str(); } diff --git a/src/lua/functions/creatures/monster/monster_functions.cpp b/src/lua/functions/creatures/monster/monster_functions.cpp index 77793f8508b..83b484d5e53 100644 --- a/src/lua/functions/creatures/monster/monster_functions.cpp +++ b/src/lua/functions/creatures/monster/monster_functions.cpp @@ -75,6 +75,9 @@ void MonsterFunctions::init(lua_State* L) { Lua::registerMethod(L, "Monster", "isDead", MonsterFunctions::luaMonsterIsDead); Lua::registerMethod(L, "Monster", "immune", MonsterFunctions::luaMonsterImmune); + Lua::registerMethod(L, "Monster", "criticalChance", MonsterFunctions::luaMonsterCriticalChance); + Lua::registerMethod(L, "Monster", "criticalDamage", MonsterFunctions::luaMonsterCriticalDamage); + CharmFunctions::init(L); LootFunctions::init(L); MonsterSpellFunctions::init(L); @@ -791,3 +794,37 @@ int MonsterFunctions::luaMonsterImmune(lua_State* L) { Lua::pushBoolean(L, monster->isImmune()); return 1; } + +int MonsterFunctions::luaMonsterCriticalChance(lua_State* L) { + // get: monster:criticalChance(); set: monster:criticalChance(critical) + const auto &monster = Lua::getUserdataShared(L, 1); + const auto critical = Lua::getNumber(L, 2, 0); + if (monster) { + if (lua_gettop(L) == 1) { + Lua::pushBoolean(L, monster->getCriticalChance()); + } else { + monster->setCriticalChance(critical); + Lua::pushBoolean(L, monster->getCriticalChance()); + } + } else { + lua_pushnil(L); + } + return 1; +} + +int MonsterFunctions::luaMonsterCriticalDamage(lua_State* L) { + // get: monster:criticalDamage(); set: monster:criticalDamage(damage) + const auto &monster = Lua::getUserdataShared(L, 1); + const auto damage = Lua::getNumber(L, 2, 0); + if (monster) { + if (lua_gettop(L) == 1) { + Lua::pushBoolean(L, monster->getCriticalDamage()); + } else { + monster->setCriticalDamage(damage); + Lua::pushBoolean(L, monster->getCriticalDamage()); + } + } else { + lua_pushnil(L); + } + return 1; +} diff --git a/src/lua/functions/creatures/monster/monster_functions.hpp b/src/lua/functions/creatures/monster/monster_functions.hpp index fdc45d7732c..ca8f5848271 100644 --- a/src/lua/functions/creatures/monster/monster_functions.hpp +++ b/src/lua/functions/creatures/monster/monster_functions.hpp @@ -80,6 +80,8 @@ class MonsterFunctions { static int luaMonsterIsDead(lua_State* L); static int luaMonsterImmune(lua_State* L); - + static int luaMonsterCriticalChance(lua_State* L); + static int luaMonsterCriticalDamage(lua_State* L); + friend class CreatureFunctions; }; From ba5ab7d3ea377fca3e93af7fd44a0c96112eb3be Mon Sep 17 00:00:00 2001 From: Felipe Paluco Date: Sat, 7 Dec 2024 09:45:13 -0300 Subject: [PATCH 72/95] feat: add chance to drop soul prism --- data-otservbr-global/lib/others/soulpit.lua | 7 ++++--- .../monster/ondroploot_soul_core.lua | 17 ++++++++++++----- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/data-otservbr-global/lib/others/soulpit.lua b/data-otservbr-global/lib/others/soulpit.lua index ad5d7076053..8ae31552317 100644 --- a/data-otservbr-global/lib/others/soulpit.lua +++ b/data-otservbr-global/lib/others/soulpit.lua @@ -1,9 +1,10 @@ SoulPit = { registeredMonstersSpell = {}, SoulCoresConfiguration = { - chanceToGetSameMonsterSoulCore = 30, -- 30% - chanceToDropSoulCore = 50, -- 50% - chanceToGetOminousSoulCore = 5, -- 5% + chanceToGetSameMonsterSoulCore = 15, -- 15% + chanceToDropSoulCore = 10, -- 10% + chanceToGetOminousSoulCore = 3, -- 3% + chanceToDropSoulPrism = 8, -- 8% monsterVariationsSoulCore = { ["Horse"] = "horse soul core (taupe)", ["Brown Horse"] = "horse soul core (brown)", diff --git a/data/scripts/eventcallbacks/monster/ondroploot_soul_core.lua b/data/scripts/eventcallbacks/monster/ondroploot_soul_core.lua index eafa067bc98..aeb086cfd47 100644 --- a/data/scripts/eventcallbacks/monster/ondroploot_soul_core.lua +++ b/data/scripts/eventcallbacks/monster/ondroploot_soul_core.lua @@ -8,7 +8,7 @@ function callback.monsterOnDropLoot(monster, corpse) if not player or not player:canReceiveLoot() then return end - if monster:getMonsterForgeClassification() == FORGE_NORMAL_MONSTER then + if monster:getMonsterForgeClassification() ~= FORGE_FIENDISH_MONSTER then return end @@ -36,15 +36,22 @@ function callback.monsterOnDropLoot(monster, corpse) end if soulCoreId then - lootTable = { - [soulCoreId] = { - count = 1, - }, + lootTable[soulCoreId] = { + count = 1, } else return {} end end + + if math.random(100) < SoulPit.SoulCoresConfiguration.chanceToDropSoulPrism then + local soulPrismId = getItemIdByName("soul prism") + if soulPrismId then + lootTable[soulPrismId] = { + count = 1, + } + end + end corpse:addLoot(mType:generateLootRoll({}, lootTable, player)) end From 9b5285d7c1aeebe731acc66d0ee02fe600a33aea Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Mon, 9 Dec 2024 05:28:55 +0000 Subject: [PATCH 73/95] Code format - (Clang-format) --- src/creatures/monsters/monster.cpp | 4 ++-- src/lua/functions/creatures/monster/monster_functions.cpp | 2 +- src/lua/functions/creatures/monster/monster_functions.hpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/creatures/monsters/monster.cpp b/src/creatures/monsters/monster.cpp index 965ea5805bf..a9bb0756244 100644 --- a/src/creatures/monsters/monster.cpp +++ b/src/creatures/monsters/monster.cpp @@ -1139,7 +1139,7 @@ void Monster::doAttacking(uint32_t interval) { bool inRange = false; if (spellBlock.spell == nullptr || (spellBlock.isMelee && isFleeing())) { - continue; + continue; } if (canUseSpell(myPos, targetPos, spellBlock, interval, inRange, resetTicks)) { @@ -1153,7 +1153,7 @@ void Monster::doAttacking(uint32_t interval) { maxCombatValue = spellBlock.maxCombatValue; if (spellBlock.spell == nullptr) { - continue; + continue; } spellBlock.spell->castSpell(getMonster(), attackedCreature); diff --git a/src/lua/functions/creatures/monster/monster_functions.cpp b/src/lua/functions/creatures/monster/monster_functions.cpp index 83b484d5e53..e3802f02a64 100644 --- a/src/lua/functions/creatures/monster/monster_functions.cpp +++ b/src/lua/functions/creatures/monster/monster_functions.cpp @@ -77,7 +77,7 @@ void MonsterFunctions::init(lua_State* L) { Lua::registerMethod(L, "Monster", "criticalChance", MonsterFunctions::luaMonsterCriticalChance); Lua::registerMethod(L, "Monster", "criticalDamage", MonsterFunctions::luaMonsterCriticalDamage); - + CharmFunctions::init(L); LootFunctions::init(L); MonsterSpellFunctions::init(L); diff --git a/src/lua/functions/creatures/monster/monster_functions.hpp b/src/lua/functions/creatures/monster/monster_functions.hpp index ca8f5848271..a49b234f39c 100644 --- a/src/lua/functions/creatures/monster/monster_functions.hpp +++ b/src/lua/functions/creatures/monster/monster_functions.hpp @@ -82,6 +82,6 @@ class MonsterFunctions { static int luaMonsterImmune(lua_State* L); static int luaMonsterCriticalChance(lua_State* L); static int luaMonsterCriticalDamage(lua_State* L); - + friend class CreatureFunctions; }; From 363524a5c8a56f780a65fbff66a2f3cccadb40ba Mon Sep 17 00:00:00 2001 From: Pedro Henrique Alves Cruz Date: Sat, 4 Jan 2025 22:03:40 -0300 Subject: [PATCH 74/95] perf: condition --- .../spells/monster/soulpit_intensehex.lua | 2 ++ src/creatures/combat/condition.cpp | 23 +++---------------- 2 files changed, 5 insertions(+), 20 deletions(-) diff --git a/data-otservbr-global/scripts/spells/monster/soulpit_intensehex.lua b/data-otservbr-global/scripts/spells/monster/soulpit_intensehex.lua index 2f6b25b0d78..675a5f3b4fc 100644 --- a/data-otservbr-global/scripts/spells/monster/soulpit_intensehex.lua +++ b/data-otservbr-global/scripts/spells/monster/soulpit_intensehex.lua @@ -3,6 +3,8 @@ combat:setParameter(COMBAT_PARAM_EFFECT, CONST_ME_STUN) combat:setParameter(COMBAT_PARAM_DISTANCEEFFECT, CONST_ANI_NONE) local condition = Condition(CONDITION_INTENSEHEX) +condition:setParameter(CONDITION_PARAM_BUFF_DAMAGEDEALT, 50) +condition:setParameter(CONDITION_PARAM_BUFF_HEALINGRECEIVED, 50) condition:setParameter(CONDITION_PARAM_TICKS, 3000) combat:addCondition(condition) diff --git a/src/creatures/combat/condition.cpp b/src/creatures/combat/condition.cpp index fa578765d5c..fa10c49a7cf 100644 --- a/src/creatures/combat/condition.cpp +++ b/src/creatures/combat/condition.cpp @@ -207,8 +207,6 @@ bool Condition::executeCondition(const std::shared_ptr &creature, int3 } std::shared_ptr Condition::createCondition(ConditionId_t id, ConditionType_t type, int32_t ticks, int32_t param /* = 0*/, bool buff /* = false*/, uint32_t subId /* = 0*/, bool isPersistent /* = false*/) { - - std::shared_ptr conditionAttributes = nullptr; switch (type) { case CONDITION_POISON: case CONDITION_FIRE: @@ -239,6 +237,9 @@ std::shared_ptr Condition::createCondition(ConditionId_t id, Conditio case CONDITION_SOUL: return ObjectPool::allocateShared(id, type, ticks, buff, subId); + case CONDITION_LESSERHEX: + case CONDITION_INTENSEHEX: + case CONDITION_GREATERHEX: case CONDITION_ATTRIBUTES: return ObjectPool::allocateShared(id, type, ticks, buff, subId); @@ -254,24 +255,6 @@ std::shared_ptr Condition::createCondition(ConditionId_t id, Conditio case CONDITION_FEARED: return ObjectPool::allocateShared(id, type, ticks, buff, subId); - case CONDITION_LESSERHEX: - conditionAttributes = std::make_shared(id, type, ticks, buff, subId); - conditionAttributes->setParam(CONDITION_PARAM_BUFF_HEALINGRECEIVED, 50); - return conditionAttributes; - - case CONDITION_INTENSEHEX: - conditionAttributes = std::make_shared(id, type, ticks, buff, subId); - conditionAttributes->setParam(CONDITION_PARAM_BUFF_HEALINGRECEIVED, 50); - conditionAttributes->setParam(CONDITION_PARAM_BUFF_DAMAGEDEALT, 50); - return conditionAttributes; - - case CONDITION_GREATERHEX: - conditionAttributes = std::make_shared(id, type, ticks, buff, subId); - conditionAttributes->setParam(CONDITION_PARAM_BUFF_HEALINGRECEIVED, 50); - conditionAttributes->setParam(CONDITION_PARAM_BUFF_DAMAGEDEALT, 50); - conditionAttributes->setParam(CONDITION_PARAM_STAT_MAXHITPOINTSPERCENT, 60); - return conditionAttributes; - case CONDITION_ROOTED: case CONDITION_INFIGHT: case CONDITION_DRUNK: From 84c15c9cce7527b9cfffe95a613d1a6f1393db89 Mon Sep 17 00:00:00 2001 From: Pedro Henrique Alves Cruz Date: Tue, 7 Jan 2025 00:37:38 -0300 Subject: [PATCH 75/95] fix: instace monsters spells and some improvements --- data-otservbr-global/lib/others/soulpit.lua | 19 +------- .../scripts/actions/soulpit/soulpit_fight.lua | 12 +++-- .../monster/soulpit_creatureevents.lua | 4 -- .../spells/monster/soulpit_intensehex.lua | 7 +-- .../spells/monster/soulpit_opressor.lua | 7 +-- .../spells/monster/soulpit_powerless.lua | 7 +-- src/creatures/monsters/monster.cpp | 8 ++-- src/creatures/monsters/monster.hpp | 3 ++ src/creatures/monsters/monsters.hpp | 16 ------- .../functions/core/game/game_functions.cpp | 8 ---- .../creatures/monster/monster_functions.cpp | 47 +++++++++++++++++++ .../creatures/monster/monster_functions.hpp | 3 ++ 12 files changed, 72 insertions(+), 69 deletions(-) diff --git a/data-otservbr-global/lib/others/soulpit.lua b/data-otservbr-global/lib/others/soulpit.lua index 8ae31552317..09bc94af106 100644 --- a/data-otservbr-global/lib/others/soulpit.lua +++ b/data-otservbr-global/lib/others/soulpit.lua @@ -1,5 +1,4 @@ SoulPit = { - registeredMonstersSpell = {}, SoulCoresConfiguration = { chanceToGetSameMonsterSoulCore = 15, -- 15% chanceToDropSoulCore = 10, -- 10% @@ -34,27 +33,22 @@ SoulPit = { { pos = Position(32371, 31155, 8), teleport = Position(32373, 31138, 8), - effect = CONST_ME_TELEPORT, }, { pos = Position(32371, 31156, 8), teleport = Position(32373, 31138, 8), - effect = CONST_ME_TELEPORT, }, { pos = Position(32371, 31157, 8), teleport = Position(32373, 31138, 8), - effect = CONST_ME_TELEPORT, }, { pos = Position(32371, 31158, 8), teleport = Position(32373, 31138, 8), - effect = CONST_ME_TELEPORT, }, { pos = Position(32371, 31159, 8), teleport = Position(32373, 31138, 8), - effect = CONST_ME_TELEPORT, }, }, waves = { @@ -121,22 +115,11 @@ SoulPit = { { name = "soulpit intensehex", interval = 2000, chance = 15, minDamage = 0, maxDamage = 0 }, }, apply = function(monster) - monster:registerEvent("opressorSoulPit") - local monsterType = monster:getType() - local monsterTypeName = monsterType:name() - - -- Checking spells - if SoulPit.registeredMonstersSpell[monsterTypeName] then - return true - end - -- Applying spells for _, spell in pairs(SoulPit.bossAbilities.opressorSoulPit.spells) do - monsterType:addAttack(readSpell(spell, monsterType)) + monster:addAttackSpell(readSpell(spell, monster:getType())) end - SoulPit.registeredMonstersSpell[monsterTypeName] = true - return true end, }, diff --git a/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua b/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua index 0cb2bfd402b..e778386733d 100644 --- a/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua +++ b/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua @@ -116,7 +116,10 @@ function soulPitAction.onUse(player, item, fromPosition, target, toPosition, isH local position = stack ~= 40 and SoulPit.zone:randomPosition() or SoulPit.bossPosition for i = 1, SoulPit.timeToSpawnMonsters / 1000 do encounter:addEvent(function(position) - position:sendMagicEffect(SoulPit.effects[stack]) + local effect = SoulPit.effects[stack] + if effect then + position:sendMagicEffect(effect) + end end, i * 1000, position) end @@ -128,9 +131,12 @@ function soulPitAction.onUse(player, item, fromPosition, target, toPosition, isH if not monster then return false end - if stack == 40 then - bossAbility.apply(monster) + + if stack ~= 40 then + return false end + + bossAbility.apply(monster) end, SoulPit.timeToSpawnMonsters, monsterName, stack, position, randomAbility, chosenBossAbility) end end diff --git a/data-otservbr-global/scripts/creaturescripts/monster/soulpit_creatureevents.lua b/data-otservbr-global/scripts/creaturescripts/monster/soulpit_creatureevents.lua index e2bc9a27cea..118a190bf51 100644 --- a/data-otservbr-global/scripts/creaturescripts/monster/soulpit_creatureevents.lua +++ b/data-otservbr-global/scripts/creaturescripts/monster/soulpit_creatureevents.lua @@ -15,7 +15,3 @@ function enrage.onHealthChange(creature, attacker, primaryDamage, primaryType, s return primaryDamage, primaryType, secondaryDamage, secondaryType end enrage:register() - -local opressor = CreatureEvent("opressorSoulPit") -function opressor.onThink() end -opressor:register() diff --git a/data-otservbr-global/scripts/spells/monster/soulpit_intensehex.lua b/data-otservbr-global/scripts/spells/monster/soulpit_intensehex.lua index 675a5f3b4fc..9dc9f54dfbe 100644 --- a/data-otservbr-global/scripts/spells/monster/soulpit_intensehex.lua +++ b/data-otservbr-global/scripts/spells/monster/soulpit_intensehex.lua @@ -11,12 +11,7 @@ combat:addCondition(condition) local spell = Spell("instant") function spell.onCastSpell(creature, var) - local monster = creature:getMonster() - if monster and monster:soulPit() and not monster:getMaster() and table.contains(creature:getEvents(CREATURE_EVENT_THINK), "opressorSoulPit") then - return combat:execute(creature, var) - end - - return true + return combat:execute(creature, var) end spell:name("soulpit intensehex") diff --git a/data-otservbr-global/scripts/spells/monster/soulpit_opressor.lua b/data-otservbr-global/scripts/spells/monster/soulpit_opressor.lua index b504fcb0116..0b29b4944b5 100644 --- a/data-otservbr-global/scripts/spells/monster/soulpit_opressor.lua +++ b/data-otservbr-global/scripts/spells/monster/soulpit_opressor.lua @@ -26,11 +26,8 @@ local spell = Spell("instant") local combats = { combatRoot, combatFear } function spell.onCastSpell(creature, var) - local monster = creature:getMonster() - if monster and monster:soulPit() and not monster:getMaster() and table.contains(creature:getEvents(CREATURE_EVENT_THINK), "opressorSoulPit") then - for _, combat in pairs(combats) do - combat:execute(creature, var) - end + for _, combat in pairs(combats) do + combat:execute(creature, var) end return true diff --git a/data-otservbr-global/scripts/spells/monster/soulpit_powerless.lua b/data-otservbr-global/scripts/spells/monster/soulpit_powerless.lua index 13f2969eaac..36af89a9860 100644 --- a/data-otservbr-global/scripts/spells/monster/soulpit_powerless.lua +++ b/data-otservbr-global/scripts/spells/monster/soulpit_powerless.lua @@ -9,12 +9,7 @@ combat:addCondition(condition) local spell = Spell("instant") function spell.onCastSpell(creature, var) - local monster = creature:getMonster() - if monster and monster:soulPit() and not monster:getMaster() and table.contains(creature:getEvents(CREATURE_EVENT_THINK), "opressorSoulPit") then - return combat:execute(creature, var) - end - - return true + return combat:execute(creature, var) end spell:name("soulpit powerless") diff --git a/src/creatures/monsters/monster.cpp b/src/creatures/monsters/monster.cpp index a9bb0756244..ae848ee0fd6 100644 --- a/src/creatures/monsters/monster.cpp +++ b/src/creatures/monsters/monster.cpp @@ -48,6 +48,8 @@ Monster::Monster(const std::shared_ptr &mType) : internalLight = mType->info.light; hiddenHealth = mType->info.hiddenHealth; targetDistance = mType->info.targetDistance; + attackSpells = mType->info.attackSpells; + defenseSpells = mType->info.defenseSpells; // Register creature events for (const std::string &scriptName : mType->info.scripts) { @@ -1135,7 +1137,7 @@ void Monster::doAttacking(uint32_t interval) { const Position &myPos = getPosition(); const Position &targetPos = attackedCreature->getPosition(); - for (const spellBlock_t &spellBlock : mType->info.attackSpells) { + for (const spellBlock_t &spellBlock : attackSpells) { bool inRange = false; if (spellBlock.spell == nullptr || (spellBlock.isMelee && isFleeing())) { @@ -1187,7 +1189,7 @@ bool Monster::canUseAttack(const Position &pos, const std::shared_ptr if (isHostile()) { const Position &targetPos = target->getPosition(); uint32_t distance = std::max(Position::getDistanceX(pos, targetPos), Position::getDistanceY(pos, targetPos)); - for (const spellBlock_t &spellBlock : mType->info.attackSpells) { + for (const spellBlock_t &spellBlock : attackSpells) { if (spellBlock.range != 0 && distance <= spellBlock.range) { return g_game().isSightClear(pos, targetPos, true); } @@ -1282,7 +1284,7 @@ void Monster::onThinkDefense(uint32_t interval) { bool resetTicks = true; defenseTicks += interval; - for (const spellBlock_t &spellBlock : mType->info.defenseSpells) { + for (const spellBlock_t &spellBlock : defenseSpells) { if (spellBlock.speed > defenseTicks) { resetTicks = false; continue; diff --git a/src/creatures/monsters/monster.hpp b/src/creatures/monsters/monster.hpp index b904769130c..89305e7f5f7 100644 --- a/src/creatures/monsters/monster.hpp +++ b/src/creatures/monsters/monster.hpp @@ -290,6 +290,9 @@ class Monster final : public Creature { std::unordered_map m_reflectElementMap; + std::vector attackSpells; + std::vector defenseSpells; + Position masterPos; bool isWalkingBack = false; diff --git a/src/creatures/monsters/monsters.hpp b/src/creatures/monsters/monsters.hpp index 979b2ffcc01..146f53e604c 100644 --- a/src/creatures/monsters/monsters.hpp +++ b/src/creatures/monsters/monsters.hpp @@ -30,22 +30,6 @@ class Loot { class BaseSpell; struct spellBlock_t { - constexpr spellBlock_t() = default; - ~spellBlock_t() = default; - spellBlock_t(const spellBlock_t &other) = delete; - spellBlock_t &operator=(const spellBlock_t &other) = delete; - spellBlock_t(spellBlock_t &&other) noexcept : - spell(std::move(other.spell)), - chance(other.chance), - speed(other.speed), - range(other.range), - minCombatValue(other.minCombatValue), - maxCombatValue(other.maxCombatValue), - combatSpell(other.combatSpell), - isMelee(other.isMelee) { - other.spell = nullptr; - } - std::shared_ptr spell = nullptr; uint32_t chance = 100; uint32_t speed = 2000; diff --git a/src/lua/functions/core/game/game_functions.cpp b/src/lua/functions/core/game/game_functions.cpp index 8619ff814f6..acdf0c4202b 100644 --- a/src/lua/functions/core/game/game_functions.cpp +++ b/src/lua/functions/core/game/game_functions.cpp @@ -572,14 +572,6 @@ int GameFunctions::luaGameCreateSoulPitMonster(lua_State* L) { if (g_game().placeCreature(monster, position, extended, force)) { monster->setSoulPitStack(stack); monster->onSpawn(); - const auto &mtype = monster->getMonsterType(); - if (mtype && mtype->info.raceid > 0 && mtype->info.bosstiaryRace == BosstiaryRarity_t::RARITY_ARCHFOE) { - for (const auto &spectator : Spectators().find(monster->getPosition(), true)) { - if (const auto &tmpPlayer = spectator->getPlayer()) { - tmpPlayer->sendBosstiaryCooldownTimer(); - } - } - } Lua::pushUserdata(L, monster); Lua::setMetatable(L, -1, "Monster"); diff --git a/src/lua/functions/creatures/monster/monster_functions.cpp b/src/lua/functions/creatures/monster/monster_functions.cpp index e3802f02a64..7d73d7bbea9 100644 --- a/src/lua/functions/creatures/monster/monster_functions.cpp +++ b/src/lua/functions/creatures/monster/monster_functions.cpp @@ -78,6 +78,9 @@ void MonsterFunctions::init(lua_State* L) { Lua::registerMethod(L, "Monster", "criticalChance", MonsterFunctions::luaMonsterCriticalChance); Lua::registerMethod(L, "Monster", "criticalDamage", MonsterFunctions::luaMonsterCriticalDamage); + Lua::registerMethod(L, "Monster", "addAttackSpell", MonsterFunctions::luaMonsterAddAttackSpell); + Lua::registerMethod(L, "Monster", "addDefenseSpell", MonsterFunctions::luaMonsterAddDefenseSpell); + CharmFunctions::init(L); LootFunctions::init(L); MonsterSpellFunctions::init(L); @@ -828,3 +831,47 @@ int MonsterFunctions::luaMonsterCriticalDamage(lua_State* L) { } return 1; } + +int MonsterFunctions::luaMonsterAddAttackSpell(lua_State* L) { + // monster:addAttackSpell(monsterspell) + const auto &monster = Lua::getUserdataShared(L, 1); + if (monster) { + const auto &spell = Lua::getUserdataShared(L, 2); + if (spell) { + spellBlock_t sb; + const auto &monsterName = monster->getName(); + if (g_monsters().deserializeSpell(spell, sb, monsterName)) { + monster->attackSpells.push_back(std::move(sb)); + } else { + g_logger().warn("Monster: {}, cant load spell: {}", monsterName, spell->name); + } + } else { + lua_pushnil(L); + } + } else { + lua_pushnil(L); + } + return 1; +} + +int MonsterFunctions::luaMonsterAddDefenseSpell(lua_State* L) { + // monster:addDefenseSpell(monsterspell) + const auto &monster = Lua::getUserdataShared(L, 1); + if (monster) { + const auto &spell = Lua::getUserdataShared(L, 2); + if (spell) { + spellBlock_t sb; + const auto &monsterName = monster->getName(); + if (g_monsters().deserializeSpell(spell, sb, monsterName)) { + monster->defenseSpells.push_back(std::move(sb)); + } else { + g_logger().warn("Monster: {}, Cant load spell: {}", monsterName, spell->name); + } + } else { + lua_pushnil(L); + } + } else { + lua_pushnil(L); + } + return 1; +} diff --git a/src/lua/functions/creatures/monster/monster_functions.hpp b/src/lua/functions/creatures/monster/monster_functions.hpp index a49b234f39c..a7c6a5269e1 100644 --- a/src/lua/functions/creatures/monster/monster_functions.hpp +++ b/src/lua/functions/creatures/monster/monster_functions.hpp @@ -83,5 +83,8 @@ class MonsterFunctions { static int luaMonsterCriticalChance(lua_State* L); static int luaMonsterCriticalDamage(lua_State* L); + static int luaMonsterAddAttackSpell(lua_State* L); + static int luaMonsterAddDefenseSpell(lua_State* L); + friend class CreatureFunctions; }; From 5ca3287e8f3e9e450d80b914f358ae67d086a4bd Mon Sep 17 00:00:00 2001 From: Pedro Henrique Alves Cruz Date: Tue, 7 Jan 2025 18:49:14 -0300 Subject: [PATCH 76/95] fix: bestiary race and stars monsterType to export in Game instead of MonsterType --- .../functions/core/game/game_functions.cpp | 33 +++++++++++++++++++ .../functions/core/game/game_functions.hpp | 3 ++ 2 files changed, 36 insertions(+) diff --git a/src/lua/functions/core/game/game_functions.cpp b/src/lua/functions/core/game/game_functions.cpp index acdf0c4202b..99d36df60ba 100644 --- a/src/lua/functions/core/game/game_functions.cpp +++ b/src/lua/functions/core/game/game_functions.cpp @@ -110,6 +110,9 @@ void GameFunctions::init(lua_State* L) { Lua::registerMethod(L, "Game", "getAchievements", GameFunctions::luaGameGetAchievements); Lua::registerMethod(L, "Game", "getSoulCoreItems", GameFunctions::luaGameGetSoulCoreItems); + + Lua::registerMethod(L, "Game", "getMonstersByRace", GameFunctions::luaGameGetMonstersByRace); + Lua::registerMethod(L, "Game", "getMonstersByBestiaryStars", GameFunctions::luaGameGetMonstersByBestiaryStars); } // Game @@ -1055,3 +1058,33 @@ int GameFunctions::luaGameGetSoulCoreItems(lua_State* L) { return 1; } + +int GameFunctions::luaGameGetMonstersByRace(lua_State* L) { + // Game.getMonstersByRace(race) + const BestiaryType_t race = Lua::getNumber(L, 1); + const auto monstersByRace = g_monsters().getMonstersByRace(race); + + lua_createtable(L, monstersByRace.size(), 0); + int index = 0; + for (const auto &monsterType : monstersByRace) { + Lua::pushUserdata(L, monsterType); + Lua::setMetatable(L, -1, "MonsterType"); + lua_rawseti(L, -2, ++index); + } + return 1; +} + +int GameFunctions::luaGameGetMonstersByBestiaryStars(lua_State* L) { + // Game.getMonstersByBestiaryStars(stars) + const uint8_t stars = Lua::getNumber(L, 1); + const auto monstersByStars = g_monsters().getMonstersByBestiaryStars(stars); + + lua_createtable(L, monstersByStars.size(), 0); + int index = 0; + for (const auto &monsterType : monstersByStars) { + Lua::pushUserdata(L, monsterType); + Lua::setMetatable(L, -1, "MonsterType"); + lua_rawseti(L, -2, ++index); + } + return 1; +} diff --git a/src/lua/functions/core/game/game_functions.hpp b/src/lua/functions/core/game/game_functions.hpp index e70c7c0f6f1..b24b960368c 100644 --- a/src/lua/functions/core/game/game_functions.hpp +++ b/src/lua/functions/core/game/game_functions.hpp @@ -91,4 +91,7 @@ class GameFunctions { static int luaGameGetAchievements(lua_State* L); static int luaGameGetSoulCoreItems(lua_State* L); + + static int luaGameGetMonstersByRace(lua_State* L); + static int luaGameGetMonstersByBestiaryStars(lua_State* L); }; From 603f4983fa37929588430a768da853d47a3e94d6 Mon Sep 17 00:00:00 2001 From: Felipe Paluco Date: Tue, 7 Jan 2025 19:27:58 -0300 Subject: [PATCH 77/95] chore: remove log and adjust drop chances --- data-otservbr-global/lib/others/soulpit.lua | 6 +++--- .../scripts/eventcallbacks/monster/ondroploot_soul_core.lua | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/data-otservbr-global/lib/others/soulpit.lua b/data-otservbr-global/lib/others/soulpit.lua index 09bc94af106..63ab44001e9 100644 --- a/data-otservbr-global/lib/others/soulpit.lua +++ b/data-otservbr-global/lib/others/soulpit.lua @@ -1,9 +1,9 @@ SoulPit = { SoulCoresConfiguration = { chanceToGetSameMonsterSoulCore = 15, -- 15% - chanceToDropSoulCore = 10, -- 10% - chanceToGetOminousSoulCore = 3, -- 3% - chanceToDropSoulPrism = 8, -- 8% + chanceToDropSoulCore = 5, -- 5% + chanceToGetOminousSoulCore = 2, -- 2% + chanceToDropSoulPrism = 4, -- 4% monsterVariationsSoulCore = { ["Horse"] = "horse soul core (taupe)", ["Brown Horse"] = "horse soul core (brown)", diff --git a/data/scripts/eventcallbacks/monster/ondroploot_soul_core.lua b/data/scripts/eventcallbacks/monster/ondroploot_soul_core.lua index aeb086cfd47..dc5784830b5 100644 --- a/data/scripts/eventcallbacks/monster/ondroploot_soul_core.lua +++ b/data/scripts/eventcallbacks/monster/ondroploot_soul_core.lua @@ -31,7 +31,6 @@ function callback.monsterOnDropLoot(monster, corpse) local randomMonster = monstersInCategory[math.random(#monstersInCategory)] local itemName = randomMonster:name():lower() .. " soul core" soulCoreId = getItemIdByName(itemName) - logger.info("soulcoreId: " .. soulCoreId) end end From 9b807ce73fe6266fac27beadce21b653a6802490 Mon Sep 17 00:00:00 2001 From: Felipe Paluco Date: Wed, 8 Jan 2025 18:10:23 -0300 Subject: [PATCH 78/95] feat: add soulpit move events and actions --- .../actions/soulpit/soulpit_arena_exit.lua | 17 ++++++ .../actions/soulpit/soulpit_entrance.lua | 58 +++++++++++++++++++ 2 files changed, 75 insertions(+) create mode 100644 data-otservbr-global/scripts/actions/soulpit/soulpit_arena_exit.lua create mode 100644 data-otservbr-global/scripts/actions/soulpit/soulpit_entrance.lua diff --git a/data-otservbr-global/scripts/actions/soulpit/soulpit_arena_exit.lua b/data-otservbr-global/scripts/actions/soulpit/soulpit_arena_exit.lua new file mode 100644 index 00000000000..7636ca99d2d --- /dev/null +++ b/data-otservbr-global/scripts/actions/soulpit/soulpit_arena_exit.lua @@ -0,0 +1,17 @@ +local soulpitArenaExitConfig = { + arenaExit = Position(32375, 31153, 8), + destination = Position(32373, 31158, 8) +} + +local soulpitArenaExit = Action() + +function soulpitArenaExit.onUse(player, item, fromPosition, target, toPosition, isHotkey) + if not player then + return false + end + player:getPosition():sendMagicEffect(CONST_ME_TELEPORT) + player:teleportTo(soulpitArenaExitConfig.destination) +end + +soulpitArenaExit:position(soulpitArenaExitConfig.arenaExit) +soulpitArenaExit:register() \ No newline at end of file diff --git a/data-otservbr-global/scripts/actions/soulpit/soulpit_entrance.lua b/data-otservbr-global/scripts/actions/soulpit/soulpit_entrance.lua new file mode 100644 index 00000000000..decb5c1af7c --- /dev/null +++ b/data-otservbr-global/scripts/actions/soulpit/soulpit_entrance.lua @@ -0,0 +1,58 @@ +local config = { + entrance = { + positions = { + Position(32350, 31030, 3), + Position(32349, 31030, 3), + }, + destination = Position(32374, 31171, 8) + }, + exit = { + position = Position(32374, 31173, 8), + destination = Position(32349, 31032, 3) + } +} + +local soulpitEntrance = MoveEvent() + +function soulpitEntrance.onStepIn(creature, item, position, fromPosition) + local player = creature:getPlayer() + if not player then + return true + end + + if not config.entrance.destination then + return true + end + + player:teleportTo(config.entrance.destination) + position:sendMagicEffect(CONST_ME_TELEPORT) + return true +end + +soulpitEntrance:type("stepin") +for value in pairs(config.entrance.positions) do + soulpitEntrance:position(config.entrance.positions[value]) +end +soulpitEntrance:register() + + +local soulpitExit = MoveEvent() + +function soulpitExit.onStepIn(creature, item, position, fromPosition) + local player = creature:getPlayer() + if not player then + return true + end + + if not config.exit then + return true + end + + player:teleportTo(config.exit.destination) + position:sendMagicEffect(CONST_ME_TELEPORT) + return true +end + +soulpitExit:type("stepin") +soulpitExit:position(config.exit.position) +soulpitExit:register() \ No newline at end of file From 3a75a560ed15f45ad9fa0e507e8c0deda36b2a0d Mon Sep 17 00:00:00 2001 From: Felipe Paluco Date: Wed, 8 Jan 2025 18:10:43 -0300 Subject: [PATCH 79/95] fix: soulpit positions and obelisk id --- data-otservbr-global/lib/others/soulpit.lua | 34 ++++++++++----------- data/items/items.xml | 4 +-- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/data-otservbr-global/lib/others/soulpit.lua b/data-otservbr-global/lib/others/soulpit.lua index 63ab44001e9..dcd6f7a9d3c 100644 --- a/data-otservbr-global/lib/others/soulpit.lua +++ b/data-otservbr-global/lib/others/soulpit.lua @@ -30,25 +30,25 @@ SoulPit = { soulCores = Game.getSoulCoreItems(), requiredLevel = 8, playerPositions = { - { - pos = Position(32371, 31155, 8), - teleport = Position(32373, 31138, 8), + { + pos = Position(32375, 31158, 8), + teleport = Position(32373, 31151, 8), }, { - pos = Position(32371, 31156, 8), - teleport = Position(32373, 31138, 8), + pos = Position(32375, 31159, 8), + teleport = Position(32374, 31151, 8), }, { - pos = Position(32371, 31157, 8), - teleport = Position(32373, 31138, 8), + pos = Position(32375, 31160, 8), + teleport = Position(32375, 31151, 8), }, { - pos = Position(32371, 31158, 8), - teleport = Position(32373, 31138, 8), + pos = Position(32375, 31161, 8), + teleport = Position(32376, 31151, 8), }, { - pos = Position(32371, 31159, 8), - teleport = Position(32373, 31138, 8), + pos = Position(32375, 31162, 8), + teleport = Position(32377, 31151, 8), }, }, waves = { @@ -128,11 +128,11 @@ SoulPit = { checkMonstersDelay = 4.5 * 1000, -- 4.5 seconds | The check delay should never be less than the timeToSpawnMonsters. timeToSpawnMonsters = 4 * 1000, -- 4 seconds totalMonsters = 7, - obeliskActive = 49175, - obeliskInactive = 49174, - obeliskPosition = Position(32371, 31154, 8), - bossPosition = Position(32372, 31135, 8), - exit = Position(32371, 31164, 8), + obeliskActive = 47379, + obeliskInactive = 47367, + obeliskPosition = Position(32375, 31157, 8), + bossPosition = Position(32376, 31144, 8), + exit = Position(32373, 31158, 8), zone = Zone("soulpit"), getMonsterVariationNameBySoulCore = function(searchName) @@ -165,5 +165,5 @@ SoulPit = { end, } -SoulPit.zone:addArea(Position(32365, 31134, 8), Position(32382, 31152, 8)) +SoulPit.zone:addArea(Position(32362, 31132, 8), Position(32390, 31153, 8)) SoulPit.zone:setRemoveDestination(SoulPit.exit) diff --git a/data/items/items.xml b/data/items/items.xml index cb62f819edc..ebb6f6402dc 100644 --- a/data/items/items.xml +++ b/data/items/items.xml @@ -80130,10 +80130,10 @@ Granted by TibiaGoals.com"/> - + - + From 652ab98d968643cbe1f36b78c137546507db592c Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Wed, 8 Jan 2025 21:16:54 +0000 Subject: [PATCH 80/95] Lua code format - (Stylua) --- data-otservbr-global/lib/others/soulpit.lua | 2 +- .../scripts/actions/soulpit/soulpit_arena_exit.lua | 10 +++++----- .../scripts/actions/soulpit/soulpit_entrance.lua | 9 ++++----- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/data-otservbr-global/lib/others/soulpit.lua b/data-otservbr-global/lib/others/soulpit.lua index dcd6f7a9d3c..572ab2626d9 100644 --- a/data-otservbr-global/lib/others/soulpit.lua +++ b/data-otservbr-global/lib/others/soulpit.lua @@ -30,7 +30,7 @@ SoulPit = { soulCores = Game.getSoulCoreItems(), requiredLevel = 8, playerPositions = { - { + { pos = Position(32375, 31158, 8), teleport = Position(32373, 31151, 8), }, diff --git a/data-otservbr-global/scripts/actions/soulpit/soulpit_arena_exit.lua b/data-otservbr-global/scripts/actions/soulpit/soulpit_arena_exit.lua index 7636ca99d2d..21304d0cfb5 100644 --- a/data-otservbr-global/scripts/actions/soulpit/soulpit_arena_exit.lua +++ b/data-otservbr-global/scripts/actions/soulpit/soulpit_arena_exit.lua @@ -1,6 +1,6 @@ local soulpitArenaExitConfig = { - arenaExit = Position(32375, 31153, 8), - destination = Position(32373, 31158, 8) + arenaExit = Position(32375, 31153, 8), + destination = Position(32373, 31158, 8), } local soulpitArenaExit = Action() @@ -9,9 +9,9 @@ function soulpitArenaExit.onUse(player, item, fromPosition, target, toPosition, if not player then return false end - player:getPosition():sendMagicEffect(CONST_ME_TELEPORT) - player:teleportTo(soulpitArenaExitConfig.destination) + player:getPosition():sendMagicEffect(CONST_ME_TELEPORT) + player:teleportTo(soulpitArenaExitConfig.destination) end soulpitArenaExit:position(soulpitArenaExitConfig.arenaExit) -soulpitArenaExit:register() \ No newline at end of file +soulpitArenaExit:register() diff --git a/data-otservbr-global/scripts/actions/soulpit/soulpit_entrance.lua b/data-otservbr-global/scripts/actions/soulpit/soulpit_entrance.lua index decb5c1af7c..d31f63fe386 100644 --- a/data-otservbr-global/scripts/actions/soulpit/soulpit_entrance.lua +++ b/data-otservbr-global/scripts/actions/soulpit/soulpit_entrance.lua @@ -4,12 +4,12 @@ local config = { Position(32350, 31030, 3), Position(32349, 31030, 3), }, - destination = Position(32374, 31171, 8) + destination = Position(32374, 31171, 8), }, exit = { position = Position(32374, 31173, 8), - destination = Position(32349, 31032, 3) - } + destination = Position(32349, 31032, 3), + }, } local soulpitEntrance = MoveEvent() @@ -35,7 +35,6 @@ for value in pairs(config.entrance.positions) do end soulpitEntrance:register() - local soulpitExit = MoveEvent() function soulpitExit.onStepIn(creature, item, position, fromPosition) @@ -55,4 +54,4 @@ end soulpitExit:type("stepin") soulpitExit:position(config.exit.position) -soulpitExit:register() \ No newline at end of file +soulpitExit:register() From 3e99b3bc2e95af26ddb6d5fb91b8f17c8fde99ee Mon Sep 17 00:00:00 2001 From: Felipe Paluco Date: Wed, 8 Jan 2025 18:33:46 -0300 Subject: [PATCH 81/95] fix: conflicts with migrations --- config.lua.dist | 2 +- data-otservbr-global/migrations/49.lua | 5 +++++ src/lua/functions/core/game/game_functions.cpp | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 data-otservbr-global/migrations/49.lua diff --git a/config.lua.dist b/config.lua.dist index 0a5c2280584..9661a4cb7e0 100644 --- a/config.lua.dist +++ b/config.lua.dist @@ -86,7 +86,7 @@ maxItem = 5000 maxContainer = 500 maxContainerDepth = 200 --- Animus Mastery - SoulPit (Get more info in: TBD) +-- Animus Mastery - SoulPit (Get more info in: https://github.com/opentibiabr/canary/pull/3230) -- NOTE: animusMasteryMaxMonsterXpMultiplier is the maximum experience the multiplier can be. -- NOTE: animusMasteryMonsterXpMultiplier is the monster experience multiplier that has the animus mastery unlocked. -- NOTE: animusMasteryMonstersXpMultiplier is the multiplier for each 'animusMasteryMonstersToIncreaseXpMultiplier' monsters that diff --git a/data-otservbr-global/migrations/49.lua b/data-otservbr-global/migrations/49.lua new file mode 100644 index 00000000000..d51eb8f04cf --- /dev/null +++ b/data-otservbr-global/migrations/49.lua @@ -0,0 +1,5 @@ +function onUpdateDatabase() + logger.info("Updating database to version 49 (feat: animus mastery (soulpit))") + + db.query("ALTER TABLE `players` ADD `animus_mastery` mediumblob NOT NULL;") +end diff --git a/src/lua/functions/core/game/game_functions.cpp b/src/lua/functions/core/game/game_functions.cpp index 99d36df60ba..dc51ef15c41 100644 --- a/src/lua/functions/core/game/game_functions.cpp +++ b/src/lua/functions/core/game/game_functions.cpp @@ -574,7 +574,7 @@ int GameFunctions::luaGameCreateSoulPitMonster(lua_State* L) { const bool force = Lua::getBoolean(L, 5, false); if (g_game().placeCreature(monster, position, extended, force)) { monster->setSoulPitStack(stack); - monster->onSpawn(); + monster->onSpawn(position); Lua::pushUserdata(L, monster); Lua::setMetatable(L, -1, "Monster"); From 4bc4095302d52eb03893096ca23a5086f109c7a6 Mon Sep 17 00:00:00 2001 From: Pedro Henrique Alves Cruz Date: Thu, 9 Jan 2025 22:22:21 -0300 Subject: [PATCH 82/95] fix: change to scoped and fix ubuntu pipeline --- .../scripts/quests/soulpit}/exalted_core.lua | 6 +++--- .../scripts/quests/soulpit}/ondroploot_soul_core.lua | 2 +- .../scripts/quests/soulpit}/soul_prism.lua | 6 +++--- .../soulpit}/soulpit_creatureevents.lua | 0 .../{actions => quests}/soulpit/soulpit_fight.lua | 0 .../soulpit}/soulpit_intensehex.lua | 0 .../monster => quests/soulpit}/soulpit_opressor.lua | 0 .../monster => quests/soulpit}/soulpit_powerless.lua | 0 src/creatures/combat/spells.cpp | 4 ++-- src/creatures/combat/spells.hpp | 4 ++-- .../players/animus_mastery/animus_mastery.cpp | 6 +++++- src/creatures/players/player.cpp | 12 ++++++------ src/creatures/players/player.hpp | 7 ++++--- src/io/functions/iologindata_load_player.cpp | 2 +- src/io/functions/iologindata_save_player.cpp | 2 +- src/lua/functions/core/game/game_functions.cpp | 1 - .../functions/creatures/player/player_functions.cpp | 6 +++--- src/server/network/protocol/protocolgame.cpp | 4 ++-- 18 files changed, 33 insertions(+), 29 deletions(-) rename {data/scripts/actions/items => data-otservbr-global/scripts/quests/soulpit}/exalted_core.lua (89%) rename {data/scripts/eventcallbacks/monster => data-otservbr-global/scripts/quests/soulpit}/ondroploot_soul_core.lua (96%) rename {data/scripts/actions/items => data-otservbr-global/scripts/quests/soulpit}/soul_prism.lua (90%) rename data-otservbr-global/scripts/{creaturescripts/monster => quests/soulpit}/soulpit_creatureevents.lua (100%) rename data-otservbr-global/scripts/{actions => quests}/soulpit/soulpit_fight.lua (100%) rename data-otservbr-global/scripts/{spells/monster => quests/soulpit}/soulpit_intensehex.lua (100%) rename data-otservbr-global/scripts/{spells/monster => quests/soulpit}/soulpit_opressor.lua (100%) rename data-otservbr-global/scripts/{spells/monster => quests/soulpit}/soulpit_powerless.lua (100%) diff --git a/data/scripts/actions/items/exalted_core.lua b/data-otservbr-global/scripts/quests/soulpit/exalted_core.lua similarity index 89% rename from data/scripts/actions/items/exalted_core.lua rename to data-otservbr-global/scripts/quests/soulpit/exalted_core.lua index 7a4c778589a..029c0d33840 100644 --- a/data/scripts/actions/items/exalted_core.lua +++ b/data-otservbr-global/scripts/quests/soulpit/exalted_core.lua @@ -46,15 +46,15 @@ function exaltedCore.onUse(player, item, fromPosition, target, toPosition, isHot return false end - local currentDifficulty = monsterType:getBestiaryStars() + local currentDifficulty = monsterType:BestiaryStars() local previousDifficultyLevel = getPreviousDifficultyLevel(currentDifficulty) local previousDifficultyMonsters = nil if previousDifficultyLevel then - previousDifficultyMonsters = monsterType:getMonstersByBestiaryStars(SoulPit.SoulCoresConfiguration.monstersDifficulties[previousDifficultyLevel]) + previousDifficultyMonsters = Game.getMonstersByBestiaryStars(SoulPit.SoulCoresConfiguration.monstersDifficulties[previousDifficultyLevel]) else previousDifficultyLevel = currentDifficulty - previousDifficultyMonsters = monsterType:getMonstersByBestiaryStars(SoulPit.SoulCoresConfiguration.monstersDifficulties[currentDifficulty]) + previousDifficultyMonsters = Game.getMonstersByBestiaryStars(SoulPit.SoulCoresConfiguration.monstersDifficulties[currentDifficulty]) end if #previousDifficultyMonsters == 0 then diff --git a/data/scripts/eventcallbacks/monster/ondroploot_soul_core.lua b/data-otservbr-global/scripts/quests/soulpit/ondroploot_soul_core.lua similarity index 96% rename from data/scripts/eventcallbacks/monster/ondroploot_soul_core.lua rename to data-otservbr-global/scripts/quests/soulpit/ondroploot_soul_core.lua index aeb086cfd47..9ddb479c117 100644 --- a/data/scripts/eventcallbacks/monster/ondroploot_soul_core.lua +++ b/data-otservbr-global/scripts/quests/soulpit/ondroploot_soul_core.lua @@ -25,7 +25,7 @@ function callback.monsterOnDropLoot(monster, corpse) if not soulCoreId and not trySameMonsterSoulCore then local race = mType:Bestiaryrace() - local monstersInCategory = mType:getMonstersByRace(race) + local monstersInCategory = Game.getMonstersByRace(race) if monstersInCategory and #monstersInCategory > 0 then local randomMonster = monstersInCategory[math.random(#monstersInCategory)] diff --git a/data/scripts/actions/items/soul_prism.lua b/data-otservbr-global/scripts/quests/soulpit/soul_prism.lua similarity index 90% rename from data/scripts/actions/items/soul_prism.lua rename to data-otservbr-global/scripts/quests/soulpit/soul_prism.lua index 190ad086098..e88243938ca 100644 --- a/data/scripts/actions/items/soul_prism.lua +++ b/data-otservbr-global/scripts/quests/soulpit/soul_prism.lua @@ -55,15 +55,15 @@ function soulPrism.onUse(player, item, fromPosition, target, toPosition, isHotke return false end - local currentDifficulty = monsterType:getBestiaryStars() + local currentDifficulty = monsterType:BestiaryStars() local nextDifficultyLevel = getNextDifficultyLevel(currentDifficulty) local nextDifficultyMonsters = nil if nextDifficultyLevel then - nextDifficultyMonsters = monsterType:getMonstersByBestiaryStars(SoulPit.SoulCoresConfiguration.monstersDifficulties[nextDifficultyLevel]) + nextDifficultyMonsters = Game.getMonstersByBestiaryStars(SoulPit.SoulCoresConfiguration.monstersDifficulties[nextDifficultyLevel]) else nextDifficultyLevel = currentDifficulty - nextDifficultyMonsters = monsterType:getMonstersByBestiaryStars(SoulPit.SoulCoresConfiguration.monstersDifficulties[currentDifficulty]) + nextDifficultyMonsters = Game.getMonstersByBestiaryStars(SoulPit.SoulCoresConfiguration.monstersDifficulties[currentDifficulty]) end if #nextDifficultyMonsters == 0 then diff --git a/data-otservbr-global/scripts/creaturescripts/monster/soulpit_creatureevents.lua b/data-otservbr-global/scripts/quests/soulpit/soulpit_creatureevents.lua similarity index 100% rename from data-otservbr-global/scripts/creaturescripts/monster/soulpit_creatureevents.lua rename to data-otservbr-global/scripts/quests/soulpit/soulpit_creatureevents.lua diff --git a/data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua b/data-otservbr-global/scripts/quests/soulpit/soulpit_fight.lua similarity index 100% rename from data-otservbr-global/scripts/actions/soulpit/soulpit_fight.lua rename to data-otservbr-global/scripts/quests/soulpit/soulpit_fight.lua diff --git a/data-otservbr-global/scripts/spells/monster/soulpit_intensehex.lua b/data-otservbr-global/scripts/quests/soulpit/soulpit_intensehex.lua similarity index 100% rename from data-otservbr-global/scripts/spells/monster/soulpit_intensehex.lua rename to data-otservbr-global/scripts/quests/soulpit/soulpit_intensehex.lua diff --git a/data-otservbr-global/scripts/spells/monster/soulpit_opressor.lua b/data-otservbr-global/scripts/quests/soulpit/soulpit_opressor.lua similarity index 100% rename from data-otservbr-global/scripts/spells/monster/soulpit_opressor.lua rename to data-otservbr-global/scripts/quests/soulpit/soulpit_opressor.lua diff --git a/data-otservbr-global/scripts/spells/monster/soulpit_powerless.lua b/data-otservbr-global/scripts/quests/soulpit/soulpit_powerless.lua similarity index 100% rename from data-otservbr-global/scripts/spells/monster/soulpit_powerless.lua rename to data-otservbr-global/scripts/quests/soulpit/soulpit_powerless.lua diff --git a/src/creatures/combat/spells.cpp b/src/creatures/combat/spells.cpp index 8365428ef94..2d611f3bfa6 100644 --- a/src/creatures/combat/spells.cpp +++ b/src/creatures/combat/spells.cpp @@ -931,7 +931,7 @@ void Spell::addVocMap(uint16_t vocationId, bool b) { vocSpellMap[vocationId] = b; } -SpellGroup_t Spell::getGroup() { +SpellGroup_t Spell::getGroup() const { return group; } @@ -1053,7 +1053,7 @@ void Spell::setLockedPZ(bool b) { InstantSpell::InstantSpell() = default; -bool InstantSpell::playerCastInstant(const std::shared_ptr &player, std::string ¶m) { +bool InstantSpell::playerCastInstant(const std::shared_ptr &player, std::string ¶m) const { if (!playerSpellCheck(player)) { return false; } diff --git a/src/creatures/combat/spells.hpp b/src/creatures/combat/spells.hpp index 0dc9fa72efa..e7b8a2a6183 100644 --- a/src/creatures/combat/spells.hpp +++ b/src/creatures/combat/spells.hpp @@ -150,7 +150,7 @@ class Spell : public BaseSpell { [[nodiscard]] const VocSpellMap &getVocMap() const; void addVocMap(uint16_t vocationId, bool b); - SpellGroup_t getGroup(); + SpellGroup_t getGroup() const; void setGroup(SpellGroup_t g); SpellGroup_t getSecondaryGroup(); void setSecondaryGroup(SpellGroup_t g); @@ -280,7 +280,7 @@ class Spell : public BaseSpell { class InstantSpell final : public Spell { public: InstantSpell(); - bool playerCastInstant(const std::shared_ptr &player, std::string ¶m); + bool playerCastInstant(const std::shared_ptr &player, std::string ¶m) const; bool castSpell(const std::shared_ptr &creature) override; bool castSpell(const std::shared_ptr &creature, const std::shared_ptr &target) override; diff --git a/src/creatures/players/animus_mastery/animus_mastery.cpp b/src/creatures/players/animus_mastery/animus_mastery.cpp index d6f7b2f0459..595c98ac81e 100644 --- a/src/creatures/players/animus_mastery/animus_mastery.cpp +++ b/src/creatures/players/animus_mastery/animus_mastery.cpp @@ -7,9 +7,12 @@ * Website: https://docs.opentibiabr.com/ */ -#include "creatures/players/animus_mastery/animus_mastery.hpp" +// Player.hpp already includes the animus mastery #include "creatures/players/player.hpp" + #include "config/configmanager.hpp" +#include "io/fileloader.hpp" +#include "utils/tools.hpp" AnimusMastery::AnimusMastery(Player &player) : m_player(player) { @@ -65,4 +68,5 @@ bool AnimusMastery::unserialize(PropStream &propStream) { while (propStream.readString(monsterName)) { animusMasteries.emplace_back(monsterName); } + return true; } diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index 836316f9785..b3a24f23fac 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -72,14 +72,14 @@ Player::Player(std::shared_ptr p) : lastPing(OTSYS_TIME()), lastPong(lastPing), inbox(std::make_shared(ITEM_INBOX)), - client(std::move(p)) { + client(std::move(p)), + m_animusMastery(*this) { m_playerVIP = std::make_unique(*this); m_wheelPlayer = std::make_unique(*this); m_playerAchievement = std::make_unique(*this); m_playerBadge = std::make_unique(*this); m_playerCyclopedia = std::make_unique(*this); m_playerTitle = std::make_unique(*this); - m_animusMastery = std::make_unique(*this); } Player::~Player() { @@ -3116,11 +3116,11 @@ void Player::addExperience(const std::shared_ptr &target, uint64_t exp exp += (exp * (1.75 * getHazardSystemPoints() * g_configManager().getFloat(HAZARD_EXP_BONUS_MULTIPLIER))) / 100.; } - const bool handleAnimusMastery = monster && animusMastery()->has(monster->getMonsterType()->name); + const bool handleAnimusMastery = monster && animusMastery().has(monster->getMonsterType()->name); float animusMasteryMultiplier = 0; if (handleAnimusMastery) { - animusMasteryMultiplier = animusMastery()->getExperienceMultiplier(); + animusMasteryMultiplier = animusMastery().getExperienceMultiplier(); exp *= animusMasteryMultiplier; } @@ -10318,11 +10318,11 @@ const std::unique_ptr &Player::vip() const { } // Animus Mastery interface -std::unique_ptr &Player::animusMastery() { +AnimusMastery &Player::animusMastery() { return m_animusMastery; } -const std::unique_ptr &Player::animusMastery() const { +const AnimusMastery &Player::animusMastery() const { return m_animusMastery; } diff --git a/src/creatures/players/player.hpp b/src/creatures/players/player.hpp index 219e71781cd..0bb89e97dc1 100644 --- a/src/creatures/players/player.hpp +++ b/src/creatures/players/player.hpp @@ -16,6 +16,7 @@ #include "items/cylinder.hpp" #include "game/movement/position.hpp" #include "creatures/creatures_definitions.hpp" +#include "creatures/players/animus_mastery/animus_mastery.hpp" class AnimusMastery; class House; @@ -1274,8 +1275,8 @@ class Player final : public Creature, public Cylinder, public Bankable { const std::unique_ptr &vip() const; // Player animusMastery interface - std::unique_ptr &animusMastery(); - const std::unique_ptr &animusMastery() const; + AnimusMastery &animusMastery(); + const AnimusMastery &animusMastery() const; void sendLootMessage(const std::string &message) const; @@ -1651,7 +1652,7 @@ class Player final : public Creature, public Cylinder, public Bankable { std::unique_ptr m_playerCyclopedia; std::unique_ptr m_playerTitle; std::unique_ptr m_playerVIP; - std::unique_ptr m_animusMastery; + AnimusMastery m_animusMastery; std::mutex quickLootMutex; diff --git a/src/io/functions/iologindata_load_player.cpp b/src/io/functions/iologindata_load_player.cpp index df79ee92737..8e8365627f3 100644 --- a/src/io/functions/iologindata_load_player.cpp +++ b/src/io/functions/iologindata_load_player.cpp @@ -287,7 +287,7 @@ void IOLoginDataLoad::loadPlayerAnimusMastery(const std::shared_ptr &pla PropStream propStream; propStream.init(attr, attrSize); - player->animusMastery()->unserialize(propStream); + player->animusMastery().unserialize(propStream); } void IOLoginDataLoad::loadPlayerDefaultOutfit(const std::shared_ptr &player, const DBResult_ptr &result) { diff --git a/src/io/functions/iologindata_save_player.cpp b/src/io/functions/iologindata_save_player.cpp index 47b2c92fee0..11888ee6660 100644 --- a/src/io/functions/iologindata_save_player.cpp +++ b/src/io/functions/iologindata_save_player.cpp @@ -249,7 +249,7 @@ bool IOLoginDataSave::savePlayerFirst(const std::shared_ptr &player) { // serialize animus mastery PropWriteStream propAnimusMasteryStream; - player->animusMastery()->serialize(propAnimusMasteryStream); + player->animusMastery().serialize(propAnimusMasteryStream); size_t animusMasterySize; const char* animusMastery = propAnimusMasteryStream.getStream(animusMasterySize); diff --git a/src/lua/functions/core/game/game_functions.cpp b/src/lua/functions/core/game/game_functions.cpp index dc51ef15c41..eff01af0313 100644 --- a/src/lua/functions/core/game/game_functions.cpp +++ b/src/lua/functions/core/game/game_functions.cpp @@ -581,7 +581,6 @@ int GameFunctions::luaGameCreateSoulPitMonster(lua_State* L) { } else { if (isSummon) { monster->setMaster(nullptr); - } else { } lua_pushnil(L); } diff --git a/src/lua/functions/creatures/player/player_functions.cpp b/src/lua/functions/creatures/player/player_functions.cpp index a158ae21139..a5a54808f16 100644 --- a/src/lua/functions/creatures/player/player_functions.cpp +++ b/src/lua/functions/creatures/player/player_functions.cpp @@ -4863,7 +4863,7 @@ int PlayerFunctions::luaPlayerAddAnimusMastery(lua_State* L) { } const std::string &monsterType = Lua::getString(L, 2); - player->animusMastery()->add(monsterType); + player->animusMastery().add(monsterType); return 1; } @@ -4875,7 +4875,7 @@ int PlayerFunctions::luaPlayerRemoveAnimusMastery(lua_State* L) { } const std::string &monsterType = Lua::getString(L, 2); - player->animusMastery()->remove(monsterType); + player->animusMastery().remove(monsterType); return 1; } @@ -4888,7 +4888,7 @@ int PlayerFunctions::luaPlayerHasAnimusMastery(lua_State* L) { const std::string &monsterType = Lua::getString(L, 2); - bool has = player->animusMastery()->has(monsterType); + bool has = player->animusMastery().has(monsterType); Lua::pushBoolean(L, has); return 1; diff --git a/src/server/network/protocol/protocolgame.cpp b/src/server/network/protocol/protocolgame.cpp index bf1c366f4d0..d8a09f0fe94 100644 --- a/src/server/network/protocol/protocolgame.cpp +++ b/src/server/network/protocol/protocolgame.cpp @@ -2391,8 +2391,8 @@ void ProtocolGame::parseBestiarysendMonsterData(NetworkMessage &msg) { newmsg.addByte(currentLevel); - newmsg.add(static_cast((player->animusMastery()->getExperienceMultiplier() - 1) * 1000)); // Animus Mastery Bonus - newmsg.add(player->animusMastery()->getPoints()); // Animus Mastery Points + newmsg.add(static_cast((player->animusMastery().getExperienceMultiplier() - 1) * 1000)); // Animus Mastery Bonus + newmsg.add(player->animusMastery().getPoints()); // Animus Mastery Points newmsg.add(killCounter); From 88ee8cbfd291d791a58f3fe952bdab66d3f5d67e Mon Sep 17 00:00:00 2001 From: Felipe Paluco Date: Thu, 16 Jan 2025 18:45:18 -0300 Subject: [PATCH 83/95] fix: animus_mastery field migration and database schema --- data-otservbr-global/migrations/49.lua | 2 +- schema.sql | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/data-otservbr-global/migrations/49.lua b/data-otservbr-global/migrations/49.lua index d51eb8f04cf..2f2f2460d10 100644 --- a/data-otservbr-global/migrations/49.lua +++ b/data-otservbr-global/migrations/49.lua @@ -1,5 +1,5 @@ function onUpdateDatabase() logger.info("Updating database to version 49 (feat: animus mastery (soulpit))") - db.query("ALTER TABLE `players` ADD `animus_mastery` mediumblob NOT NULL;") + db.query("ALTER TABLE `players` ADD `animus_mastery` mediumblob DEFAULT NULL;") end diff --git a/schema.sql b/schema.sql index 6fe1f21cbb8..9d8f3522f69 100644 --- a/schema.sql +++ b/schema.sql @@ -149,6 +149,7 @@ CREATE TABLE IF NOT EXISTS `players` ( `forge_dust_level` bigint(21) NOT NULL DEFAULT '100', `randomize_mount` tinyint(1) NOT NULL DEFAULT '0', `boss_points` int NOT NULL DEFAULT '0', + `animus_mastery` mediumblob DEFAULT NULL, INDEX `account_id` (`account_id`), INDEX `vocation` (`vocation`), CONSTRAINT `players_pk` PRIMARY KEY (`id`), From e80d923246197d4f1c99d40e3013c474a79ba1bc Mon Sep 17 00:00:00 2001 From: Felipe Paluco Date: Fri, 17 Jan 2025 11:36:05 -0300 Subject: [PATCH 84/95] fix: display animus mastery bonus correctly in bestiary --- src/server/network/protocol/protocolgame.cpp | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/server/network/protocol/protocolgame.cpp b/src/server/network/protocol/protocolgame.cpp index d8a09f0fe94..60399d24fbb 100644 --- a/src/server/network/protocol/protocolgame.cpp +++ b/src/server/network/protocol/protocolgame.cpp @@ -2391,9 +2391,13 @@ void ProtocolGame::parseBestiarysendMonsterData(NetworkMessage &msg) { newmsg.addByte(currentLevel); - newmsg.add(static_cast((player->animusMastery().getExperienceMultiplier() - 1) * 1000)); // Animus Mastery Bonus - newmsg.add(player->animusMastery().getPoints()); // Animus Mastery Points - + if (currentLevel > 3 && player->animusMastery().has(mtype->name)) { + newmsg.add(static_cast((player->animusMastery().getExperienceMultiplier() - 1) * 1000)); // Animus Mastery Bonus + newmsg.add(player->animusMastery().getPoints()); // Animus Mastery Points + } else { + newmsg.add(0); + newmsg.add(0); + } newmsg.add(killCounter); newmsg.add(mtype->info.bestiaryFirstUnlock); @@ -3027,11 +3031,15 @@ void ProtocolGame::parseBestiarysendCreatures(NetworkMessage &msg) { newmsg.addByte(0); } - newmsg.add(0); // Creature Animous Bonus + const auto monsterType = g_monsters().getMonsterType(it_.second); + if (monsterType && player->animusMastery().has(it_.second)) { + newmsg.add(1); + } else { + newmsg.add(0); + } } - newmsg.add(0); // Animus Mastery Points - + newmsg.add(player->animusMastery().getPoints()); writeToOutputBuffer(newmsg); } From 38a74d56adbd54a9fc60b5019d23447116f82983 Mon Sep 17 00:00:00 2001 From: Felipe Paluco Date: Fri, 17 Jan 2025 11:43:48 -0300 Subject: [PATCH 85/95] fix: add effect when teleporting to soulpit arena --- data-otservbr-global/lib/others/soulpit.lua | 5 +++++ .../scripts/quests/soulpit/soulpit_fight.lua | 1 - data/libs/functions/lever.lua | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/data-otservbr-global/lib/others/soulpit.lua b/data-otservbr-global/lib/others/soulpit.lua index 572ab2626d9..0e75091b6d8 100644 --- a/data-otservbr-global/lib/others/soulpit.lua +++ b/data-otservbr-global/lib/others/soulpit.lua @@ -33,22 +33,27 @@ SoulPit = { { pos = Position(32375, 31158, 8), teleport = Position(32373, 31151, 8), + effect = CONST_ME_TELEPORT }, { pos = Position(32375, 31159, 8), teleport = Position(32374, 31151, 8), + effect = CONST_ME_TELEPORT }, { pos = Position(32375, 31160, 8), teleport = Position(32375, 31151, 8), + effect = CONST_ME_TELEPORT }, { pos = Position(32375, 31161, 8), teleport = Position(32376, 31151, 8), + effect = CONST_ME_TELEPORT }, { pos = Position(32375, 31162, 8), teleport = Position(32377, 31151, 8), + effect = CONST_ME_TELEPORT }, }, waves = { diff --git a/data-otservbr-global/scripts/quests/soulpit/soulpit_fight.lua b/data-otservbr-global/scripts/quests/soulpit/soulpit_fight.lua index e778386733d..c607a0de9a9 100644 --- a/data-otservbr-global/scripts/quests/soulpit/soulpit_fight.lua +++ b/data-otservbr-global/scripts/quests/soulpit/soulpit_fight.lua @@ -59,7 +59,6 @@ function soulPitAction.onUse(player, item, fromPosition, target, toPosition, isH return false end - local infoPositions = lever:getInfoPositions() return true end) diff --git a/data/libs/functions/lever.lua b/data/libs/functions/lever.lua index 39802af49d2..651b1e9e908 100644 --- a/data/libs/functions/lever.lua +++ b/data/libs/functions/lever.lua @@ -172,7 +172,7 @@ function Lever.teleportPlayers(self) -- It will teleport all players to the posi local player = v.creature if player then player:teleportTo(v.teleport) - player:getPosition():sendMagicEffect(v.effect) + player:getPosition():sendMagicEffect(v.effect or CONST_ME_TELEPORT) self:getTeleportPlayerFunc(player) end end From 674aac094d80e7fc0249289eebae23c2cd203df2 Mon Sep 17 00:00:00 2001 From: Felipe Paluco Date: Fri, 17 Jan 2025 12:19:57 -0300 Subject: [PATCH 86/95] fix: apply animus mastery exp multiplier only when bestiary unlocked --- src/creatures/players/player.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index 0a7a24e73be..df965af2a1f 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -3120,8 +3120,13 @@ void Player::addExperience(const std::shared_ptr &target, uint64_t exp float animusMasteryMultiplier = 0; if (handleAnimusMastery) { - animusMasteryMultiplier = animusMastery().getExperienceMultiplier(); - exp *= animusMasteryMultiplier; + auto mType = g_monsters().getMonsterType(monster->getName()); + uint32_t killCounter = this->getBestiaryKillCount(mType->info.raceid); + uint8_t currentLevel = g_iobestiary().getKillStatus(mType, killCounter); + if (currentLevel > 3) { + animusMasteryMultiplier = animusMastery().getExperienceMultiplier(); + exp *= animusMasteryMultiplier; + } } experience += exp; @@ -3135,7 +3140,7 @@ void Player::addExperience(const std::shared_ptr &target, uint64_t exp } } - if (handleAnimusMastery) { + if (handleAnimusMastery && animusMasteryMultiplier > 0) { expString = fmt::format("{} (animus mastery bonus {:.1f}%)", expString, (animusMasteryMultiplier - 1) * 100); } From 3adda39cc8de7e673ba317e1f107ef2385c3970a Mon Sep 17 00:00:00 2001 From: Felipe Paluco Date: Fri, 17 Jan 2025 14:40:59 -0300 Subject: [PATCH 87/95] fix: round animus mastery experience multiplier value --- src/server/network/protocol/protocolgame.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/network/protocol/protocolgame.cpp b/src/server/network/protocol/protocolgame.cpp index 60399d24fbb..d42344a1518 100644 --- a/src/server/network/protocol/protocolgame.cpp +++ b/src/server/network/protocol/protocolgame.cpp @@ -2392,7 +2392,7 @@ void ProtocolGame::parseBestiarysendMonsterData(NetworkMessage &msg) { newmsg.addByte(currentLevel); if (currentLevel > 3 && player->animusMastery().has(mtype->name)) { - newmsg.add(static_cast((player->animusMastery().getExperienceMultiplier() - 1) * 1000)); // Animus Mastery Bonus + newmsg.add(static_cast(std::round((player->animusMastery().getExperienceMultiplier() - 1) * 1000))); // Animus Mastery Bonus newmsg.add(player->animusMastery().getPoints()); // Animus Mastery Points } else { newmsg.add(0); From 570ed9894626c5750797c9c5456ecda483459323 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Fri, 17 Jan 2025 17:41:45 +0000 Subject: [PATCH 88/95] Lua code format - (Stylua) --- data-otservbr-global/lib/others/soulpit.lua | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/data-otservbr-global/lib/others/soulpit.lua b/data-otservbr-global/lib/others/soulpit.lua index 0e75091b6d8..b677b10fd9c 100644 --- a/data-otservbr-global/lib/others/soulpit.lua +++ b/data-otservbr-global/lib/others/soulpit.lua @@ -33,27 +33,27 @@ SoulPit = { { pos = Position(32375, 31158, 8), teleport = Position(32373, 31151, 8), - effect = CONST_ME_TELEPORT + effect = CONST_ME_TELEPORT, }, { pos = Position(32375, 31159, 8), teleport = Position(32374, 31151, 8), - effect = CONST_ME_TELEPORT + effect = CONST_ME_TELEPORT, }, { pos = Position(32375, 31160, 8), teleport = Position(32375, 31151, 8), - effect = CONST_ME_TELEPORT + effect = CONST_ME_TELEPORT, }, { pos = Position(32375, 31161, 8), teleport = Position(32376, 31151, 8), - effect = CONST_ME_TELEPORT + effect = CONST_ME_TELEPORT, }, { pos = Position(32375, 31162, 8), teleport = Position(32377, 31151, 8), - effect = CONST_ME_TELEPORT + effect = CONST_ME_TELEPORT, }, }, waves = { From c21fd73776b372ec364425aa0e9991b231410721 Mon Sep 17 00:00:00 2001 From: Felipe Paluco Date: Fri, 17 Jan 2025 15:55:00 -0300 Subject: [PATCH 89/95] fix: only show bonus of animus mastery if full unlocked --- src/server/network/protocol/protocolgame.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/network/protocol/protocolgame.cpp b/src/server/network/protocol/protocolgame.cpp index d42344a1518..bc41c48ebcc 100644 --- a/src/server/network/protocol/protocolgame.cpp +++ b/src/server/network/protocol/protocolgame.cpp @@ -3032,7 +3032,7 @@ void ProtocolGame::parseBestiarysendCreatures(NetworkMessage &msg) { } const auto monsterType = g_monsters().getMonsterType(it_.second); - if (monsterType && player->animusMastery().has(it_.second)) { + if (monsterType && player->animusMastery().has(it_.second) && progress > 3 ) { newmsg.add(1); } else { newmsg.add(0); From 8f6d207e6a825641aa9a2c9b7ed74b3944d135ea Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Fri, 17 Jan 2025 18:55:45 +0000 Subject: [PATCH 90/95] Code format - (Clang-format) --- src/server/network/protocol/protocolgame.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/server/network/protocol/protocolgame.cpp b/src/server/network/protocol/protocolgame.cpp index bc41c48ebcc..53188844e42 100644 --- a/src/server/network/protocol/protocolgame.cpp +++ b/src/server/network/protocol/protocolgame.cpp @@ -3032,14 +3032,14 @@ void ProtocolGame::parseBestiarysendCreatures(NetworkMessage &msg) { } const auto monsterType = g_monsters().getMonsterType(it_.second); - if (monsterType && player->animusMastery().has(it_.second) && progress > 3 ) { + if (monsterType && player->animusMastery().has(it_.second) && progress > 3) { newmsg.add(1); } else { - newmsg.add(0); + newmsg.add(0); } } - newmsg.add(player->animusMastery().getPoints()); + newmsg.add(player->animusMastery().getPoints()); writeToOutputBuffer(newmsg); } From a3e4689429b96fb8989b60baf5150598c3c8a78f Mon Sep 17 00:00:00 2001 From: Felipe Paluco Date: Fri, 17 Jan 2025 16:16:52 -0300 Subject: [PATCH 91/95] fix: remove limitations to animus mastery indicators --- src/server/network/protocol/protocolgame.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/server/network/protocol/protocolgame.cpp b/src/server/network/protocol/protocolgame.cpp index 53188844e42..3a2d4456c89 100644 --- a/src/server/network/protocol/protocolgame.cpp +++ b/src/server/network/protocol/protocolgame.cpp @@ -2391,7 +2391,7 @@ void ProtocolGame::parseBestiarysendMonsterData(NetworkMessage &msg) { newmsg.addByte(currentLevel); - if (currentLevel > 3 && player->animusMastery().has(mtype->name)) { + if (player->animusMastery().has(mtype->name)) { newmsg.add(static_cast(std::round((player->animusMastery().getExperienceMultiplier() - 1) * 1000))); // Animus Mastery Bonus newmsg.add(player->animusMastery().getPoints()); // Animus Mastery Points } else { @@ -3032,7 +3032,7 @@ void ProtocolGame::parseBestiarysendCreatures(NetworkMessage &msg) { } const auto monsterType = g_monsters().getMonsterType(it_.second); - if (monsterType && player->animusMastery().has(it_.second) && progress > 3) { + if (monsterType && player->animusMastery().has(it_.second)) { newmsg.add(1); } else { newmsg.add(0); From 4bfd4ee6bdf72347b8e9654c028bab738f4ae0bc Mon Sep 17 00:00:00 2001 From: Felipe Paluco Date: Fri, 17 Jan 2025 16:29:22 -0300 Subject: [PATCH 92/95] fix: apply animus mastery exp multiplier independently of unlocked --- src/creatures/players/player.cpp | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index df965af2a1f..0a7a24e73be 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -3120,13 +3120,8 @@ void Player::addExperience(const std::shared_ptr &target, uint64_t exp float animusMasteryMultiplier = 0; if (handleAnimusMastery) { - auto mType = g_monsters().getMonsterType(monster->getName()); - uint32_t killCounter = this->getBestiaryKillCount(mType->info.raceid); - uint8_t currentLevel = g_iobestiary().getKillStatus(mType, killCounter); - if (currentLevel > 3) { - animusMasteryMultiplier = animusMastery().getExperienceMultiplier(); - exp *= animusMasteryMultiplier; - } + animusMasteryMultiplier = animusMastery().getExperienceMultiplier(); + exp *= animusMasteryMultiplier; } experience += exp; @@ -3140,7 +3135,7 @@ void Player::addExperience(const std::shared_ptr &target, uint64_t exp } } - if (handleAnimusMastery && animusMasteryMultiplier > 0) { + if (handleAnimusMastery) { expString = fmt::format("{} (animus mastery bonus {:.1f}%)", expString, (animusMasteryMultiplier - 1) * 100); } From 9dc0f2a71b3b7f2ba3877f96e8972f84f59a8c90 Mon Sep 17 00:00:00 2001 From: Felipe Paluco Date: Sat, 18 Jan 2025 11:13:54 -0300 Subject: [PATCH 93/95] fix: message when get hits nullptr --- src/game/game.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/game/game.cpp b/src/game/game.cpp index aef6b4ea0d4..232184ff739 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -7635,15 +7635,12 @@ void Game::buildMessageAsTarget( const std::string &damageString ) const { ss.str({}); - const auto &monster = attacker->getMonster(); + const auto &monster = attacker ? attacker->getMonster() : nullptr; bool handleSoulPit = monster ? monster->getSoulPit() && monster->getForgeStack() == 40 : false; - std::string attackMsg = ""; - std::string article = "an"; - if (!handleSoulPit && damage.critical) { - attackMsg = "critical"; - article = "a"; - } + std::string attackMsg = damage.critical && !handleSoulPit ? "critical " : ""; + std::string article = damage.critical && !handleSoulPit ? "a" : "an"; + ss << "You lose " << damageString; if (!attacker) { ss << '.'; From b0e4d15dcb386dc0c925944f1fe99a5d28a826dc Mon Sep 17 00:00:00 2001 From: Felipe Paluco Date: Sat, 18 Jan 2025 11:31:32 -0300 Subject: [PATCH 94/95] fix: animus mastery bonus on creature list --- src/server/network/protocol/protocolgame.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/server/network/protocol/protocolgame.cpp b/src/server/network/protocol/protocolgame.cpp index 3a2d4456c89..4cdc5ecbdf1 100644 --- a/src/server/network/protocol/protocolgame.cpp +++ b/src/server/network/protocol/protocolgame.cpp @@ -3033,13 +3033,14 @@ void ProtocolGame::parseBestiarysendCreatures(NetworkMessage &msg) { const auto monsterType = g_monsters().getMonsterType(it_.second); if (monsterType && player->animusMastery().has(it_.second)) { - newmsg.add(1); + newmsg.add(static_cast(std::round((player->animusMastery().getExperienceMultiplier() - 1) * 1000))); // Animus Mastery Bonus } else { newmsg.add(0); } } - newmsg.add(player->animusMastery().getPoints()); + newmsg.add(player->animusMastery().getPoints()); // Animus Mastery Points + writeToOutputBuffer(newmsg); } From 763a806e7504cd74c6d91adb117c4f11e41c4ab2 Mon Sep 17 00:00:00 2001 From: Felipe Paluco Date: Sun, 19 Jan 2025 03:23:16 -0300 Subject: [PATCH 95/95] fix: change item:removeItem to item:remove to avoid duplications --- data-otservbr-global/scripts/quests/soulpit/exalted_core.lua | 4 ++-- data-otservbr-global/scripts/quests/soulpit/soul_prism.lua | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/data-otservbr-global/scripts/quests/soulpit/exalted_core.lua b/data-otservbr-global/scripts/quests/soulpit/exalted_core.lua index 029c0d33840..dadb7b7fd0d 100644 --- a/data-otservbr-global/scripts/quests/soulpit/exalted_core.lua +++ b/data-otservbr-global/scripts/quests/soulpit/exalted_core.lua @@ -81,9 +81,9 @@ function exaltedCore.onUse(player, item, fromPosition, target, toPosition, isHot end player:addItem(newSoulCoreItem, 1) - player:removeItem(target:getId(), 1) + target:remove(1) player:sendTextMessage(MESSAGE_EVENT_ADVANCE, string.format("You have received a %s soul core.", newMonsterType:getName())) - player:removeItem(item:getId(), 1) + item:remove(1) player:getPosition():sendMagicEffect(CONST_ME_MAGIC_BLUE) return true end diff --git a/data-otservbr-global/scripts/quests/soulpit/soul_prism.lua b/data-otservbr-global/scripts/quests/soulpit/soul_prism.lua index e88243938ca..326bba11e4d 100644 --- a/data-otservbr-global/scripts/quests/soulpit/soul_prism.lua +++ b/data-otservbr-global/scripts/quests/soulpit/soul_prism.lua @@ -94,10 +94,10 @@ function soulPrism.onUse(player, item, fromPosition, target, toPosition, isHotke player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You have received an Ominous Soul Core.") else player:addItem(newSoulCoreItem, 1) - player:removeItem(target:getId(), 1) + target:remove(1) player:sendTextMessage(MESSAGE_EVENT_ADVANCE, string.format("You have received a %s soul core.", newMonsterType:getName())) end - player:removeItem(item:getId(), 1) + item:remove(1) player:getPosition():sendMagicEffect(CONST_ME_MAGIC_BLUE) return true end