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; };