From 6ebf1ad2dbebe8632d9cc9000a368764847efebf Mon Sep 17 00:00:00 2001 From: Elson Costa Date: Tue, 7 May 2024 11:33:19 -0300 Subject: [PATCH] fixes merge. --- src/canary_server.cpp | 2 - src/creatures/CMakeLists.txt | 1 + .../players/cyclopedia/player_title.cpp | 18 +++---- src/creatures/players/player.cpp | 15 +++++- src/creatures/players/player.hpp | 9 ++++ src/game/game.cpp | 40 ++++++++++++-- src/game/game.hpp | 5 +- src/game/game_definitions.hpp | 2 +- .../functions/core/game/game_functions.cpp | 1 + .../creatures/player/player_functions.cpp | 52 +++++++++++++++++++ .../creatures/player/player_functions.hpp | 9 ++++ src/server/network/protocol/protocolgame.cpp | 21 +++++--- src/server/network/protocol/protocolgame.hpp | 2 + vcproj/canary.vcxproj | 2 + 14 files changed, 153 insertions(+), 26 deletions(-) diff --git a/src/canary_server.cpp b/src/canary_server.cpp index b6fe4384394..bfabbf51ab8 100644 --- a/src/canary_server.cpp +++ b/src/canary_server.cpp @@ -374,8 +374,6 @@ void CanaryServer::loadModules() { modulesLoadHelper((g_npcs().load(false, true)), "npc"); g_game().loadBoostedCreature(); - // TODO: g_game().initializeCyclopedia(); - // TODO: verify g_game().initializeGameWorldHighscores(); g_ioBosstiary().loadBoostedBoss(); g_ioprey().initializeTaskHuntOptions(); g_game().logCyclopediaStats(); diff --git a/src/creatures/CMakeLists.txt b/src/creatures/CMakeLists.txt index 630812c7996..6715281439f 100644 --- a/src/creatures/CMakeLists.txt +++ b/src/creatures/CMakeLists.txt @@ -23,6 +23,7 @@ target_sources(${PROJECT_NAME}_lib PRIVATE players/player.cpp players/achievement/player_achievement.cpp players/cyclopedia/player_badge.cpp + players/cyclopedia/player_title.cpp players/wheel/player_wheel.cpp players/wheel/wheel_gems.cpp players/vocations/vocation.cpp diff --git a/src/creatures/players/cyclopedia/player_title.cpp b/src/creatures/players/cyclopedia/player_title.cpp index 6b8f1480220..fe6faeaa3d7 100644 --- a/src/creatures/players/cyclopedia/player_title.cpp +++ b/src/creatures/players/cyclopedia/player_title.cpp @@ -38,7 +38,7 @@ bool PlayerTitle::add(uint8_t id, uint32_t timestamp /* = 0*/) { return false; } - const Title &title = g_game().getTitleByIdOrName(id); + const Title &title = g_game().getTitleById(id); if (title.m_id == 0) { return false; } @@ -69,7 +69,7 @@ std::string PlayerTitle::getCurrentTitleName() const { return ""; } - auto title = g_game().getTitleByIdOrName(currentTitle); + auto title = g_game().getTitleById(currentTitle); if (title.m_id == 0) { return ""; } @@ -103,9 +103,9 @@ void PlayerTitle::checkAndUpdateNewTitles() { } break; case CyclopediaTitle_t::HIGHSCORES: - // if (checkHighscore(title.m_skill)) { - // add(title.m_id); - // } + // if (checkHighscore(title.m_skill)) { + // add(title.m_id); + // } break; case CyclopediaTitle_t::BESTIARY: if (checkBestiary(title.m_race)) { @@ -123,9 +123,9 @@ void PlayerTitle::checkAndUpdateNewTitles() { } break; case CyclopediaTitle_t::MAP: - // if (checkMap(title.m_amount)) { - // add(title.m_id); - // } + // if (checkMap(title.m_amount)) { + // add(title.m_id); + // } break; case CyclopediaTitle_t::QUEST: if (checkQuest(title.m_storage)) { @@ -149,7 +149,7 @@ void PlayerTitle::loadUnlockedTitles() { const auto &unlockedTitles = getUnlockedKV()->keys(); g_logger().debug("[{}] - Loading unlocked titles: {}", __FUNCTION__, unlockedTitles.size()); for (const auto &titleName : unlockedTitles) { - const Title &title = g_game().getTitleByIdOrName(0, titleName); + const Title &title = g_game().getTitleByName(titleName); if (title.m_id == 0) { g_logger().error("[{}] - Title {} not found.", __FUNCTION__, titleName); continue; diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index 8b889ac200b..17f4d10b2de 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -17,6 +17,7 @@ #include "creatures/players/wheel/player_wheel.hpp" #include "creatures/players/achievement/player_achievement.hpp" #include "creatures/players/cyclopedia/player_badge.hpp" +#include "creatures/players/cyclopedia/player_title.hpp" #include "creatures/players/storages/storages.hpp" #include "game/game.hpp" #include "game/modal_window/modal_window.hpp" @@ -51,6 +52,7 @@ Player::Player(ProtocolGame_ptr p) : m_wheelPlayer = std::make_unique(*this); m_playerAchievement = std::make_unique(*this); m_playerBadge = std::make_unique(*this); + m_playerTitle = std::make_unique(*this); } Player::~Player() { @@ -121,7 +123,7 @@ std::string Player::getDescription(int32_t lookDistance) { capitalizeWords(subjectPronoun); if (lookDistance == -1) { - s << "yourself."; + s << "yourself" << (title()->getCurrentTitle() == 0 ? "" : (", " + title()->getCurrentTitleName())) << "."; if (group->access) { s << " You are " << group->name << '.'; @@ -144,6 +146,8 @@ std::string Player::getDescription(int32_t lookDistance) { s << " (Level " << level << ')'; } + s << (title()->getCurrentTitle() == 0 ? "" : (", " + title()->getCurrentTitleName())); + s << ". " << subjectPronoun; if (group->access) { @@ -7968,6 +7972,15 @@ const std::unique_ptr &Player::badge() const { return m_playerBadge; } +// Title interface +std::unique_ptr &Player::title() { + return m_playerTitle; +} + +const std::unique_ptr &Player::title() const { + return m_playerTitle; +} + void Player::sendLootMessage(const std::string &message) const { auto party = getParty(); if (!party) { diff --git a/src/creatures/players/player.hpp b/src/creatures/players/player.hpp index ca9c944bb2e..a54c458bf76 100644 --- a/src/creatures/players/player.hpp +++ b/src/creatures/players/player.hpp @@ -35,6 +35,7 @@ #include "game/bank/bank.hpp" #include "enums/object_category.hpp" #include "creatures/players/cyclopedia/player_badge.hpp" +#include "creatures/players/cyclopedia/player_title.hpp" class House; class NetworkMessage; @@ -51,12 +52,14 @@ class Spell; class PlayerWheel; class PlayerAchievement; class PlayerBadge; +class PlayerTitle; class Spectators; class Account; struct ModalWindow; struct Achievement; struct Badge; +struct Title; struct ForgeHistory { ForgeAction_t actionType = ForgeAction_t::FUSION; @@ -2609,6 +2612,10 @@ class Player final : public Creature, public Cylinder, public Bankable { std::unique_ptr &badge(); const std::unique_ptr &badge() const; + // Player title interface + std::unique_ptr &title(); + const std::unique_ptr &title() const; + void sendLootMessage(const std::string &message) const; std::shared_ptr getLootPouch(); @@ -3005,10 +3012,12 @@ class Player final : public Creature, public Cylinder, public Bankable { friend class IOLoginDataSave; friend class PlayerAchievement; friend class PlayerBadge; + friend class PlayerTitle; std::unique_ptr m_wheelPlayer; std::unique_ptr m_playerAchievement; std::unique_ptr m_playerBadge; + std::unique_ptr m_playerTitle; std::mutex quickLootMutex; diff --git a/src/game/game.cpp b/src/game/game.cpp index 57e18e2c2f6..69bc2805d54 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -38,6 +38,7 @@ #include "creatures/players/wheel/player_wheel.hpp" #include "creatures/players/achievement/player_achievement.hpp" #include "creatures/players/cyclopedia/player_badge.hpp" +#include "creatures/players/cyclopedia/player_title.hpp" #include "creatures/npcs/npc.hpp" #include "server/network/webhook/webhook.hpp" #include "server/network/protocol/protocollogin.hpp" @@ -8246,10 +8247,9 @@ void Game::kickPlayer(uint32_t playerId, bool displayEffect) { void Game::playerFriendSystemAction(std::shared_ptr player, uint8_t type, uint8_t titleId) { uint32_t playerGUID = player->getGUID(); if (type == 0x0E) { - // todo in titles system PR - // player->title()->setCurrentTitle(titleId); - // player->sendCyclopediaCharacterBaseInformation(); - // player->sendCyclopediaCharacterTitles(); + player->title()->setCurrentTitle(titleId); + player->sendCyclopediaCharacterBaseInformation(); + player->sendCyclopediaCharacterTitles(); return; } } @@ -10645,7 +10645,7 @@ std::map Game::getAchievements() { void Game::logCyclopediaStats() { g_logger().info("Loaded {} badges from Badge System", m_badges.size()); - // todo in title system: g_logger().info("Loaded {} titles from Title system", m_titles.size()); + g_logger().info("Loaded {} titles from Title system", m_titles.size()); } std::unordered_set Game::getBadges() { @@ -10677,3 +10677,33 @@ Badge Game::getBadgeByName(const std::string &name) { } return {}; } + +std::unordered_set Game::getTitles() { + return m_titles; +} + +Title Game::getTitleById(uint8_t id) { + if (id == 0) { + return {}; + } + auto it = std::find_if(m_titles.begin(), m_titles.end(), [id](const Title &t) { + return t.m_id == id; + }); + if (it != m_titles.end()) { + return *it; + } + return {}; +} + +Title Game::getTitleByName(const std::string &name) { + if (name.empty()) { + return {}; + } + auto it = std::find_if(m_titles.begin(), m_titles.end(), [name](const Title &t) { + return t.m_maleName == name; + }); + if (it != m_titles.end()) { + return *it; + } + return {}; +} diff --git a/src/game/game.hpp b/src/game/game.hpp index 6414d469c6e..b8700b0f763 100644 --- a/src/game/game.hpp +++ b/src/game/game.hpp @@ -51,6 +51,7 @@ class Spectators; struct Achievement; struct HighscoreCategory; struct Badge; +struct Title; static constexpr uint16_t SERVER_BEAT = 0x32; static constexpr int32_t EVENT_MS = 10000; @@ -727,13 +728,15 @@ class Game { Badge getBadgeByName(const std::string &name); std::unordered_set<Title> getTitles(); - Title getTitleByIdOrName(uint8_t id, const std::string &name = ""); + Title getTitleById(uint8_t id); + Title getTitleByName(const std::string &name); private: std::map<uint16_t, Achievement> m_achievements; std::map<std::string, uint16_t> m_achievementsNameToId; std::unordered_set<Badge> m_badges; + std::unordered_set<Title> m_titles; std::vector<HighscoreCategory> m_highscoreCategories; std::unordered_map<uint8_t, std::string> m_highscoreCategoriesNames; diff --git a/src/game/game_definitions.hpp b/src/game/game_definitions.hpp index 156a695df31..ea1622c494a 100644 --- a/src/game/game_definitions.hpp +++ b/src/game/game_definitions.hpp @@ -70,7 +70,7 @@ enum CyclopediaBadge_t : uint8_t { TOURNAMENT_POINTS, }; -enum class CyclopediaTitle_t : uint8_t { +enum CyclopediaTitle_t : uint8_t { NOTHING = 0, GOLD, MOUNTS, diff --git a/src/lua/functions/core/game/game_functions.cpp b/src/lua/functions/core/game/game_functions.cpp index 5f705cf426d..a91889e43bc 100644 --- a/src/lua/functions/core/game/game_functions.cpp +++ b/src/lua/functions/core/game/game_functions.cpp @@ -28,6 +28,7 @@ #include "lua/callbacks/events_callbacks.hpp" #include "creatures/players/achievement/player_achievement.hpp" #include "creatures/players/cyclopedia/player_badge.hpp" +#include "creatures/players/cyclopedia/player_title.hpp" #include "map/spectators.hpp" // Game diff --git a/src/lua/functions/creatures/player/player_functions.cpp b/src/lua/functions/creatures/player/player_functions.cpp index 81b70263a0a..d62c3e6f93b 100644 --- a/src/lua/functions/creatures/player/player_functions.cpp +++ b/src/lua/functions/creatures/player/player_functions.cpp @@ -16,6 +16,7 @@ #include "creatures/players/wheel/player_wheel.hpp" #include "creatures/players/achievement/player_achievement.hpp" #include "creatures/players/cyclopedia/player_badge.hpp" +#include "creatures/players/cyclopedia/player_title.hpp" #include "game/game.hpp" #include "io/iologindata.hpp" #include "io/ioprey.hpp" @@ -4293,3 +4294,54 @@ int PlayerFunctions::luaPlayerAddBadge(lua_State* L) { pushBoolean(L, true); return 1; } + +int PlayerFunctions::luaPlayerAddTitle(lua_State* L) { + // player:addTitle(id) + const auto &player = getUserdataShared<Player>(L, 1); + if (!player) { + reportErrorFunc(getErrorDesc(LUA_ERROR_PLAYER_NOT_FOUND)); + return 1; + } + + player->title()->add(getNumber<uint8_t>(L, 2, 0)); + pushBoolean(L, true); + return 1; +} + +int PlayerFunctions::luaPlayerGetTitles(lua_State* L) { + // player:getTitles() + const auto &player = getUserdataShared<Player>(L, 1); + if (!player) { + reportErrorFunc(getErrorDesc(LUA_ERROR_PLAYER_NOT_FOUND)); + return 1; + } + + auto playerTitles = player->title()->getUnlockedTitles(); + lua_createtable(L, static_cast<int>(playerTitles.size()), 0); + + int index = 0; + for (auto title : playerTitles) { + lua_pushnumber(L, title.first.m_id); + lua_rawseti(L, -2, ++index); + } + return 1; +} + +int PlayerFunctions::luaPlayerSetCurrentTitle(lua_State* L) { + // player:setCurrentTitle(id) + const auto &player = getUserdataShared<Player>(L, 1); + if (!player) { + reportErrorFunc(getErrorDesc(LUA_ERROR_PLAYER_NOT_FOUND)); + return 1; + } + + const auto &title = g_game().getTitleByIdOrName(getNumber<uint8_t>(L, 2, 0)); + if (title.m_id == 0) { + reportErrorFunc(getErrorDesc(LUA_ERROR_VARIANT_NOT_FOUND)); + return 1; + } + + player->title()->setCurrentTitle(title.m_id); + pushBoolean(L, true); + return 1; +} diff --git a/src/lua/functions/creatures/player/player_functions.hpp b/src/lua/functions/creatures/player/player_functions.hpp index a30f9f755ca..4d89a86d27f 100644 --- a/src/lua/functions/creatures/player/player_functions.hpp +++ b/src/lua/functions/creatures/player/player_functions.hpp @@ -367,6 +367,11 @@ class PlayerFunctions final : LuaScriptInterface { // Badge Functions registerMethod(L, "Player", "addBadge", PlayerFunctions::luaPlayerAddBadge); + // Title Functions + registerMethod(L, "Player", "addTitle", PlayerFunctions::luaPlayerAddTitle); + registerMethod(L, "Player", "getTitles", PlayerFunctions::luaPlayerGetTitles); + registerMethod(L, "Player", "setCurrentTitle", PlayerFunctions::luaPlayerSetCurrentTitle); + GroupFunctions::init(L); GuildFunctions::init(L); MountFunctions::init(L); @@ -723,5 +728,9 @@ class PlayerFunctions final : LuaScriptInterface { static int luaPlayerAddBadge(lua_State* L); + static int luaPlayerAddTitle(lua_State* L); + static int luaPlayerGetTitles(lua_State* L); + static int luaPlayerSetCurrentTitle(lua_State* L); + friend class CreatureFunctions; }; diff --git a/src/server/network/protocol/protocolgame.cpp b/src/server/network/protocol/protocolgame.cpp index 454988019a2..a404ac9c9a4 100644 --- a/src/server/network/protocol/protocolgame.cpp +++ b/src/server/network/protocol/protocolgame.cpp @@ -28,6 +28,7 @@ #include "creatures/players/wheel/player_wheel.hpp" #include "creatures/players/achievement/player_achievement.hpp" #include "creatures/players/cyclopedia/player_badge.hpp" +#include "creatures/players/cyclopedia/player_title.hpp" #include "creatures/players/grouping/familiars.hpp" #include "server/network/protocol/protocolgame.hpp" #include "game/scheduling/dispatcher.hpp" @@ -2058,9 +2059,8 @@ void ProtocolGame::sendItemInspection(uint16_t itemId, uint8_t itemCount, std::s void ProtocolGame::parseFriendSystemAction(NetworkMessage &msg) { uint8_t state = msg.getByte(); if (state == 0x0E) { - // todo title system pr - // uint8_t titleId = msg.getByte(); - // g_game().playerFriendSystemAction(player, state, titleId); + uint8_t titleId = msg.getByte(); + g_game().playerFriendSystemAction(player, state, titleId); } } @@ -3380,9 +3380,8 @@ void ProtocolGame::sendCyclopediaCharacterBaseInformation() { msg.add<uint16_t>(player->getLevel()); AddOutfit(msg, player->getDefaultOutfit(), false); - msg.addByte(0x00); // Store summary & Character titles - msg.addString("", "ProtocolGame::sendCyclopediaCharacterBaseInformation - empty"); // character title - // msg.addString(player->title()->getCurrentTitleName(), "ProtocolGame::sendCyclopediaCharacterBaseInformation - player->title()->getCurrentTitleName()"); // character title + msg.addByte(0x01); // Store summary & Character titles + msg.addString(player->title()->getCurrentTitleName(), "ProtocolGame::sendCyclopediaCharacterBaseInformation - player->title()->getCurrentTitleName()"); // character title writeToOutputBuffer(msg); } @@ -3910,6 +3909,13 @@ void ProtocolGame::sendCyclopediaCharacterInspection() { msg.addString("Vocation", "ProtocolGame::sendCyclopediaCharacterInspection - Vocation"); msg.addString(player->getVocation()->getVocName(), "ProtocolGame::sendCyclopediaCharacterInspection - player->getVocation()->getVocName()"); + // Player title + if (player->title()->getCurrentTitle() != 0) { + playerDescriptionSize++; + msg.addString("Title", "ProtocolGame::sendCyclopediaCharacterInspection - Title"); + msg.addString(player->title()->getCurrentTitleName(), "ProtocolGame::sendCyclopediaCharacterInspection - player->title()->getCurrentTitleName()"); + } + // Loyalty title if (!player->getLoyaltyTitle().empty()) { playerDescriptionSize++; @@ -3951,7 +3957,6 @@ void ProtocolGame::sendCyclopediaCharacterBadges() { msg.addByte(player->isPremium() ? 0x01 : 0x00); // IsPremium (GOD has always 'Premium') // Character loyalty title msg.addString(player->getLoyaltyTitle(), "ProtocolGame::sendCyclopediaCharacterBadges - player->getLoyaltyTitle()"); - // msg.addByte(0x01); // Enable badges uint8_t badgesSize = 0; auto badgesSizePosition = msg.getBufferPosition(); @@ -3975,6 +3980,8 @@ void ProtocolGame::sendCyclopediaCharacterTitles() { return; } + auto titles = g_game().getTitles(); + NetworkMessage msg; msg.addByte(0xDA); msg.addByte(CYCLOPEDIA_CHARACTERINFO_TITLES); diff --git a/src/server/network/protocol/protocolgame.hpp b/src/server/network/protocol/protocolgame.hpp index 7aea8ff805d..2541c8f6416 100644 --- a/src/server/network/protocol/protocolgame.hpp +++ b/src/server/network/protocol/protocolgame.hpp @@ -14,6 +14,7 @@ #include "creatures/creature.hpp" #include "enums/forge_conversion.hpp" #include "creatures/players/cyclopedia/player_badge.hpp" +#include "creatures/players/cyclopedia/player_title.hpp" class NetworkMessage; class Player; @@ -31,6 +32,7 @@ class TaskHuntingOption; struct ModalWindow; struct Achievement; struct Badge; +struct Title; using ProtocolGame_ptr = std::shared_ptr<ProtocolGame>; diff --git a/vcproj/canary.vcxproj b/vcproj/canary.vcxproj index 4489838562a..1c21dc87210 100644 --- a/vcproj/canary.vcxproj +++ b/vcproj/canary.vcxproj @@ -46,6 +46,7 @@ <ClInclude Include="..\src\creatures\players\wheel\wheel_gems.hpp" /> <ClInclude Include="..\src\creatures\players\achievement\player_achievement.hpp" /> <ClInclude Include="..\src\creatures\players\cyclopedia\player_badge.hpp" /> + <ClInclude Include="..\src\creatures\players\cyclopedia\player_title.hpp" /> <ClInclude Include="..\src\creatures\players\wheel\player_wheel.hpp" /> <ClInclude Include="..\src\creatures\players\wheel\wheel_definitions.hpp" /> <ClInclude Include="..\src\database\database.hpp" /> @@ -259,6 +260,7 @@ <ClCompile Include="..\src\creatures\players\wheel\wheel_gems.cpp" /> <ClCompile Include="..\src\creatures\players\achievement\player_achievement.cpp" /> <ClCompile Include="..\src\creatures\players\cyclopedia\player_badge.cpp" /> + <ClCompile Include="..\src\creatures\players\cyclopedia\player_title.cpp" /> <ClCompile Include="..\src\creatures\players\wheel\player_wheel.cpp" /> <ClCompile Include="..\src\database\database.cpp" /> <ClCompile Include="..\src\database\databasemanager.cpp" />