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
This commit is contained in:
Aaron Kimbrell 2022-07-24 13:04:02 -05:00 committed by GitHub
parent ef0a3c6d0b
commit f2d1c5d26d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 185 additions and 117 deletions

View File

@ -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

View File

@ -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<ControllablePhysicsComponent>();
auto* characterComponent = GetComponent<CharacterComponent>();
auto* levelComponent = GetComponent<LevelProgressionComponent>();
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);
}

View File

@ -7,6 +7,7 @@ set(DGAME_DCOMPONENTS_SOURCES "BaseCombatAIComponent.cpp"
"ControllablePhysicsComponent.cpp"
"DestroyableComponent.cpp"
"InventoryComponent.cpp"
"LevelProgressionComponent.cpp"
"LUPExhibitComponent.cpp"
"MissionComponent.cpp"
"MissionOfferComponent.cpp"

View File

@ -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<CDRewardsTable>("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<InventoryComponent>();
auto* controllablePhysicsComponent = parent->GetComponent<ControllablePhysicsComponent>();
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<float>(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);

View File

@ -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
*/

View File

@ -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<CDRewardsTable>("Rewards");
const auto& rewards = rewardsTable->GetByLevelID(m_Level);
bool rewardingItem = rewards.size() > 0;
auto* inventoryComponent = m_Parent->GetComponent<InventoryComponent>();
auto* controllablePhysicsComponent = m_Parent->GetComponent<ControllablePhysicsComponent>();
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<float>(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);
}

View File

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

View File

@ -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<DestroyableComponent*>(entity->GetComponent(COMPONENT_TYPE_DESTROYABLE));
if (dest != nullptr && entity->GetLOT() == 1) {
auto* characterComponent = entity->GetComponent<CharacterComponent>();
if (characterComponent == nullptr) {
return;
auto* levelComponent = entity->GetComponent<LevelProgressionComponent>();
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<ControllablePhysicsComponent*>(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<CharacterComponent*>(entity->GetComponent(COMPONENT_TYPE_CHARACTER));
auto* levelComp = entity->GetComponent<LevelProgressionComponent>();
if (!levelComp) return;
auto* character = entity->GetComponent<CharacterComponent>();
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<InventoryComponent>();
@ -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();

View File

@ -1,10 +1,11 @@
#include "Mission.h"
#include "Mission.h"
#include <ctime>
#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<InventoryComponent>();
auto* levelComponent = entity->GetComponent<LevelProgressionComponent>();
auto* characterComponent = entity->GetComponent<CharacterComponent>();
auto* destroyableComponent = entity->GetComponent<DestroyableComponent>();
auto* missionComponent = entity->GetComponent<MissionComponent>();
@ -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 {

View File

@ -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<MissionComponent>();
auto* inventoryComponent = player->GetComponent<InventoryComponent>();
auto* destroyableComponent = player->GetComponent<DestroyableComponent>();
auto* characterComponent = player->GetComponent<CharacterComponent>();
auto* levelComponent = player->GetComponent<LevelProgressionComponent>();
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.
}

View File

@ -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<CharacterComponent>();
if (!characterComponent) return;
auto levelComponent = entity->GetComponent<LevelProgressionComponent>();
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 != "") {