2021-12-05 17:54:36 +00:00
# include "BuffComponent.h"
# include <BitStream.h>
# include "CDClientDatabase.h"
# include <stdexcept>
# include "DestroyableComponent.h"
# include "Game.h"
2023-10-21 23:31:55 +00:00
# include "Logger.h"
2021-12-05 17:54:36 +00:00
# include "GameMessages.h"
# include "SkillComponent.h"
# include "ControllablePhysicsComponent.h"
# include "EntityManager.h"
2022-12-18 15:46:04 +00:00
# include "CDClientManager.h"
2023-03-17 14:36:21 +00:00
# include "CDSkillBehaviorTable.h"
2023-12-01 16:12:48 +00:00
# include "TeamManager.h"
2021-12-05 17:54:36 +00:00
std : : unordered_map < int32_t , std : : vector < BuffParameter > > BuffComponent : : m_Cache { } ;
2023-12-01 16:12:48 +00:00
namespace {
std : : map < std : : string , std : : string > BuffFx = {
{ " overtime " , " OTB_ " } ,
{ " max_health " , " HEALTH_ " } ,
{ " max_imagination " , " IMAGINATION_ " } ,
{ " max_armor " , " ARMOR_ " } ,
{ " speed " , " SPEED_ " } ,
{ " loot " , " LOOT_ " }
} ;
}
2021-12-05 17:54:36 +00:00
BuffComponent : : BuffComponent ( Entity * parent ) : Component ( parent ) {
}
BuffComponent : : ~ BuffComponent ( ) {
}
2023-08-10 21:33:15 +00:00
void BuffComponent : : Serialize ( RakNet : : BitStream * outBitStream , bool bIsInitialUpdate ) {
2021-12-05 17:54:36 +00:00
if ( ! bIsInitialUpdate ) return ;
2023-12-01 16:12:48 +00:00
outBitStream - > Write ( ! m_Buffs . empty ( ) ) ;
if ( ! m_Buffs . empty ( ) ) {
2021-12-05 17:54:36 +00:00
outBitStream - > Write < uint32_t > ( m_Buffs . size ( ) ) ;
2023-12-01 16:12:48 +00:00
for ( const auto & [ id , buff ] : m_Buffs ) {
outBitStream - > Write < uint32_t > ( id ) ;
outBitStream - > Write ( buff . time ! = 0.0f ) ;
2023-12-28 04:18:20 +00:00
if ( buff . time ! = 0.0f ) outBitStream - > Write < uint32_t > ( buff . time * 1000.0f ) ;
2023-12-01 16:12:48 +00:00
outBitStream - > Write ( buff . cancelOnDeath ) ;
outBitStream - > Write ( buff . cancelOnZone ) ;
outBitStream - > Write ( buff . cancelOnDamaged ) ;
outBitStream - > Write ( buff . cancelOnRemoveBuff ) ;
outBitStream - > Write ( buff . cancelOnUi ) ;
outBitStream - > Write ( buff . cancelOnLogout ) ;
outBitStream - > Write ( buff . cancelOnUnequip ) ;
outBitStream - > Write0 ( ) ; // Cancel on Damage Absorb Ran Out. Generally false from what I can tell
auto * team = TeamManager : : Instance ( ) - > GetTeam ( buff . source ) ;
bool addedByTeammate = false ;
if ( team ) {
addedByTeammate = std : : count ( team - > members . begin ( ) , team - > members . end ( ) , m_Parent - > GetObjectID ( ) ) > 0 ;
}
outBitStream - > Write ( addedByTeammate ) ; // Added by teammate. If source is in the same team as the target, this is true. Otherwise, false.
outBitStream - > Write ( buff . applyOnTeammates ) ;
if ( addedByTeammate ) outBitStream - > Write ( buff . source ) ;
outBitStream - > Write < uint32_t > ( buff . refCount ) ;
2021-12-05 17:54:36 +00:00
}
}
2022-07-25 02:26:51 +00:00
2023-12-01 16:12:48 +00:00
outBitStream - > Write0 ( ) ; // something to do with immunity buffs?
2021-12-05 17:54:36 +00:00
}
2022-07-25 02:26:51 +00:00
void BuffComponent : : Update ( float deltaTime ) {
2021-12-05 17:54:36 +00:00
/**
* Loop through all buffs and apply deltaTime to ther time .
* If they have expired , remove the buff and break .
*/
for ( auto & buff : m_Buffs ) {
// For damage buffs
if ( buff . second . tick ! = 0.0f & & buff . second . stacks > 0 ) {
buff . second . tickTime - = deltaTime ;
if ( buff . second . tickTime < = 0.0f ) {
buff . second . tickTime = buff . second . tick ;
buff . second . stacks - - ;
2022-01-01 09:46:05 +00:00
SkillComponent : : HandleUnmanaged ( buff . second . behaviorID , m_Parent - > GetObjectID ( ) , buff . second . source ) ;
2021-12-05 17:54:36 +00:00
}
}
// These are indefinate buffs, don't update them.
if ( buff . second . time = = 0.0f ) {
continue ;
}
buff . second . time - = deltaTime ;
if ( buff . second . time < = 0.0f ) {
RemoveBuff ( buff . first ) ;
break ;
}
}
}
2023-12-01 16:12:48 +00:00
const std : : string & GetFxName ( const std : : string & buffname ) {
const auto & toReturn = BuffFx [ buffname ] ;
if ( toReturn . empty ( ) ) {
LOG_DEBUG ( " No fx name for %s " , buffname . c_str ( ) ) ;
}
return toReturn ;
}
void BuffComponent : : ApplyBuffFx ( uint32_t buffId , const BuffParameter & buff ) {
std : : string fxToPlay ;
const auto & buffName = GetFxName ( buff . name ) ;
if ( buffName . empty ( ) ) return ;
fxToPlay + = std : : to_string ( buffId ) ;
LOG_DEBUG ( " Playing %s %i " , fxToPlay . c_str ( ) , buff . effectId ) ;
GameMessages : : SendPlayFXEffect ( m_Parent - > GetObjectID ( ) , buff . effectId , u " cast " , fxToPlay , LWOOBJID_EMPTY , 1.07f , 1.0f , false ) ;
}
void BuffComponent : : RemoveBuffFx ( uint32_t buffId , const BuffParameter & buff ) {
std : : string fxToPlay ;
const auto & buffName = GetFxName ( buff . name ) ;
if ( buffName . empty ( ) ) return ;
fxToPlay + = std : : to_string ( buffId ) ;
LOG_DEBUG ( " Stopping %s " , fxToPlay . c_str ( ) ) ;
GameMessages : : SendStopFXEffect ( m_Parent , false , fxToPlay ) ;
}
2021-12-05 17:54:36 +00:00
void BuffComponent : : ApplyBuff ( const int32_t id , const float duration , const LWOOBJID source , bool addImmunity ,
bool cancelOnDamaged , bool cancelOnDeath , bool cancelOnLogout , bool cancelOnRemoveBuff ,
2023-12-01 16:12:48 +00:00
bool cancelOnUi , bool cancelOnUnequip , bool cancelOnZone , bool applyOnTeammates ) {
2021-12-05 17:54:36 +00:00
// Prevent buffs from stacking.
if ( HasBuff ( id ) ) {
2023-12-01 16:12:48 +00:00
m_Buffs [ id ] . refCount + + ;
m_Buffs [ id ] . time = duration ;
2021-12-05 17:54:36 +00:00
return ;
}
2023-12-01 16:12:48 +00:00
auto * team = TeamManager : : Instance ( ) - > GetTeam ( source ) ;
bool addedByTeammate = false ;
if ( team ) {
addedByTeammate = std : : count ( team - > members . begin ( ) , team - > members . end ( ) , m_Parent - > GetObjectID ( ) ) > 0 ;
}
2023-12-28 04:18:20 +00:00
GameMessages : : SendAddBuff ( const_cast < LWOOBJID & > ( m_Parent - > GetObjectID ( ) ) , source , static_cast < uint32_t > ( id ) ,
static_cast < uint32_t > ( duration ) * 1000 , addImmunity , cancelOnDamaged , cancelOnDeath ,
2023-12-01 16:12:48 +00:00
cancelOnLogout , cancelOnRemoveBuff , cancelOnUi , cancelOnUnequip , cancelOnZone , addedByTeammate , applyOnTeammates ) ;
2021-12-05 17:54:36 +00:00
float tick = 0 ;
float stacks = 0 ;
int32_t behaviorID = 0 ;
const auto & parameters = GetBuffParameters ( id ) ;
for ( const auto & parameter : parameters ) {
if ( parameter . name = = " overtime " ) {
2023-03-17 14:36:21 +00:00
auto * behaviorTemplateTable = CDClientManager : : Instance ( ) . GetTable < CDSkillBehaviorTable > ( ) ;
2021-12-05 17:54:36 +00:00
behaviorID = behaviorTemplateTable - > GetSkillByID ( parameter . values [ 0 ] ) . behaviorID ;
stacks = static_cast < int32_t > ( parameter . values [ 1 ] ) ;
tick = parameter . values [ 2 ] ;
const auto unknown2 = parameter . values [ 3 ] ; // Always 0
}
}
ApplyBuffEffect ( id ) ;
Buff buff ;
buff . id = id ;
buff . time = duration ;
buff . tick = tick ;
buff . tickTime = tick ;
buff . stacks = stacks ;
buff . source = source ;
buff . behaviorID = behaviorID ;
2023-12-01 16:12:48 +00:00
buff . cancelOnDamaged = cancelOnDamaged ;
buff . cancelOnDeath = cancelOnDeath ;
buff . cancelOnLogout = cancelOnLogout ;
buff . cancelOnRemoveBuff = cancelOnRemoveBuff ;
buff . cancelOnUi = cancelOnUi ;
buff . cancelOnUnequip = cancelOnUnequip ;
buff . cancelOnZone = cancelOnZone ;
buff . refCount = 1 ;
2021-12-05 17:54:36 +00:00
m_Buffs . emplace ( id , buff ) ;
2023-12-01 16:12:48 +00:00
auto * parent = GetParent ( ) ;
if ( ! cancelOnDeath ) return ;
m_Parent - > AddDieCallback ( [ parent , id ] ( ) {
LOG_DEBUG ( " Removing buff %i because parent died " , id ) ;
if ( ! parent ) return ;
auto * buffComponent = parent - > GetComponent < BuffComponent > ( ) ;
if ( buffComponent ) buffComponent - > RemoveBuff ( id , false , false , true ) ;
} ) ;
2021-12-05 17:54:36 +00:00
}
2023-12-01 16:12:48 +00:00
void BuffComponent : : RemoveBuff ( int32_t id , bool fromUnEquip , bool removeImmunity , bool ignoreRefCount ) {
2021-12-05 17:54:36 +00:00
const auto & iter = m_Buffs . find ( id ) ;
if ( iter = = m_Buffs . end ( ) ) {
return ;
}
2022-07-25 02:26:51 +00:00
2023-12-01 16:12:48 +00:00
if ( ! ignoreRefCount & & ! iter - > second . cancelOnRemoveBuff ) {
iter - > second . refCount - - ;
LOG_DEBUG ( " refCount for buff %i is now %i " , id , iter - > second . refCount ) ;
if ( iter - > second . refCount > 0 ) {
return ;
}
}
2022-11-28 00:40:14 +00:00
GameMessages : : SendRemoveBuff ( m_Parent , fromUnEquip , removeImmunity , id ) ;
2021-12-05 17:54:36 +00:00
m_Buffs . erase ( iter ) ;
RemoveBuffEffect ( id ) ;
}
2022-07-25 02:26:51 +00:00
bool BuffComponent : : HasBuff ( int32_t id ) {
2021-12-05 17:54:36 +00:00
return m_Buffs . find ( id ) ! = m_Buffs . end ( ) ;
}
2022-07-25 02:26:51 +00:00
void BuffComponent : : ApplyBuffEffect ( int32_t id ) {
2021-12-05 17:54:36 +00:00
const auto & parameters = GetBuffParameters ( id ) ;
for ( const auto & parameter : parameters ) {
2023-12-01 16:12:48 +00:00
ApplyBuffFx ( id , parameter ) ;
2021-12-05 17:54:36 +00:00
if ( parameter . name = = " max_health " ) {
const auto maxHealth = parameter . value ;
auto * destroyable = this - > GetParent ( ) - > GetComponent < DestroyableComponent > ( ) ;
if ( destroyable = = nullptr ) return ;
destroyable - > SetMaxHealth ( destroyable - > GetMaxHealth ( ) + maxHealth ) ;
} else if ( parameter . name = = " max_armor " ) {
const auto maxArmor = parameter . value ;
auto * destroyable = this - > GetParent ( ) - > GetComponent < DestroyableComponent > ( ) ;
if ( destroyable = = nullptr ) return ;
destroyable - > SetMaxArmor ( destroyable - > GetMaxArmor ( ) + maxArmor ) ;
} else if ( parameter . name = = " max_imagination " ) {
const auto maxImagination = parameter . value ;
auto * destroyable = this - > GetParent ( ) - > GetComponent < DestroyableComponent > ( ) ;
if ( destroyable = = nullptr ) return ;
destroyable - > SetMaxImagination ( destroyable - > GetMaxImagination ( ) + maxImagination ) ;
} else if ( parameter . name = = " speed " ) {
auto * controllablePhysicsComponent = this - > GetParent ( ) - > GetComponent < ControllablePhysicsComponent > ( ) ;
2022-12-24 08:49:31 +00:00
if ( ! controllablePhysicsComponent ) return ;
const auto speed = parameter . value ;
controllablePhysicsComponent - > AddSpeedboost ( speed ) ;
2021-12-05 17:54:36 +00:00
}
}
}
2022-07-25 02:26:51 +00:00
void BuffComponent : : RemoveBuffEffect ( int32_t id ) {
2021-12-05 17:54:36 +00:00
const auto & parameters = GetBuffParameters ( id ) ;
for ( const auto & parameter : parameters ) {
2023-12-01 16:12:48 +00:00
RemoveBuffFx ( id , parameter ) ;
2021-12-05 17:54:36 +00:00
if ( parameter . name = = " max_health " ) {
const auto maxHealth = parameter . value ;
auto * destroyable = this - > GetParent ( ) - > GetComponent < DestroyableComponent > ( ) ;
if ( destroyable = = nullptr ) return ;
destroyable - > SetMaxHealth ( destroyable - > GetMaxHealth ( ) - maxHealth ) ;
} else if ( parameter . name = = " max_armor " ) {
const auto maxArmor = parameter . value ;
auto * destroyable = this - > GetParent ( ) - > GetComponent < DestroyableComponent > ( ) ;
if ( destroyable = = nullptr ) return ;
destroyable - > SetMaxArmor ( destroyable - > GetMaxArmor ( ) - maxArmor ) ;
} else if ( parameter . name = = " max_imagination " ) {
const auto maxImagination = parameter . value ;
auto * destroyable = this - > GetParent ( ) - > GetComponent < DestroyableComponent > ( ) ;
if ( destroyable = = nullptr ) return ;
destroyable - > SetMaxImagination ( destroyable - > GetMaxImagination ( ) - maxImagination ) ;
} else if ( parameter . name = = " speed " ) {
auto * controllablePhysicsComponent = this - > GetParent ( ) - > GetComponent < ControllablePhysicsComponent > ( ) ;
2022-12-24 08:49:31 +00:00
if ( ! controllablePhysicsComponent ) return ;
const auto speed = parameter . value ;
controllablePhysicsComponent - > RemoveSpeedboost ( speed ) ;
2021-12-05 17:54:36 +00:00
}
}
}
2022-07-25 02:26:51 +00:00
void BuffComponent : : RemoveAllBuffs ( ) {
2021-12-05 17:54:36 +00:00
for ( const auto & buff : m_Buffs ) {
RemoveBuffEffect ( buff . first ) ;
}
m_Buffs . clear ( ) ;
}
2022-07-25 02:26:51 +00:00
void BuffComponent : : Reset ( ) {
2021-12-05 17:54:36 +00:00
RemoveAllBuffs ( ) ;
}
2022-07-25 02:26:51 +00:00
void BuffComponent : : ReApplyBuffs ( ) {
2021-12-05 17:54:36 +00:00
for ( const auto & buff : m_Buffs ) {
ApplyBuffEffect ( buff . first ) ;
}
}
Entity * BuffComponent : : GetParent ( ) const {
return m_Parent ;
}
2022-07-25 02:03:22 +00:00
void BuffComponent : : LoadFromXml ( tinyxml2 : : XMLDocument * doc ) {
2021-12-05 17:54:36 +00:00
// Load buffs
auto * dest = doc - > FirstChildElement ( " obj " ) - > FirstChildElement ( " dest " ) ;
2022-07-25 02:26:51 +00:00
2021-12-05 17:54:36 +00:00
// Make sure we have a clean buff element.
auto * buffElement = dest - > FirstChildElement ( " buff " ) ;
2022-07-25 02:26:51 +00:00
2021-12-05 17:54:36 +00:00
// Old character, no buffs to load
if ( buffElement = = nullptr ) {
return ;
}
auto * buffEntry = buffElement - > FirstChildElement ( " b " ) ;
2023-12-01 16:12:48 +00:00
while ( buffEntry ) {
2021-12-05 17:54:36 +00:00
int32_t id = buffEntry - > IntAttribute ( " id " ) ;
float t = buffEntry - > FloatAttribute ( " t " ) ;
float tk = buffEntry - > FloatAttribute ( " tk " ) ;
2023-12-01 16:12:48 +00:00
float tt = buffEntry - > FloatAttribute ( " tt " ) ;
2021-12-05 17:54:36 +00:00
int32_t s = buffEntry - > FloatAttribute ( " s " ) ;
LWOOBJID sr = buffEntry - > Int64Attribute ( " sr " ) ;
int32_t b = buffEntry - > IntAttribute ( " b " ) ;
2023-12-01 16:12:48 +00:00
int32_t refCount = buffEntry - > IntAttribute ( " refCount " ) ;
bool cancelOnDamaged = buffEntry - > BoolAttribute ( " cancelOnDamaged " ) ;
bool cancelOnDeath = buffEntry - > BoolAttribute ( " cancelOnDeath " ) ;
bool cancelOnLogout = buffEntry - > BoolAttribute ( " cancelOnLogout " ) ;
bool cancelOnRemoveBuff = buffEntry - > BoolAttribute ( " cancelOnRemoveBuff " ) ;
bool cancelOnUi = buffEntry - > BoolAttribute ( " cancelOnUi " ) ;
bool cancelOnUnequip = buffEntry - > BoolAttribute ( " cancelOnUnequip " ) ;
bool cancelOnZone = buffEntry - > BoolAttribute ( " cancelOnZone " ) ;
bool applyOnTeammates = buffEntry - > BoolAttribute ( " applyOnTeammates " ) ;
2021-12-05 17:54:36 +00:00
Buff buff ;
buff . id = id ;
buff . time = t ;
buff . tick = tk ;
buff . stacks = s ;
buff . source = sr ;
buff . behaviorID = b ;
2023-12-01 16:12:48 +00:00
buff . refCount = refCount ;
buff . tickTime = tt ;
buff . cancelOnDamaged = cancelOnDamaged ;
buff . cancelOnDeath = cancelOnDeath ;
buff . cancelOnLogout = cancelOnLogout ;
buff . cancelOnRemoveBuff = cancelOnRemoveBuff ;
buff . cancelOnUi = cancelOnUi ;
buff . cancelOnUnequip = cancelOnUnequip ;
buff . cancelOnZone = cancelOnZone ;
buff . applyOnTeammates = applyOnTeammates ;
2021-12-05 17:54:36 +00:00
m_Buffs . emplace ( id , buff ) ;
buffEntry = buffEntry - > NextSiblingElement ( " b " ) ;
}
}
2022-07-25 02:26:51 +00:00
void BuffComponent : : UpdateXml ( tinyxml2 : : XMLDocument * doc ) {
2021-12-05 17:54:36 +00:00
// Save buffs
auto * dest = doc - > FirstChildElement ( " obj " ) - > FirstChildElement ( " dest " ) ;
2022-07-25 02:26:51 +00:00
2021-12-05 17:54:36 +00:00
// Make sure we have a clean buff element.
auto * buffElement = dest - > FirstChildElement ( " buff " ) ;
2022-07-25 02:26:51 +00:00
2021-12-05 17:54:36 +00:00
if ( buffElement = = nullptr ) {
buffElement = doc - > NewElement ( " buff " ) ;
dest - > LinkEndChild ( buffElement ) ;
} else {
buffElement - > DeleteChildren ( ) ;
}
2023-12-01 16:12:48 +00:00
for ( const auto & [ id , buff ] : m_Buffs ) {
2021-12-05 17:54:36 +00:00
auto * buffEntry = doc - > NewElement ( " b " ) ;
2023-12-24 14:01:47 +00:00
// TODO: change this if to if (buff.cancelOnZone || buff.cancelOnLogout) handling at some point. No current way to differentiate between zone transfer and logout.
if ( buff . cancelOnZone ) continue ;
2023-12-01 16:12:48 +00:00
buffEntry - > SetAttribute ( " id " , id ) ;
buffEntry - > SetAttribute ( " t " , buff . time ) ;
buffEntry - > SetAttribute ( " tk " , buff . tick ) ;
buffEntry - > SetAttribute ( " tt " , buff . tickTime ) ;
buffEntry - > SetAttribute ( " s " , buff . stacks ) ;
buffEntry - > SetAttribute ( " sr " , buff . source ) ;
buffEntry - > SetAttribute ( " b " , buff . behaviorID ) ;
buffEntry - > SetAttribute ( " refCount " , buff . refCount ) ;
buffEntry - > SetAttribute ( " cancelOnDamaged " , buff . cancelOnDamaged ) ;
buffEntry - > SetAttribute ( " cancelOnDeath " , buff . cancelOnDeath ) ;
buffEntry - > SetAttribute ( " cancelOnLogout " , buff . cancelOnLogout ) ;
buffEntry - > SetAttribute ( " cancelOnRemoveBuff " , buff . cancelOnRemoveBuff ) ;
buffEntry - > SetAttribute ( " cancelOnUi " , buff . cancelOnUi ) ;
buffEntry - > SetAttribute ( " cancelOnUnequip " , buff . cancelOnUnequip ) ;
buffEntry - > SetAttribute ( " cancelOnZone " , buff . cancelOnZone ) ;
buffEntry - > SetAttribute ( " applyOnTeammates " , buff . applyOnTeammates ) ;
2022-07-25 02:26:51 +00:00
2021-12-05 17:54:36 +00:00
buffElement - > LinkEndChild ( buffEntry ) ;
}
}
2022-07-25 02:26:51 +00:00
const std : : vector < BuffParameter > & BuffComponent : : GetBuffParameters ( int32_t buffId ) {
2021-12-05 17:54:36 +00:00
const auto & pair = m_Cache . find ( buffId ) ;
if ( pair ! = m_Cache . end ( ) ) {
return pair - > second ;
}
2023-12-01 16:12:48 +00:00
auto query = CDClientDatabase : : CreatePreppedStmt ( " SELECT * FROM BuffParameters WHERE BuffID = ?; " ) ;
2023-12-28 04:18:20 +00:00
query . bind ( 1 , static_cast < int > ( buffId ) ) ;
2021-12-05 17:54:36 +00:00
2022-01-13 03:48:27 +00:00
auto result = query . execQuery ( ) ;
2022-07-25 02:26:51 +00:00
2021-12-05 17:54:36 +00:00
std : : vector < BuffParameter > parameters { } ;
while ( ! result . eof ( ) ) {
BuffParameter param ;
param . buffId = buffId ;
2023-12-01 16:12:48 +00:00
param . name = result . getStringField ( " ParameterName " ) ;
param . value = result . getFloatField ( " NumberValue " ) ;
param . effectId = result . getIntField ( " EffectID " ) ;
2021-12-05 17:54:36 +00:00
if ( ! result . fieldIsNull ( 3 ) ) {
2023-12-01 16:12:48 +00:00
std : : istringstream stream ( result . getStringField ( " StringValue " ) ) ;
2021-12-05 17:54:36 +00:00
std : : string token ;
while ( std : : getline ( stream , token , ' , ' ) ) {
try {
const auto value = std : : stof ( token ) ;
param . values . push_back ( value ) ;
} catch ( std : : invalid_argument & exception ) {
2023-10-21 23:31:55 +00:00
LOG ( " Failed to parse value (%s): (%s)! " , token . c_str ( ) , exception . what ( ) ) ;
2021-12-05 17:54:36 +00:00
}
}
}
parameters . push_back ( param ) ;
result . nextRow ( ) ;
}
2022-07-25 02:26:51 +00:00
2021-12-05 17:54:36 +00:00
m_Cache . insert_or_assign ( buffId , parameters ) ;
return m_Cache . find ( buffId ) - > second ;
}