2021-12-05 17:54:36 +00:00
# include "DestroyableComponent.h"
# include <BitStream.h>
# include "dLogger.h"
# include "Game.h"
# include "AMFFormat.h"
# include "AMFFormat_BitStream.h"
# include "GameMessages.h"
# include "User.h"
# include "CDClientManager.h"
# include "CDDestructibleComponentTable.h"
# include "EntityManager.h"
# include "RebuildComponent.h"
# include "CppScripts.h"
# include "Loot.h"
# include "Character.h"
# include "Spawner.h"
# include "BaseCombatAIComponent.h"
# include "TeamManager.h"
# include "BuffComponent.h"
# include "SkillComponent.h"
# include "Item.h"
# include <sstream>
# include <algorithm>
# include "MissionComponent.h"
# include "CharacterComponent.h"
2022-09-02 18:49:19 +00:00
# include "PossessableComponent.h"
# include "PossessorComponent.h"
2022-12-18 15:46:04 +00:00
# include "InventoryComponent.h"
2022-02-05 12:27:24 +00:00
# include "dZoneManager.h"
2023-01-01 12:51:22 +00:00
# include "WorldConfig.h"
2021-12-05 17:54:36 +00:00
DestroyableComponent : : DestroyableComponent ( Entity * parent ) : Component ( parent ) {
m_iArmor = 0 ;
m_fMaxArmor = 0.0f ;
m_iImagination = 0 ;
m_fMaxImagination = 0.0f ;
m_FactionIDs = std : : vector < int32_t > ( ) ;
m_EnemyFactionIDs = std : : vector < int32_t > ( ) ;
m_IsSmashable = false ;
m_IsDead = false ;
m_IsSmashed = false ;
m_IsGMImmune = false ;
m_IsShielded = false ;
m_DamageToAbsorb = 0 ;
m_HasBricks = false ;
m_DirtyThreatList = false ;
m_HasThreats = false ;
m_ExplodeFactor = 1.0f ;
m_iHealth = 0 ;
m_fMaxHealth = 0 ;
m_AttacksToBlock = 0 ;
m_LootMatrixID = 0 ;
m_MinCoins = 0 ;
m_MaxCoins = 0 ;
m_DamageReduction = 0 ;
2023-01-07 05:59:19 +00:00
m_ImmuneToBasicAttackCount = 0 ;
m_ImmuneToDamageOverTimeCount = 0 ;
m_ImmuneToKnockbackCount = 0 ;
m_ImmuneToInterruptCount = 0 ;
m_ImmuneToSpeedCount = 0 ;
m_ImmuneToImaginationGainCount = 0 ;
m_ImmuneToImaginationLossCount = 0 ;
m_ImmuneToQuickbuildInterruptCount = 0 ;
m_ImmuneToPullToPointCount = 0 ;
2021-12-05 17:54:36 +00:00
}
DestroyableComponent : : ~ DestroyableComponent ( ) {
}
void DestroyableComponent : : Reinitialize ( LOT templateID ) {
CDComponentsRegistryTable * compRegistryTable = CDClientManager : : Instance ( ) - > GetTable < CDComponentsRegistryTable > ( " ComponentsRegistry " ) ;
int32_t buffComponentID = compRegistryTable - > GetByIDAndType ( templateID , COMPONENT_TYPE_BUFF ) ;
int32_t collectibleComponentID = compRegistryTable - > GetByIDAndType ( templateID , COMPONENT_TYPE_COLLECTIBLE ) ;
int32_t rebuildComponentID = compRegistryTable - > GetByIDAndType ( templateID , COMPONENT_TYPE_REBUILD ) ;
int32_t componentID = 0 ;
if ( collectibleComponentID > 0 ) componentID = collectibleComponentID ;
if ( rebuildComponentID > 0 ) componentID = rebuildComponentID ;
if ( buffComponentID > 0 ) componentID = buffComponentID ;
CDDestructibleComponentTable * destCompTable = CDClientManager : : Instance ( ) - > GetTable < CDDestructibleComponentTable > ( " DestructibleComponent " ) ;
std : : vector < CDDestructibleComponent > destCompData = destCompTable - > Query ( [ = ] ( CDDestructibleComponent entry ) { return ( entry . id = = componentID ) ; } ) ;
if ( componentID > 0 ) {
std : : vector < CDDestructibleComponent > destCompData = destCompTable - > Query ( [ = ] ( CDDestructibleComponent entry ) { return ( entry . id = = componentID ) ; } ) ;
if ( destCompData . size ( ) > 0 ) {
SetHealth ( destCompData [ 0 ] . life ) ;
SetImagination ( destCompData [ 0 ] . imagination ) ;
SetArmor ( destCompData [ 0 ] . armor ) ;
SetMaxHealth ( destCompData [ 0 ] . life ) ;
SetMaxImagination ( destCompData [ 0 ] . imagination ) ;
SetMaxArmor ( destCompData [ 0 ] . armor ) ;
SetIsSmashable ( destCompData [ 0 ] . isSmashable ) ;
}
} else {
SetHealth ( 1 ) ;
SetImagination ( 0 ) ;
SetArmor ( 0 ) ;
SetMaxHealth ( 1 ) ;
SetMaxImagination ( 0 ) ;
SetMaxArmor ( 0 ) ;
SetIsSmashable ( true ) ;
}
}
void DestroyableComponent : : Serialize ( RakNet : : BitStream * outBitStream , bool bIsInitialUpdate , uint32_t & flags ) {
if ( bIsInitialUpdate ) {
2023-01-07 05:59:19 +00:00
outBitStream - > Write1 ( ) ; // always write these on construction
outBitStream - > Write ( m_ImmuneToBasicAttackCount ) ;
outBitStream - > Write ( m_ImmuneToDamageOverTimeCount ) ;
outBitStream - > Write ( m_ImmuneToKnockbackCount ) ;
outBitStream - > Write ( m_ImmuneToInterruptCount ) ;
outBitStream - > Write ( m_ImmuneToSpeedCount ) ;
outBitStream - > Write ( m_ImmuneToImaginationGainCount ) ;
outBitStream - > Write ( m_ImmuneToImaginationLossCount ) ;
outBitStream - > Write ( m_ImmuneToQuickbuildInterruptCount ) ;
outBitStream - > Write ( m_ImmuneToPullToPointCount ) ;
2021-12-05 17:54:36 +00:00
}
2022-07-28 13:39:57 +00:00
2022-01-15 19:02:14 +00:00
outBitStream - > Write ( m_DirtyHealth | | bIsInitialUpdate ) ;
2021-12-05 17:54:36 +00:00
if ( m_DirtyHealth | | bIsInitialUpdate ) {
outBitStream - > Write ( m_iHealth ) ;
outBitStream - > Write ( m_fMaxHealth ) ;
outBitStream - > Write ( m_iArmor ) ;
outBitStream - > Write ( m_fMaxArmor ) ;
outBitStream - > Write ( m_iImagination ) ;
outBitStream - > Write ( m_fMaxImagination ) ;
2022-07-28 13:39:57 +00:00
2021-12-05 17:54:36 +00:00
outBitStream - > Write ( m_DamageToAbsorb ) ;
outBitStream - > Write ( IsImmune ( ) ) ;
outBitStream - > Write ( m_IsGMImmune ) ;
outBitStream - > Write ( m_IsShielded ) ;
2022-07-28 13:39:57 +00:00
2021-12-05 17:54:36 +00:00
outBitStream - > Write ( m_fMaxHealth ) ;
outBitStream - > Write ( m_fMaxArmor ) ;
outBitStream - > Write ( m_fMaxImagination ) ;
outBitStream - > Write ( uint32_t ( m_FactionIDs . size ( ) ) ) ;
for ( size_t i = 0 ; i < m_FactionIDs . size ( ) ; + + i ) {
outBitStream - > Write ( m_FactionIDs [ i ] ) ;
}
outBitStream - > Write ( m_IsSmashable ) ;
2022-01-15 19:02:14 +00:00
2021-12-05 17:54:36 +00:00
if ( bIsInitialUpdate ) {
outBitStream - > Write ( m_IsDead ) ;
outBitStream - > Write ( m_IsSmashed ) ;
2022-01-15 19:02:14 +00:00
2021-12-05 17:54:36 +00:00
if ( m_IsSmashable ) {
outBitStream - > Write ( m_HasBricks ) ;
2022-11-07 08:12:35 +00:00
outBitStream - > Write ( m_ExplodeFactor ! = 1.0f ) ;
if ( m_ExplodeFactor ! = 1.0f ) outBitStream - > Write ( m_ExplodeFactor ) ;
2021-12-05 17:54:36 +00:00
}
}
m_DirtyHealth = false ;
}
2022-01-15 19:02:14 +00:00
2022-11-07 08:12:35 +00:00
outBitStream - > Write ( m_DirtyThreatList | | bIsInitialUpdate ) ;
2021-12-05 17:54:36 +00:00
if ( m_DirtyThreatList | | bIsInitialUpdate ) {
outBitStream - > Write ( m_HasThreats ) ;
m_DirtyThreatList = false ;
}
}
2022-07-25 02:03:22 +00:00
void DestroyableComponent : : LoadFromXml ( tinyxml2 : : XMLDocument * doc ) {
2021-12-05 17:54:36 +00:00
tinyxml2 : : XMLElement * dest = doc - > FirstChildElement ( " obj " ) - > FirstChildElement ( " dest " ) ;
if ( ! dest ) {
2022-07-25 02:26:51 +00:00
Game : : logger - > Log ( " DestroyableComponent " , " Failed to find dest tag! " ) ;
2021-12-05 17:54:36 +00:00
return ;
}
auto * buffComponent = m_Parent - > GetComponent < BuffComponent > ( ) ;
2022-01-15 19:02:14 +00:00
2021-12-05 17:54:36 +00:00
if ( buffComponent ! = nullptr ) {
2022-07-25 02:03:22 +00:00
buffComponent - > LoadFromXml ( doc ) ;
2021-12-05 17:54:36 +00:00
}
dest - > QueryAttribute ( " hc " , & m_iHealth ) ;
dest - > QueryAttribute ( " hm " , & m_fMaxHealth ) ;
dest - > QueryAttribute ( " im " , & m_fMaxImagination ) ;
dest - > QueryAttribute ( " ic " , & m_iImagination ) ;
dest - > QueryAttribute ( " ac " , & m_iArmor ) ;
dest - > QueryAttribute ( " am " , & m_fMaxArmor ) ;
m_DirtyHealth = true ;
}
void DestroyableComponent : : UpdateXml ( tinyxml2 : : XMLDocument * doc ) {
tinyxml2 : : XMLElement * dest = doc - > FirstChildElement ( " obj " ) - > FirstChildElement ( " dest " ) ;
if ( ! dest ) {
2022-07-25 02:26:51 +00:00
Game : : logger - > Log ( " DestroyableComponent " , " Failed to find dest tag! " ) ;
2021-12-05 17:54:36 +00:00
return ;
}
2022-01-15 19:02:14 +00:00
2021-12-05 17:54:36 +00:00
auto * buffComponent = m_Parent - > GetComponent < BuffComponent > ( ) ;
2022-01-15 19:02:14 +00:00
2021-12-05 17:54:36 +00:00
if ( buffComponent ! = nullptr ) {
buffComponent - > UpdateXml ( doc ) ;
}
dest - > SetAttribute ( " hc " , m_iHealth ) ;
dest - > SetAttribute ( " hm " , m_fMaxHealth ) ;
dest - > SetAttribute ( " im " , m_fMaxImagination ) ;
dest - > SetAttribute ( " ic " , m_iImagination ) ;
dest - > SetAttribute ( " ac " , m_iArmor ) ;
dest - > SetAttribute ( " am " , m_fMaxArmor ) ;
}
void DestroyableComponent : : SetHealth ( int32_t value ) {
m_DirtyHealth = true ;
auto * characterComponent = m_Parent - > GetComponent < CharacterComponent > ( ) ;
if ( characterComponent ! = nullptr ) {
characterComponent - > TrackHealthDelta ( value - m_iHealth ) ;
}
m_iHealth = value ;
}
void DestroyableComponent : : SetMaxHealth ( float value , bool playAnim ) {
m_DirtyHealth = true ;
2022-04-25 00:25:45 +00:00
// Used for playAnim if opted in for.
int32_t difference = static_cast < int32_t > ( std : : abs ( m_fMaxHealth - value ) ) ;
2021-12-05 17:54:36 +00:00
m_fMaxHealth = value ;
if ( m_iHealth > m_fMaxHealth ) {
m_iHealth = m_fMaxHealth ;
}
if ( playAnim ) {
// Now update the player bar
if ( ! m_Parent - > GetParentUser ( ) ) return ;
AMFStringValue * amount = new AMFStringValue ( ) ;
2022-04-25 00:25:45 +00:00
amount - > SetStringValue ( std : : to_string ( difference ) ) ;
2021-12-05 17:54:36 +00:00
AMFStringValue * type = new AMFStringValue ( ) ;
type - > SetStringValue ( " health " ) ;
AMFArrayValue args ;
args . InsertValue ( " amount " , amount ) ;
args . InsertValue ( " type " , type ) ;
2022-01-15 19:02:14 +00:00
2022-07-22 05:26:09 +00:00
GameMessages : : SendUIMessageServerToSingleClient ( m_Parent , m_Parent - > GetParentUser ( ) - > GetSystemAddress ( ) , " MaxPlayerBarUpdate " , & args ) ;
2021-12-05 17:54:36 +00:00
}
2022-04-25 00:25:45 +00:00
EntityManager : : Instance ( ) - > SerializeEntity ( m_Parent ) ;
2021-12-05 17:54:36 +00:00
}
void DestroyableComponent : : SetArmor ( int32_t value ) {
m_DirtyHealth = true ;
2022-02-05 11:59:07 +00:00
// If Destroyable Component already has zero armor do not trigger the passive ability again.
bool hadArmor = m_iArmor > 0 ;
2022-07-25 02:26:51 +00:00
2021-12-05 17:54:36 +00:00
auto * characterComponent = m_Parent - > GetComponent < CharacterComponent > ( ) ;
if ( characterComponent ! = nullptr ) {
characterComponent - > TrackArmorDelta ( value - m_iArmor ) ;
}
m_iArmor = value ;
auto * inventroyComponent = m_Parent - > GetComponent < InventoryComponent > ( ) ;
2022-02-05 11:59:07 +00:00
if ( m_iArmor = = 0 & & inventroyComponent ! = nullptr & & hadArmor ) {
2021-12-05 17:54:36 +00:00
inventroyComponent - > TriggerPassiveAbility ( PassiveAbilityTrigger : : SentinelArmor ) ;
}
}
void DestroyableComponent : : SetMaxArmor ( float value , bool playAnim ) {
m_DirtyHealth = true ;
m_fMaxArmor = value ;
if ( m_iArmor > m_fMaxArmor ) {
m_iArmor = m_fMaxArmor ;
}
if ( playAnim ) {
// Now update the player bar
if ( ! m_Parent - > GetParentUser ( ) ) return ;
AMFStringValue * amount = new AMFStringValue ( ) ;
amount - > SetStringValue ( std : : to_string ( value ) ) ;
AMFStringValue * type = new AMFStringValue ( ) ;
type - > SetStringValue ( " armor " ) ;
AMFArrayValue args ;
args . InsertValue ( " amount " , amount ) ;
args . InsertValue ( " type " , type ) ;
GameMessages : : SendUIMessageServerToSingleClient ( m_Parent , m_Parent - > GetParentUser ( ) - > GetSystemAddress ( ) , " MaxPlayerBarUpdate " , & args ) ;
}
2022-04-25 00:25:45 +00:00
EntityManager : : Instance ( ) - > SerializeEntity ( m_Parent ) ;
2021-12-05 17:54:36 +00:00
}
void DestroyableComponent : : SetImagination ( int32_t value ) {
m_DirtyHealth = true ;
auto * characterComponent = m_Parent - > GetComponent < CharacterComponent > ( ) ;
if ( characterComponent ! = nullptr ) {
characterComponent - > TrackImaginationDelta ( value - m_iImagination ) ;
}
m_iImagination = value ;
auto * inventroyComponent = m_Parent - > GetComponent < InventoryComponent > ( ) ;
if ( m_iImagination = = 0 & & inventroyComponent ! = nullptr ) {
inventroyComponent - > TriggerPassiveAbility ( PassiveAbilityTrigger : : AssemblyImagination ) ;
}
}
void DestroyableComponent : : SetMaxImagination ( float value , bool playAnim ) {
m_DirtyHealth = true ;
2022-04-25 00:25:45 +00:00
// Used for playAnim if opted in for.
int32_t difference = static_cast < int32_t > ( std : : abs ( m_fMaxImagination - value ) ) ;
2021-12-05 17:54:36 +00:00
m_fMaxImagination = value ;
if ( m_iImagination > m_fMaxImagination ) {
m_iImagination = m_fMaxImagination ;
}
if ( playAnim ) {
// Now update the player bar
if ( ! m_Parent - > GetParentUser ( ) ) return ;
AMFStringValue * amount = new AMFStringValue ( ) ;
2022-04-25 00:25:45 +00:00
amount - > SetStringValue ( std : : to_string ( difference ) ) ;
2021-12-05 17:54:36 +00:00
AMFStringValue * type = new AMFStringValue ( ) ;
type - > SetStringValue ( " imagination " ) ;
AMFArrayValue args ;
args . InsertValue ( " amount " , amount ) ;
args . InsertValue ( " type " , type ) ;
2022-01-15 19:02:14 +00:00
2022-07-22 05:26:09 +00:00
GameMessages : : SendUIMessageServerToSingleClient ( m_Parent , m_Parent - > GetParentUser ( ) - > GetSystemAddress ( ) , " MaxPlayerBarUpdate " , & args ) ;
2021-12-05 17:54:36 +00:00
}
2022-04-25 00:25:45 +00:00
EntityManager : : Instance ( ) - > SerializeEntity ( m_Parent ) ;
2021-12-05 17:54:36 +00:00
}
void DestroyableComponent : : SetDamageToAbsorb ( int32_t value ) {
m_DirtyHealth = true ;
m_DamageToAbsorb = value ;
}
2022-01-15 19:02:14 +00:00
void DestroyableComponent : : SetDamageReduction ( int32_t value ) {
2021-12-05 17:54:36 +00:00
m_DirtyHealth = true ;
m_DamageReduction = value ;
}
void DestroyableComponent : : SetIsImmune ( bool value ) {
m_DirtyHealth = true ;
2023-01-07 05:59:19 +00:00
m_ImmuneToBasicAttackCount = value ? 1 : 0 ;
2021-12-05 17:54:36 +00:00
}
2022-01-15 19:02:14 +00:00
void DestroyableComponent : : SetIsGMImmune ( bool value ) {
2021-12-05 17:54:36 +00:00
m_DirtyHealth = true ;
m_IsGMImmune = value ;
}
void DestroyableComponent : : SetIsShielded ( bool value ) {
m_DirtyHealth = true ;
m_IsShielded = value ;
}
void DestroyableComponent : : AddFaction ( const int32_t factionID , const bool ignoreChecks ) {
// Ignore factionID -1
if ( factionID = = - 1 & & ! ignoreChecks ) {
return ;
}
m_FactionIDs . push_back ( factionID ) ;
m_DirtyHealth = true ;
2022-01-13 03:48:27 +00:00
auto query = CDClientDatabase : : CreatePreppedStmt (
" SELECT enemyList FROM Factions WHERE faction = ?; " ) ;
2022-01-15 19:02:14 +00:00
query . bind ( 1 , ( int ) factionID ) ;
2022-01-13 03:48:27 +00:00
auto result = query . execQuery ( ) ;
2021-12-05 17:54:36 +00:00
if ( result . eof ( ) ) return ;
if ( result . fieldIsNull ( 0 ) ) return ;
const auto * list_string = result . getStringField ( 0 ) ;
std : : stringstream ss ( list_string ) ;
std : : string token ;
2022-01-15 19:02:14 +00:00
2021-12-05 17:54:36 +00:00
while ( std : : getline ( ss , token , ' , ' ) ) {
if ( token . empty ( ) ) continue ;
2022-01-15 19:02:14 +00:00
2021-12-05 17:54:36 +00:00
auto id = std : : stoi ( token ) ;
auto exclude = std : : find ( m_FactionIDs . begin ( ) , m_FactionIDs . end ( ) , id ) ! = m_FactionIDs . end ( ) ;
if ( ! exclude ) {
exclude = std : : find ( m_EnemyFactionIDs . begin ( ) , m_EnemyFactionIDs . end ( ) , id ) ! = m_EnemyFactionIDs . end ( ) ;
}
if ( exclude ) {
continue ;
}
2022-01-15 19:02:14 +00:00
2021-12-05 17:54:36 +00:00
AddEnemyFaction ( id ) ;
}
result . finalize ( ) ;
}
bool DestroyableComponent : : IsEnemy ( const Entity * other ) const {
const auto * otherDestroyableComponent = other - > GetComponent < DestroyableComponent > ( ) ;
if ( otherDestroyableComponent ! = nullptr ) {
for ( const auto enemyFaction : m_EnemyFactionIDs ) {
for ( const auto otherFaction : otherDestroyableComponent - > GetFactionIDs ( ) ) {
if ( enemyFaction = = otherFaction )
return true ;
}
}
}
return false ;
}
bool DestroyableComponent : : IsFriend ( const Entity * other ) const {
const auto * otherDestroyableComponent = other - > GetComponent < DestroyableComponent > ( ) ;
if ( otherDestroyableComponent ! = nullptr ) {
for ( const auto enemyFaction : m_EnemyFactionIDs ) {
for ( const auto otherFaction : otherDestroyableComponent - > GetFactionIDs ( ) ) {
if ( enemyFaction = = otherFaction )
return false ;
}
}
return true ;
}
return false ;
}
void DestroyableComponent : : AddEnemyFaction ( int32_t factionID ) {
m_EnemyFactionIDs . push_back ( factionID ) ;
}
void DestroyableComponent : : SetIsSmashable ( bool value ) {
m_DirtyHealth = true ;
m_IsSmashable = value ;
}
void DestroyableComponent : : SetAttacksToBlock ( const uint32_t value ) {
m_AttacksToBlock = value ;
}
bool DestroyableComponent : : IsImmune ( ) const {
2023-01-07 05:59:19 +00:00
return m_IsGMImmune | | m_ImmuneToBasicAttackCount > 0 ;
2021-12-05 17:54:36 +00:00
}
bool DestroyableComponent : : IsKnockbackImmune ( ) const {
auto * characterComponent = m_Parent - > GetComponent < CharacterComponent > ( ) ;
auto * inventoryComponent = m_Parent - > GetComponent < InventoryComponent > ( ) ;
if ( characterComponent ! = nullptr & & inventoryComponent ! = nullptr & & characterComponent - > GetCurrentActivity ( ) = = eGameActivities : : ACTIVITY_QUICKBUILDING ) {
const auto hasPassive = inventoryComponent - > HasAnyPassive ( {
ItemSetPassiveAbilityID : : EngineerRank2 , ItemSetPassiveAbilityID : : EngineerRank3 ,
ItemSetPassiveAbilityID : : SummonerRank2 , ItemSetPassiveAbilityID : : SummonerRank3 ,
ItemSetPassiveAbilityID : : InventorRank2 , ItemSetPassiveAbilityID : : InventorRank3 ,
} , 5 ) ;
if ( hasPassive ) {
return true ;
}
}
return IsImmune ( ) | | m_IsShielded | | m_AttacksToBlock > 0 ;
}
bool DestroyableComponent : : HasFaction ( int32_t factionID ) const {
return std : : find ( m_FactionIDs . begin ( ) , m_FactionIDs . end ( ) , factionID ) ! = m_FactionIDs . end ( ) ;
}
LWOOBJID DestroyableComponent : : GetKillerID ( ) const {
return m_KillerID ;
}
Entity * DestroyableComponent : : GetKiller ( ) const {
return EntityManager : : Instance ( ) - > GetEntity ( m_KillerID ) ;
}
2022-01-02 20:37:03 +00:00
bool DestroyableComponent : : CheckValidity ( const LWOOBJID target , const bool ignoreFactions , const bool targetEnemy , const bool targetFriend ) const {
2022-01-02 21:01:29 +00:00
auto * targetEntity = EntityManager : : Instance ( ) - > GetEntity ( target ) ;
2021-12-05 17:54:36 +00:00
2022-01-02 21:01:29 +00:00
if ( targetEntity = = nullptr ) {
2022-07-25 02:26:51 +00:00
Game : : logger - > Log ( " DestroyableComponent " , " Invalid entity for checking validity (%llu)! " , target ) ;
2021-12-05 17:54:36 +00:00
return false ;
}
2022-01-02 21:01:29 +00:00
auto * targetDestroyable = targetEntity - > GetComponent < DestroyableComponent > ( ) ;
2021-12-05 17:54:36 +00:00
2022-01-02 21:01:29 +00:00
if ( targetDestroyable = = nullptr ) {
2021-12-05 17:54:36 +00:00
return false ;
}
2022-01-02 21:01:29 +00:00
auto * targetQuickbuild = targetEntity - > GetComponent < RebuildComponent > ( ) ;
2021-12-05 17:54:36 +00:00
2022-01-02 21:01:29 +00:00
if ( targetQuickbuild ! = nullptr ) {
const auto state = targetQuickbuild - > GetState ( ) ;
2022-01-15 19:02:14 +00:00
2021-12-05 17:54:36 +00:00
if ( state ! = REBUILD_COMPLETED ) {
return false ;
}
}
if ( ignoreFactions ) {
return true ;
}
2022-01-04 18:11:23 +00:00
// Get if the target entity is an enemy and friend
2022-01-02 21:01:29 +00:00
bool isEnemy = IsEnemy ( targetEntity ) ;
2022-01-04 18:11:23 +00:00
bool isFriend = IsFriend ( targetEntity ) ;
2021-12-05 17:54:36 +00:00
2022-01-02 21:01:29 +00:00
// Return true if the target type matches what we are targeting
2022-01-04 18:11:23 +00:00
return ( isEnemy & & targetEnemy ) | | ( isFriend & & targetFriend ) ;
2021-12-05 17:54:36 +00:00
}
void DestroyableComponent : : Heal ( const uint32_t health ) {
auto current = static_cast < uint32_t > ( GetHealth ( ) ) ;
const auto max = static_cast < uint32_t > ( GetMaxHealth ( ) ) ;
current + = health ;
current = std : : min ( current , max ) ;
SetHealth ( current ) ;
EntityManager : : Instance ( ) - > SerializeEntity ( m_Parent ) ;
}
void DestroyableComponent : : Imagine ( const int32_t deltaImagination ) {
auto current = static_cast < int32_t > ( GetImagination ( ) ) ;
const auto max = static_cast < int32_t > ( GetMaxImagination ( ) ) ;
2022-01-15 19:02:14 +00:00
2021-12-05 17:54:36 +00:00
current + = deltaImagination ;
current = std : : min ( current , max ) ;
if ( current < 0 ) {
current = 0 ;
}
SetImagination ( current ) ;
EntityManager : : Instance ( ) - > SerializeEntity ( m_Parent ) ;
}
void DestroyableComponent : : Repair ( const uint32_t armor ) {
auto current = static_cast < uint32_t > ( GetArmor ( ) ) ;
const auto max = static_cast < uint32_t > ( GetMaxArmor ( ) ) ;
current + = armor ;
current = std : : min ( current , max ) ;
SetArmor ( current ) ;
EntityManager : : Instance ( ) - > SerializeEntity ( m_Parent ) ;
}
2022-04-25 10:25:07 +00:00
void DestroyableComponent : : Damage ( uint32_t damage , const LWOOBJID source , uint32_t skillID , bool echo ) {
2021-12-05 17:54:36 +00:00
if ( GetHealth ( ) < = 0 ) {
return ;
}
if ( IsImmune ( ) ) {
return ;
}
if ( m_AttacksToBlock > 0 ) {
m_AttacksToBlock - - ;
return ;
}
// If this entity has damage reduction, reduce the damage to a minimum of 1
if ( m_DamageReduction > 0 & & damage > 0 ) {
if ( damage > m_DamageReduction ) {
damage - = m_DamageReduction ;
} else {
damage = 1 ;
}
}
const auto sourceDamage = damage ;
auto absorb = static_cast < uint32_t > ( GetDamageToAbsorb ( ) ) ;
auto armor = static_cast < uint32_t > ( GetArmor ( ) ) ;
auto health = static_cast < uint32_t > ( GetHealth ( ) ) ;
const auto absorbDamage = std : : min ( damage , absorb ) ;
damage - = absorbDamage ;
absorb - = absorbDamage ;
2022-01-15 19:02:14 +00:00
2021-12-05 17:54:36 +00:00
const auto armorDamage = std : : min ( damage , armor ) ;
damage - = armorDamage ;
armor - = armorDamage ;
health - = std : : min ( damage , health ) ;
SetDamageToAbsorb ( absorb ) ;
SetArmor ( armor ) ;
SetHealth ( health ) ;
SetIsShielded ( absorb > 0 ) ;
2022-09-02 18:49:19 +00:00
// Dismount on the possessable hit
auto possessable = m_Parent - > GetComponent < PossessableComponent > ( ) ;
if ( possessable & & possessable - > GetDepossessOnHit ( ) ) {
possessable - > Dismount ( ) ;
}
// Dismount on the possessor hit
auto possessor = m_Parent - > GetComponent < PossessorComponent > ( ) ;
if ( possessor ) {
auto possessableId = possessor - > GetPossessable ( ) ;
if ( possessableId ! = LWOOBJID_EMPTY ) {
auto possessable = EntityManager : : Instance ( ) - > GetEntity ( possessableId ) ;
if ( possessable ) {
possessor - > Dismount ( possessable ) ;
}
}
}
2021-12-05 17:54:36 +00:00
if ( m_Parent - > GetLOT ( ) ! = 1 ) {
echo = true ;
}
if ( echo ) {
EntityManager : : Instance ( ) - > SerializeEntity ( m_Parent ) ;
}
2022-01-15 19:02:14 +00:00
2021-12-05 17:54:36 +00:00
auto * attacker = EntityManager : : Instance ( ) - > GetEntity ( source ) ;
m_Parent - > OnHit ( attacker ) ;
m_Parent - > OnHitOrHealResult ( attacker , sourceDamage ) ;
2022-12-21 22:33:41 +00:00
NotifySubscribers ( attacker , sourceDamage ) ;
2021-12-05 17:54:36 +00:00
for ( const auto & cb : m_OnHitCallbacks ) {
cb ( attacker ) ;
}
if ( health ! = 0 ) {
auto * combatComponent = m_Parent - > GetComponent < BaseCombatAIComponent > ( ) ;
if ( combatComponent ! = nullptr ) {
combatComponent - > Taunt ( source , sourceDamage * 10 ) ; // * 10 is arbatrary
}
return ;
}
2022-04-25 10:25:07 +00:00
Smash ( source , eKillType : : VIOLENT , u " " , skillID ) ;
2021-12-05 17:54:36 +00:00
}
2022-12-21 22:33:41 +00:00
void DestroyableComponent : : Subscribe ( LWOOBJID scriptObjId , CppScripts : : Script * scriptToAdd ) {
m_SubscribedScripts . insert ( std : : make_pair ( scriptObjId , scriptToAdd ) ) ;
Game : : logger - > LogDebug ( " DestroyableComponent " , " Added script %llu to entity %llu " , scriptObjId , m_Parent - > GetObjectID ( ) ) ;
Game : : logger - > LogDebug ( " DestroyableComponent " , " Number of subscribed scripts %i " , m_SubscribedScripts . size ( ) ) ;
}
void DestroyableComponent : : Unsubscribe ( LWOOBJID scriptObjId ) {
auto foundScript = m_SubscribedScripts . find ( scriptObjId ) ;
if ( foundScript ! = m_SubscribedScripts . end ( ) ) {
m_SubscribedScripts . erase ( foundScript ) ;
Game : : logger - > LogDebug ( " DestroyableComponent " , " Removed script %llu from entity %llu " , scriptObjId , m_Parent - > GetObjectID ( ) ) ;
} else {
Game : : logger - > LogDebug ( " DestroyableComponent " , " Tried to remove a script for Entity %llu but script %llu didnt exist " , m_Parent - > GetObjectID ( ) , scriptObjId ) ;
}
Game : : logger - > LogDebug ( " DestroyableComponent " , " Number of subscribed scripts %i " , m_SubscribedScripts . size ( ) ) ;
}
void DestroyableComponent : : NotifySubscribers ( Entity * attacker , uint32_t damage ) {
for ( auto script : m_SubscribedScripts ) {
script . second - > NotifyHitOrHealResult ( m_Parent , attacker , damage ) ;
}
}
2022-04-25 10:25:07 +00:00
void DestroyableComponent : : Smash ( const LWOOBJID source , const eKillType killType , const std : : u16string & deathType , uint32_t skillID ) {
2021-12-05 17:54:36 +00:00
if ( m_iHealth > 0 ) {
SetArmor ( 0 ) ;
SetHealth ( 0 ) ;
EntityManager : : Instance ( ) - > SerializeEntity ( m_Parent ) ;
}
m_KillerID = source ;
auto * owner = EntityManager : : Instance ( ) - > GetEntity ( source ) ;
if ( owner ! = nullptr ) {
owner = owner - > GetOwner ( ) ; // If the owner is overwritten, we collect that here
2022-06-12 03:50:01 +00:00
auto * team = TeamManager : : Instance ( ) - > GetTeam ( owner - > GetObjectID ( ) ) ;
2021-12-05 17:54:36 +00:00
const auto isEnemy = m_Parent - > GetComponent < BaseCombatAIComponent > ( ) ! = nullptr ;
auto * inventoryComponent = owner - > GetComponent < InventoryComponent > ( ) ;
if ( inventoryComponent ! = nullptr & & isEnemy ) {
2022-12-24 02:05:30 +00:00
inventoryComponent - > TriggerPassiveAbility ( PassiveAbilityTrigger : : EnemySmashed , m_Parent ) ;
2021-12-05 17:54:36 +00:00
}
auto * missions = owner - > GetComponent < MissionComponent > ( ) ;
if ( missions ! = nullptr ) {
2023-01-07 05:06:24 +00:00
if ( team ! = nullptr ) {
2021-12-05 17:54:36 +00:00
for ( const auto memberId : team - > members ) {
auto * member = EntityManager : : Instance ( ) - > GetEntity ( memberId ) ;
if ( member = = nullptr ) continue ;
auto * memberMissions = member - > GetComponent < MissionComponent > ( ) ;
if ( memberMissions = = nullptr ) continue ;
memberMissions - > Progress ( MissionTaskType : : MISSION_TASK_TYPE_SMASH , m_Parent - > GetLOT ( ) ) ;
2022-04-25 10:25:07 +00:00
memberMissions - > Progress ( MissionTaskType : : MISSION_TASK_TYPE_SKILL , m_Parent - > GetLOT ( ) , skillID ) ;
2021-12-05 17:54:36 +00:00
}
} else {
missions - > Progress ( MissionTaskType : : MISSION_TASK_TYPE_SMASH , m_Parent - > GetLOT ( ) ) ;
2022-04-25 10:25:07 +00:00
missions - > Progress ( MissionTaskType : : MISSION_TASK_TYPE_SKILL , m_Parent - > GetLOT ( ) , skillID ) ;
2021-12-05 17:54:36 +00:00
}
}
}
2022-01-15 19:02:14 +00:00
2021-12-05 17:54:36 +00:00
const auto isPlayer = m_Parent - > IsPlayer ( ) ;
2022-04-25 10:25:07 +00:00
GameMessages : : SendDie ( m_Parent , source , source , true , killType , deathType , 0 , 0 , 0 , isPlayer , false , 1 ) ;
2021-12-05 17:54:36 +00:00
//NANI?!
if ( ! isPlayer ) {
if ( owner ! = nullptr ) {
auto * team = TeamManager : : Instance ( ) - > GetTeam ( owner - > GetObjectID ( ) ) ;
if ( team ! = nullptr & & m_Parent - > GetComponent < BaseCombatAIComponent > ( ) ! = nullptr ) {
LWOOBJID specificOwner = LWOOBJID_EMPTY ;
2021-12-24 00:25:52 +00:00
auto * scriptedActivityComponent = m_Parent - > GetComponent < ScriptedActivityComponent > ( ) ;
uint32_t teamSize = team - > members . size ( ) ;
uint32_t lootMatrixId = GetLootMatrixID ( ) ;
2021-12-05 17:54:36 +00:00
2021-12-24 00:25:52 +00:00
if ( scriptedActivityComponent ) {
lootMatrixId = scriptedActivityComponent - > GetLootMatrixForTeamSize ( teamSize ) ;
2021-12-05 17:54:36 +00:00
}
2021-12-24 00:25:52 +00:00
if ( team - > lootOption = = 0 ) { // Round robin
specificOwner = TeamManager : : Instance ( ) - > GetNextLootOwner ( team ) ;
2021-12-05 17:54:36 +00:00
2021-12-24 00:25:52 +00:00
auto * member = EntityManager : : Instance ( ) - > GetEntity ( specificOwner ) ;
2021-12-05 17:54:36 +00:00
2021-12-24 00:25:52 +00:00
if ( member ) LootGenerator : : Instance ( ) . DropLoot ( member , m_Parent , lootMatrixId , GetMinCoins ( ) , GetMaxCoins ( ) ) ;
} else {
for ( const auto memberId : team - > members ) { // Free for all
auto * member = EntityManager : : Instance ( ) - > GetEntity ( memberId ) ;
if ( member = = nullptr ) continue ;
2021-12-05 17:54:36 +00:00
2022-01-15 19:02:14 +00:00
LootGenerator : : Instance ( ) . DropLoot ( member , m_Parent , lootMatrixId , GetMinCoins ( ) , GetMaxCoins ( ) ) ;
2021-12-24 00:25:52 +00:00
}
2021-12-05 17:54:36 +00:00
}
2021-12-24 00:25:52 +00:00
} else { // drop loot for non team user
2021-12-20 10:25:45 +00:00
LootGenerator : : Instance ( ) . DropLoot ( owner , m_Parent , GetLootMatrixID ( ) , GetMinCoins ( ) , GetMaxCoins ( ) ) ;
2021-12-05 17:54:36 +00:00
}
}
} else {
2022-02-05 12:27:24 +00:00
//Check if this zone allows coin drops
2022-07-25 02:26:51 +00:00
if ( dZoneManager : : Instance ( ) - > GetPlayerLoseCoinOnDeath ( ) ) {
2022-02-05 12:27:24 +00:00
auto * character = m_Parent - > GetCharacter ( ) ;
uint64_t coinsTotal = character - > GetCoins ( ) ;
2023-01-02 00:36:10 +00:00
const uint64_t minCoinsToLose = dZoneManager : : Instance ( ) - > GetWorldConfig ( ) - > coinsLostOnDeathMin ;
if ( coinsTotal > = minCoinsToLose ) {
2023-01-01 12:51:22 +00:00
const uint64_t maxCoinsToLose = dZoneManager : : Instance ( ) - > GetWorldConfig ( ) - > coinsLostOnDeathMax ;
const float coinPercentageToLose = dZoneManager : : Instance ( ) - > GetWorldConfig ( ) - > coinsLostOnDeathPercent ;
2021-12-05 17:54:36 +00:00
2023-01-01 12:51:22 +00:00
uint64_t coinsToLose = std : : max ( static_cast < uint64_t > ( coinsTotal * coinPercentageToLose ) , minCoinsToLose ) ;
coinsToLose = std : : min ( maxCoinsToLose , coinsToLose ) ;
2022-01-15 19:02:14 +00:00
2023-01-01 12:51:22 +00:00
coinsTotal - = coinsToLose ;
2021-12-05 17:54:36 +00:00
2023-01-01 12:51:22 +00:00
LootGenerator : : Instance ( ) . DropLoot ( m_Parent , m_Parent , - 1 , coinsToLose , coinsToLose ) ;
2022-04-24 03:32:31 +00:00
character - > SetCoins ( coinsTotal , eLootSourceType : : LOOT_SOURCE_PICKUP ) ;
2022-02-05 12:27:24 +00:00
}
}
2021-12-05 17:54:36 +00:00
Entity * zoneControl = EntityManager : : Instance ( ) - > GetZoneControlEntity ( ) ;
for ( CppScripts : : Script * script : CppScripts : : GetEntityScripts ( zoneControl ) ) {
script - > OnPlayerDied ( zoneControl , m_Parent ) ;
}
std : : vector < Entity * > scriptedActs = EntityManager : : Instance ( ) - > GetEntitiesByComponent ( COMPONENT_TYPE_SCRIPTED_ACTIVITY ) ;
for ( Entity * scriptEntity : scriptedActs ) {
if ( scriptEntity - > GetObjectID ( ) ! = zoneControl - > GetObjectID ( ) ) { // Don't want to trigger twice on instance worlds
for ( CppScripts : : Script * script : CppScripts : : GetEntityScripts ( scriptEntity ) ) {
script - > OnPlayerDied ( scriptEntity , m_Parent ) ;
}
}
}
}
m_Parent - > Kill ( owner ) ;
}
2022-07-19 21:51:35 +00:00
void DestroyableComponent : : SetFaction ( int32_t factionID , bool ignoreChecks ) {
2021-12-05 17:54:36 +00:00
m_FactionIDs . clear ( ) ;
m_EnemyFactionIDs . clear ( ) ;
2022-07-19 21:51:35 +00:00
AddFaction ( factionID , ignoreChecks ) ;
2021-12-05 17:54:36 +00:00
}
2023-01-07 05:59:19 +00:00
void DestroyableComponent : : SetStatusImmunity (
const eStateChangeType state ,
const bool bImmuneToBasicAttack ,
const bool bImmuneToDamageOverTime ,
const bool bImmuneToKnockback ,
const bool bImmuneToInterrupt ,
const bool bImmuneToSpeed ,
const bool bImmuneToImaginationGain ,
const bool bImmuneToImaginationLoss ,
const bool bImmuneToQuickbuildInterrupt ,
const bool bImmuneToPullToPoint ) {
if ( state = = eStateChangeType : : POP ) {
if ( bImmuneToBasicAttack & & m_ImmuneToBasicAttackCount > 0 ) m_ImmuneToBasicAttackCount - = 1 ;
if ( bImmuneToDamageOverTime & & m_ImmuneToDamageOverTimeCount > 0 ) m_ImmuneToDamageOverTimeCount - = 1 ;
if ( bImmuneToKnockback & & m_ImmuneToKnockbackCount > 0 ) m_ImmuneToKnockbackCount - = 1 ;
if ( bImmuneToInterrupt & & m_ImmuneToInterruptCount > 0 ) m_ImmuneToInterruptCount - = 1 ;
if ( bImmuneToSpeed & & m_ImmuneToSpeedCount > 0 ) m_ImmuneToSpeedCount - = 1 ;
if ( bImmuneToImaginationGain & & m_ImmuneToImaginationGainCount > 0 ) m_ImmuneToImaginationGainCount - = 1 ;
if ( bImmuneToImaginationLoss & & m_ImmuneToImaginationLossCount > 0 ) m_ImmuneToImaginationLossCount - = 1 ;
if ( bImmuneToQuickbuildInterrupt & & m_ImmuneToQuickbuildInterruptCount > 0 ) m_ImmuneToQuickbuildInterruptCount - = 1 ;
if ( bImmuneToPullToPoint & & m_ImmuneToPullToPointCount > 0 ) m_ImmuneToPullToPointCount - = 1 ;
} else if ( state = = eStateChangeType : : PUSH ) {
if ( bImmuneToBasicAttack ) m_ImmuneToBasicAttackCount + = 1 ;
if ( bImmuneToDamageOverTime ) m_ImmuneToDamageOverTimeCount + = 1 ;
if ( bImmuneToKnockback ) m_ImmuneToKnockbackCount + = 1 ;
if ( bImmuneToInterrupt ) m_ImmuneToInterruptCount + = 1 ;
if ( bImmuneToSpeed ) m_ImmuneToSpeedCount + = 1 ;
if ( bImmuneToImaginationGain ) m_ImmuneToImaginationGainCount + = 1 ;
if ( bImmuneToImaginationLoss ) m_ImmuneToImaginationLossCount + = 1 ;
if ( bImmuneToQuickbuildInterrupt ) m_ImmuneToQuickbuildInterruptCount + = 1 ;
if ( bImmuneToPullToPoint ) m_ImmuneToPullToPointCount + = 1 ;
}
GameMessages : : SendSetStatusImmunity (
m_Parent - > GetObjectID ( ) , state , m_Parent - > GetSystemAddress ( ) ,
bImmuneToBasicAttack ,
bImmuneToDamageOverTime ,
bImmuneToKnockback ,
bImmuneToInterrupt ,
bImmuneToSpeed ,
bImmuneToImaginationGain ,
bImmuneToImaginationLoss ,
bImmuneToQuickbuildInterrupt ,
bImmuneToPullToPoint
) ;
2021-12-05 17:54:36 +00:00
}
2022-01-15 19:02:14 +00:00
void DestroyableComponent : : FixStats ( ) {
2021-12-05 17:54:36 +00:00
auto * entity = GetParent ( ) ;
if ( entity = = nullptr ) return ;
// Reset skill component and buff component
auto * skillComponent = entity - > GetComponent < SkillComponent > ( ) ;
auto * buffComponent = entity - > GetComponent < BuffComponent > ( ) ;
auto * missionComponent = entity - > GetComponent < MissionComponent > ( ) ;
auto * inventoryComponent = entity - > GetComponent < InventoryComponent > ( ) ;
auto * destroyableComponent = entity - > GetComponent < DestroyableComponent > ( ) ;
// If any of the components are nullptr, return
if ( skillComponent = = nullptr | | buffComponent = = nullptr | | missionComponent = = nullptr | | inventoryComponent = = nullptr | | destroyableComponent = = nullptr ) {
return ;
}
// Save the current stats
int32_t currentHealth = destroyableComponent - > GetHealth ( ) ;
int32_t currentArmor = destroyableComponent - > GetArmor ( ) ;
int32_t currentImagination = destroyableComponent - > GetImagination ( ) ;
// Unequip all items
auto equipped = inventoryComponent - > GetEquippedItems ( ) ;
for ( auto & equippedItem : equipped ) {
// Get the item with the item ID
auto * item = inventoryComponent - > FindItemById ( equippedItem . second . id ) ;
if ( item = = nullptr ) {
continue ;
}
// Unequip the item
item - > UnEquip ( ) ;
}
// Base stats
int32_t maxHealth = 4 ;
int32_t maxArmor = 0 ;
int32_t maxImagination = 0 ;
// Go through all completed missions and add the reward stats
for ( auto & pair : missionComponent - > GetMissions ( ) ) {
auto * mission = pair . second ;
if ( ! mission - > IsComplete ( ) ) {
continue ;
}
// Add the stats
const auto & info = mission - > GetClientInfo ( ) ;
2022-01-15 19:02:14 +00:00
2021-12-05 17:54:36 +00:00
maxHealth + = info . reward_maxhealth ;
maxImagination + = info . reward_maximagination ;
}
// Set the base stats
destroyableComponent - > SetMaxHealth ( maxHealth ) ;
destroyableComponent - > SetMaxArmor ( maxArmor ) ;
destroyableComponent - > SetMaxImagination ( maxImagination ) ;
// Re-apply all buffs
buffComponent - > ReApplyBuffs ( ) ;
// Requip all items
for ( auto & equippedItem : equipped ) {
// Get the item with the item ID
auto * item = inventoryComponent - > FindItemById ( equippedItem . second . id ) ;
if ( item = = nullptr ) {
continue ;
}
// Equip the item
item - > Equip ( ) ;
}
// Fetch correct max stats after everything is done
maxHealth = destroyableComponent - > GetMaxHealth ( ) ;
maxArmor = destroyableComponent - > GetMaxArmor ( ) ;
maxImagination = destroyableComponent - > GetMaxImagination ( ) ;
// If any of the current stats are more than their max, set them to the max
if ( currentHealth > maxHealth ) currentHealth = maxHealth ;
if ( currentArmor > maxArmor ) currentArmor = maxArmor ;
if ( currentImagination > maxImagination ) currentImagination = maxImagination ;
// Restore current stats
destroyableComponent - > SetHealth ( currentHealth ) ;
destroyableComponent - > SetArmor ( currentArmor ) ;
destroyableComponent - > SetImagination ( currentImagination ) ;
// Serialize the entity
EntityManager : : Instance ( ) - > SerializeEntity ( entity ) ;
}
void DestroyableComponent : : AddOnHitCallback ( const std : : function < void ( Entity * ) > & callback ) {
m_OnHitCallbacks . push_back ( callback ) ;
}