From f2d1c5d26d16678d57be92b43844a3fc4da24092 Mon Sep 17 00:00:00 2001 From: Aaron Kimbrell Date: Sun, 24 Jul 2022 13:04:02 -0500 Subject: [PATCH] Split out Level progression component (#671) * Split out Level progression component from Character Component This is to get to the Player forced movement Comp in a sane way * move XML to component insted of abusing charComp * use overrides should probably make everything that calls that call it correctly * fix linking issue --- dCommon/dCommonVars.h | 3 +- dGame/Entity.cpp | 23 +++++- dGame/dComponents/CMakeLists.txt | 1 + dGame/dComponents/CharacterComponent.cpp | 70 ------------------ dGame/dComponents/CharacterComponent.h | 22 ------ .../dComponents/LevelProgressionComponent.cpp | 74 +++++++++++++++++++ dGame/dComponents/LevelProgressionComponent.h | 63 ++++++++++++++++ dGame/dGameMessages/GameMessages.cpp | 22 +++--- dGame/dMission/Mission.cpp | 6 +- dGame/dUtilities/Preconditions.cpp | 7 +- dGame/dUtilities/SlashCommandHandler.cpp | 11 ++- 11 files changed, 185 insertions(+), 117 deletions(-) create mode 100644 dGame/dComponents/LevelProgressionComponent.cpp create mode 100644 dGame/dComponents/LevelProgressionComponent.h diff --git a/dCommon/dCommonVars.h b/dCommon/dCommonVars.h index 77d51125..f6efdcf8 100644 --- a/dCommon/dCommonVars.h +++ b/dCommon/dCommonVars.h @@ -402,13 +402,14 @@ enum eReplicaComponentType : int32_t { COMPONENT_TYPE_RACING_CONTROL = 71, //!< The RacingControl Component COMPONENT_TYPE_MISSION_OFFER = 73, //!< The MissionOffer Component COMPONENT_TYPE_EXHIBIT = 75, //!< The Exhibit Component - COMPONENT_TYPE_RACING_STATS = 74, //!< The Exhibit Component + COMPONENT_TYPE_RACING_STATS = 74, //!< The Racing Stats Component COMPONENT_TYPE_SOUND_TRIGGER = 77, //!< The Sound Trigger Component COMPONENT_TYPE_PROXIMITY_MONITOR = 78, //!< The Proximity Monitor Component COMPONENT_TYPE_MISSION = 84, //!< The Mission Component COMPONENT_TYPE_ROCKET_LAUNCH_LUP = 97, //!< The LUP Launchpad Componen COMPONENT_TYPE_RAIL_ACTIVATOR = 104, COMPONENT_TYPE_POSSESSABLE = 108, //!< The Possessable Component + COMPONENT_TYPE_LEVEL_PROGRESSION = 109, //!< The Level Progression Component COMPONENT_TYPE_POSSESSOR = 110, //!< The Possessor Component COMPONENT_TYPE_BUILD_BORDER = 114, //!< The Build Border Component COMPONENT_TYPE_DESTROYABLE = 1000, //!< The Destroyable Component diff --git a/dGame/Entity.cpp b/dGame/Entity.cpp index c44db6f1..45287bd8 100644 --- a/dGame/Entity.cpp +++ b/dGame/Entity.cpp @@ -28,6 +28,7 @@ #include "BuffComponent.h" #include "BouncerComponent.h" #include "InventoryComponent.h" +#include "LevelProgressionComponent.h" #include "ScriptComponent.h" #include "SkillComponent.h" #include "SimplePhysicsComponent.h" @@ -442,8 +443,14 @@ void Entity::Initialize() }*/ if (compRegistryTable->GetByIDAndType(m_TemplateID, COMPONENT_TYPE_CHARACTER) > 0 || m_Character) { - // Character Component always has a possessor component + // Character Component always has a possessor and level components m_Components.insert(std::make_pair(COMPONENT_TYPE_POSSESSOR, new PossessorComponent(this))); + + // load in the xml for the level + auto* levelComp = new LevelProgressionComponent(this); + levelComp->LoadFromXml(m_Character->GetXMLDoc()); + m_Components.insert(std::make_pair(COMPONENT_TYPE_LEVEL_PROGRESSION, levelComp)); + CharacterComponent* comp = new CharacterComponent(this, m_Character); m_Components.insert(std::make_pair(COMPONENT_TYPE_CHARACTER, comp)); } @@ -770,10 +777,9 @@ void Entity::Initialize() if (m_Character) { auto* controllablePhysicsComponent = GetComponent(); - auto* characterComponent = GetComponent(); + auto* levelComponent = GetComponent(); - if (controllablePhysicsComponent != nullptr && characterComponent->GetLevel() >= 20) - { + if (controllablePhysicsComponent != nullptr && levelComponent->GetLevel() >= 20) { controllablePhysicsComponent->SetSpeedMultiplier(525.0f / 500.0f); } } @@ -1088,6 +1094,15 @@ void Entity::WriteComponents(RakNet::BitStream* outBitStream, eReplicaPacketType // Should never happen, but just to be safe outBitStream->Write0(); } + + LevelProgressionComponent* levelProgressionComponent; + if (TryGetComponent(COMPONENT_TYPE_LEVEL_PROGRESSION, levelProgressionComponent)) { + levelProgressionComponent->Serialize(outBitStream, bIsInitialUpdate, flags); + } else { + // Should never happen, but just to be safe + outBitStream->Write0(); + } + characterComponent->Serialize(outBitStream, bIsInitialUpdate, flags); } diff --git a/dGame/dComponents/CMakeLists.txt b/dGame/dComponents/CMakeLists.txt index 706395ea..ebf93dde 100644 --- a/dGame/dComponents/CMakeLists.txt +++ b/dGame/dComponents/CMakeLists.txt @@ -7,6 +7,7 @@ set(DGAME_DCOMPONENTS_SOURCES "BaseCombatAIComponent.cpp" "ControllablePhysicsComponent.cpp" "DestroyableComponent.cpp" "InventoryComponent.cpp" + "LevelProgressionComponent.cpp" "LUPExhibitComponent.cpp" "MissionComponent.cpp" "MissionOfferComponent.cpp" diff --git a/dGame/dComponents/CharacterComponent.cpp b/dGame/dComponents/CharacterComponent.cpp index dd2b69bb..a1eb3c62 100644 --- a/dGame/dComponents/CharacterComponent.cpp +++ b/dGame/dComponents/CharacterComponent.cpp @@ -21,7 +21,6 @@ CharacterComponent::CharacterComponent(Entity* parent, Character* character) : C m_IsGM = false; m_IsLanding = false; m_IsLEGOClubMember = true; - m_Level = 1; m_DirtyCurrentActivity = false; m_DirtyGMInfo = false; @@ -80,8 +79,6 @@ CharacterComponent::~CharacterComponent() { } void CharacterComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) { - outBitStream->Write1(); - outBitStream->Write(m_Level); outBitStream->Write0(); if (bIsInitialUpdate) { @@ -181,57 +178,6 @@ void CharacterComponent::SetPvpEnabled(const bool value) m_PvpEnabled = value; } -void CharacterComponent::HandleLevelUp() -{ - auto* rewardsTable = CDClientManager::Instance()->GetTable("Rewards"); - - const auto& rewards = rewardsTable->GetByLevelID(m_Level); - bool rewardingItem = rewards.size() > 0; - - auto* parent = m_Character->GetEntity(); - - if (parent == nullptr) - { - return; - } - - auto* inventoryComponent = parent->GetComponent(); - auto* controllablePhysicsComponent = parent->GetComponent(); - - if (inventoryComponent == nullptr || controllablePhysicsComponent == nullptr) - { - return; - } - // Tell the client we beginning to send level rewards. - if(rewardingItem) GameMessages::NotifyLevelRewards(parent->GetObjectID(), parent->GetSystemAddress(), m_Level, rewardingItem); - - for (auto* reward : rewards) - { - switch (reward->rewardType) - { - case 0: - inventoryComponent->AddItem(reward->value, reward->count, eLootSourceType::LOOT_SOURCE_LEVEL_REWARD); - break; - case 4: - { - auto* items = inventoryComponent->GetInventory(eInventoryType::ITEMS); - items->SetSize(items->GetSize() + reward->value); - } - break; - case 9: - controllablePhysicsComponent->SetSpeedMultiplier(static_cast(reward->value) / 500.0f); - break; - case 11: - case 12: - break; - default: - break; - } - } - // Tell the client we have finished sending level rewards. - if(rewardingItem) GameMessages::NotifyLevelRewards(parent->GetObjectID(), parent->GetSystemAddress(), m_Level, !rewardingItem); -} - void CharacterComponent::SetGMLevel(int gmlevel) { m_DirtyGMInfo = true; if (gmlevel > 0) m_IsGM = true; @@ -332,14 +278,6 @@ void CharacterComponent::LoadFromXML() { } } - tinyxml2::XMLElement* level = doc->FirstChildElement("obj")->FirstChildElement("lvl"); - if (!level) { - Game::logger->Log("CharacterComponent", "Failed to find lvl tag while loading XML!\n"); - return; - } - - level->QueryAttribute("l", &m_Level); - if (character->FindAttribute("time")) { character->QueryUnsigned64Attribute("time", &m_TotalTimePlayed); } else { @@ -419,14 +357,6 @@ void CharacterComponent::UpdateXml(tinyxml2::XMLDocument* doc) { // End custom attributes // - tinyxml2::XMLElement* level = doc->FirstChildElement("obj")->FirstChildElement("lvl"); - if (!level) { - Game::logger->Log("CharacterComponent", "Failed to find lvl tag while updating XML!\n"); - return; - } - - level->SetAttribute("l", m_Level); - auto newUpdateTimestamp = std::time(nullptr); Game::logger->Log("TotalTimePlayed", "Time since last save: %d\n", newUpdateTimestamp - m_LastUpdateTimestamp); diff --git a/dGame/dComponents/CharacterComponent.h b/dGame/dComponents/CharacterComponent.h index 009530c4..83b67a17 100644 --- a/dGame/dComponents/CharacterComponent.h +++ b/dGame/dComponents/CharacterComponent.h @@ -95,18 +95,6 @@ public: */ void RocketUnEquip(Entity* player); - /** - * Gets the current level of the entity - * @return the current level of the entity - */ - const uint32_t GetLevel() const { return m_Level; } - - /** - * Sets the level of the entity - * @param level the level to set - */ - void SetLevel(uint32_t level) { m_Level = level; } - /** * Gets the universe score of the entity * @return the universe score of the entity @@ -191,11 +179,6 @@ public: */ void SetMountItemID(LWOOBJID mountItemID) { m_MountItemID = mountItemID; } - /** - * Gives the player rewards for the last level that they leveled up from - */ - void HandleLevelUp(); - /** * Gets the name of this character * @return the name of this character @@ -323,11 +306,6 @@ private: */ uint8_t m_PossessableType = 1; - /** - * Level of the entity - */ - uint32_t m_Level; - /** * Universe score of the entity */ diff --git a/dGame/dComponents/LevelProgressionComponent.cpp b/dGame/dComponents/LevelProgressionComponent.cpp new file mode 100644 index 00000000..96d31970 --- /dev/null +++ b/dGame/dComponents/LevelProgressionComponent.cpp @@ -0,0 +1,74 @@ +#include "LevelProgressionComponent.h" +#include "ControllablePhysicsComponent.h" +#include "InventoryComponent.h" +#include "CharacterComponent.h" +#include "tinyxml2.h" + +LevelProgressionComponent::LevelProgressionComponent(Entity* parent) : Component(parent) { + m_Parent = parent; + m_Level = 1; +} + +void LevelProgressionComponent::UpdateXml(tinyxml2::XMLDocument* doc) { + tinyxml2::XMLElement* level = doc->FirstChildElement("obj")->FirstChildElement("lvl"); + if (!level) { + Game::logger->Log("LevelProgressionComponent", "Failed to find lvl tag while updating XML!\n"); + return; + } + level->SetAttribute("l", m_Level); + +} + +void LevelProgressionComponent::LoadFromXml(tinyxml2::XMLDocument* doc){ + tinyxml2::XMLElement* level = doc->FirstChildElement("obj")->FirstChildElement("lvl"); + if (!level) { + Game::logger->Log("LevelProgressionComponent", "Failed to find lvl tag while loading XML!\n"); + return; + } + level->QueryAttribute("l", &m_Level); + +} + +void LevelProgressionComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags){ + outBitStream->Write(bIsInitialUpdate || m_DirtyLevelInfo); + if (bIsInitialUpdate || m_DirtyLevelInfo) outBitStream->Write(m_Level); + m_DirtyLevelInfo = false; +} + +void LevelProgressionComponent::HandleLevelUp() { + auto* rewardsTable = CDClientManager::Instance()->GetTable("Rewards"); + + const auto& rewards = rewardsTable->GetByLevelID(m_Level); + bool rewardingItem = rewards.size() > 0; + + auto* inventoryComponent = m_Parent->GetComponent(); + auto* controllablePhysicsComponent = m_Parent->GetComponent(); + + if (!inventoryComponent || !controllablePhysicsComponent) return; + // Tell the client we beginning to send level rewards. + if(rewardingItem) GameMessages::NotifyLevelRewards(m_Parent->GetObjectID(), m_Parent->GetSystemAddress(), m_Level, rewardingItem); + + for (auto* reward : rewards) { + switch (reward->rewardType) { + case 0: + inventoryComponent->AddItem(reward->value, reward->count, eLootSourceType::LOOT_SOURCE_LEVEL_REWARD); + break; + case 4: + { + auto* items = inventoryComponent->GetInventory(eInventoryType::ITEMS); + items->SetSize(items->GetSize() + reward->value); + } + break; + case 9: + controllablePhysicsComponent->SetSpeedMultiplier(static_cast(reward->value) / 500.0f); + break; + case 11: + case 12: + break; + default: + break; + } + } + // Tell the client we have finished sending level rewards. + if(rewardingItem) GameMessages::NotifyLevelRewards(m_Parent->GetObjectID(), m_Parent->GetSystemAddress(), m_Level, !rewardingItem); +} diff --git a/dGame/dComponents/LevelProgressionComponent.h b/dGame/dComponents/LevelProgressionComponent.h new file mode 100644 index 00000000..ead3edfb --- /dev/null +++ b/dGame/dComponents/LevelProgressionComponent.h @@ -0,0 +1,63 @@ +#pragma once + +#include "Entity.h" +#include "GameMessages.h" +#include "Component.h" + +/** + * Component that handles level progression and serilization. + * + */ +class LevelProgressionComponent : public Component { +public: + static const uint32_t ComponentType = eReplicaComponentType::COMPONENT_TYPE_LEVEL_PROGRESSION; + + /** + * Constructor for this component + * @param parent parent that contains this component + */ + LevelProgressionComponent(Entity* parent); + ~LevelProgressionComponent() override {}; + + void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags); + + /** + * Save data from this componennt to character XML + * @param doc the document to write data to + */ + void UpdateXml(tinyxml2::XMLDocument* doc) override; + + /** + * Load base data for this component from character XML + * @param doc the document to read data from + */ + void LoadFromXml(tinyxml2::XMLDocument* doc) override; + + /** + * Gets the current level of the entity + * @return the current level of the entity + */ + const uint32_t GetLevel() const { return m_Level; } + + /** + * Sets the level of the entity + * @param level the level to set + */ + void SetLevel(uint32_t level) { m_Level = level; m_DirtyLevelInfo = true; } + + /** + * Gives the player rewards for the last level that they leveled up from + */ + void HandleLevelUp(); + +private: + /** + * whether the level is dirty + */ + bool m_DirtyLevelInfo = false; + + /** + * Level of the entity + */ + uint32_t m_Level; +}; diff --git a/dGame/dGameMessages/GameMessages.cpp b/dGame/dGameMessages/GameMessages.cpp index 2f193170..935a7ccb 100644 --- a/dGame/dGameMessages/GameMessages.cpp +++ b/dGame/dGameMessages/GameMessages.cpp @@ -58,6 +58,7 @@ #include "PossessorComponent.h" #include "RacingControlComponent.h" #include "RailActivatorComponent.h" +#include "LevelProgressionComponent.h" // Message includes: #include "dZoneManager.h" @@ -912,14 +913,11 @@ void GameMessages::SendResurrect(Entity* entity) { DestroyableComponent* dest = static_cast(entity->GetComponent(COMPONENT_TYPE_DESTROYABLE)); if (dest != nullptr && entity->GetLOT() == 1) { - auto* characterComponent = entity->GetComponent(); - - if (characterComponent == nullptr) { - return; + auto* levelComponent = entity->GetComponent(); + if (levelComponent) { + dest->SetHealth(levelComponent->GetLevel() >= 45 ? 8 : 4); + dest->SetImagination(levelComponent->GetLevel() >= 45 ? 20 : 6); } - - dest->SetHealth(characterComponent->GetLevel() >= 45 ? 8 : 4); - dest->SetImagination(characterComponent->GetLevel() >= 45 ? 20 : 6); } auto cont = static_cast(entity->GetComponent(COMPONENT_TYPE_CONTROLLABLE_PHYSICS)); @@ -5309,13 +5307,15 @@ void GameMessages::HandleHasBeenCollected(RakNet::BitStream* inStream, Entity* e } void GameMessages::HandleNotifyServerLevelProcessingComplete(RakNet::BitStream* inStream, Entity* entity) { - auto* character = static_cast(entity->GetComponent(COMPONENT_TYPE_CHARACTER)); + auto* levelComp = entity->GetComponent(); + if (!levelComp) return; + auto* character = entity->GetComponent(); if (!character) return; //Update our character's level in memory: - character->SetLevel(character->GetLevel() + 1); + levelComp->SetLevel(levelComp->GetLevel() + 1); - character->HandleLevelUp(); + levelComp->HandleLevelUp(); auto* inventoryComponent = entity->GetComponent(); @@ -5333,7 +5333,7 @@ void GameMessages::HandleNotifyServerLevelProcessingComplete(RakNet::BitStream* //Send a notification in chat: std::stringstream wss; wss << "level=1:"; - wss << character->GetLevel(); + wss << levelComp->GetLevel(); wss << "\n"; wss << "name=0:"; wss << character->GetName(); diff --git a/dGame/dMission/Mission.cpp b/dGame/dMission/Mission.cpp index aeb26838..e51a6901 100644 --- a/dGame/dMission/Mission.cpp +++ b/dGame/dMission/Mission.cpp @@ -1,10 +1,11 @@ -#include "Mission.h" +#include "Mission.h" #include #include "CDClientManager.h" #include "Character.h" #include "CharacterComponent.h" +#include "LevelProgressionComponent.h" #include "DestroyableComponent.h" #include "EntityManager.h" #include "Game.h" @@ -406,6 +407,7 @@ void Mission::YieldRewards() { auto* character = GetUser()->GetLastUsedChar(); auto* inventoryComponent = entity->GetComponent(); + auto* levelComponent = entity->GetComponent(); auto* characterComponent = entity->GetComponent(); auto* destroyableComponent = entity->GetComponent(); auto* missionComponent = entity->GetComponent(); @@ -433,7 +435,7 @@ void Mission::YieldRewards() { int32_t coinsToSend = 0; if (info->LegoScore > 0) { eLootSourceType lootSource = info->isMission ? eLootSourceType::LOOT_SOURCE_MISSION : eLootSourceType::LOOT_SOURCE_ACHIEVEMENT; - if(characterComponent->GetLevel() >= dZoneManager::Instance()->GetMaxLevel()) { + if(levelComponent->GetLevel() >= dZoneManager::Instance()->GetMaxLevel()) { // Since the character is at the level cap we reward them with coins instead of UScore. coinsToSend += info->LegoScore * dZoneManager::Instance()->GetLevelCapCurrencyConversion(); } else { diff --git a/dGame/dUtilities/Preconditions.cpp b/dGame/dUtilities/Preconditions.cpp index bd10b814..2e006ec4 100644 --- a/dGame/dUtilities/Preconditions.cpp +++ b/dGame/dUtilities/Preconditions.cpp @@ -1,4 +1,4 @@ -#include "Preconditions.h" +#include "Preconditions.h" #include "Game.h" #include "dLogger.h" @@ -9,6 +9,7 @@ #include "MissionComponent.h" #include "Character.h" #include "CharacterComponent.h" +#include "LevelProgressionComponent.h" #include "DestroyableComponent.h" #include "GameMessages.h" @@ -128,7 +129,7 @@ bool Precondition::CheckValue(Entity* player, const uint32_t value, bool evaluat auto* missionComponent = player->GetComponent(); auto* inventoryComponent = player->GetComponent(); auto* destroyableComponent = player->GetComponent(); - auto* characterComponent = player->GetComponent(); + auto* levelComponent = player->GetComponent(); auto* character = player->GetCharacter(); Mission* mission; @@ -207,7 +208,7 @@ bool Precondition::CheckValue(Entity* player, const uint32_t value, bool evaluat case PreconditionType::NoInteraction: return false; // TODO case PreconditionType::HasLevel: - return characterComponent->GetLevel() >= value; + return levelComponent->GetLevel() >= value; default: return true; // There are a couple more unknown preconditions. Always return true in this case. } diff --git a/dGame/dUtilities/SlashCommandHandler.cpp b/dGame/dUtilities/SlashCommandHandler.cpp index 22fbb680..4172884f 100644 --- a/dGame/dUtilities/SlashCommandHandler.cpp +++ b/dGame/dUtilities/SlashCommandHandler.cpp @@ -62,6 +62,7 @@ #include "VanityUtilities.h" #include "GameConfig.h" #include "ScriptedActivityComponent.h" +#include "LevelProgressionComponent.h" void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entity* entity, const SystemAddress& sysAddr) { std::string chatCommand; @@ -1329,6 +1330,8 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit // query to set our uscore to the correct value for this level auto characterComponent = entity->GetComponent(); + if (!characterComponent) return; + auto levelComponent = entity->GetComponent(); auto query = CDClientDatabase::CreatePreppedStmt("SELECT requiredUScore from LevelProgressionLookup WHERE id = ?;"); query.bind(1, (int)requestedLevel); auto result = query.execQuery(); @@ -1336,18 +1339,18 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit if (result.eof()) return; // Set the UScore first - oldLevel = characterComponent->GetLevel(); + oldLevel = levelComponent->GetLevel(); characterComponent->SetUScore(result.getIntField(0, characterComponent->GetUScore())); // handle level up for each level we have passed if we set our level to be higher than the current one. if (oldLevel < requestedLevel) { while (oldLevel < requestedLevel) { oldLevel+=1; - characterComponent->SetLevel(oldLevel); - characterComponent->HandleLevelUp(); + levelComponent->SetLevel(oldLevel); + levelComponent->HandleLevelUp(); } } else { - characterComponent->SetLevel(requestedLevel); + levelComponent->SetLevel(requestedLevel); } if (requestedPlayerToSetLevelOf != "") {