2021-12-05 17:54:36 +00:00
# include "DestroyableComponent.h"
# include <BitStream.h>
# include "dLogger.h"
# include "Game.h"
2023-01-11 19:08:54 +00:00
# include "dConfig.h"
2021-12-05 17:54:36 +00:00
2023-05-13 22:22:00 +00:00
# include "Amf3.h"
# include "AmfSerialize.h"
2021-12-05 17:54:36 +00:00
# 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
# include "eMissionTaskType.h"
2023-05-02 22:39:21 +00:00
# include "eStateChangeType.h"
# include "eGameActivity.h"
2023-07-15 08:54:41 +00:00
# include "LevelProgressionComponent.h"
# include "ResistanceProfile.h"
# include "DamageProfile.h"
2021-12-05 17:54:36 +00:00
2023-03-17 14:36:21 +00:00
# include "CDComponentsRegistryTable.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 ;
2023-06-14 22:44:22 +00:00
m_IsModuleAssembly = m_Parent - > HasComponent ( eReplicaComponentType : : MODULE_ASSEMBLY ) ;
2021-12-05 17:54:36 +00:00
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-07-15 08:54:41 +00:00
m_DirtyStats = true ;
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 ;
2023-07-15 08:54:41 +00:00
m_ResistanceProfile = ResistanceProfile : : FindResistanceProfile ( m_Parent - > GetLOT ( ) ) ;
2021-12-05 17:54:36 +00:00
}
DestroyableComponent : : ~ DestroyableComponent ( ) {
}
void DestroyableComponent : : Reinitialize ( LOT templateID ) {
2023-03-17 14:36:21 +00:00
CDComponentsRegistryTable * compRegistryTable = CDClientManager : : Instance ( ) . GetTable < CDComponentsRegistryTable > ( ) ;
2021-12-05 17:54:36 +00:00
2023-03-04 07:16:37 +00:00
int32_t buffComponentID = compRegistryTable - > GetByIDAndType ( templateID , eReplicaComponentType : : BUFF ) ;
int32_t collectibleComponentID = compRegistryTable - > GetByIDAndType ( templateID , eReplicaComponentType : : COLLECTIBLE ) ;
int32_t rebuildComponentID = compRegistryTable - > GetByIDAndType ( templateID , eReplicaComponentType : : QUICK_BUILD ) ;
2021-12-05 17:54:36 +00:00
int32_t componentID = 0 ;
if ( collectibleComponentID > 0 ) componentID = collectibleComponentID ;
if ( rebuildComponentID > 0 ) componentID = rebuildComponentID ;
if ( buffComponentID > 0 ) componentID = buffComponentID ;
2023-03-17 14:36:21 +00:00
CDDestructibleComponentTable * destCompTable = CDClientManager : : Instance ( ) . GetTable < CDDestructibleComponentTable > ( ) ;
2021-12-05 17:54:36 +00:00
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 ) ; } ) ;
2023-07-15 08:54:41 +00:00
m_Info = destCompData [ 0 ] ;
2021-12-05 17:54:36 +00:00
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 {
2023-07-15 08:54:41 +00:00
m_Info = { } ;
m_Info . life = 1 ;
m_Info . imagination = 0 ;
m_Info . armor = 0 ;
2021-12-05 17:54:36 +00:00
SetHealth ( 1 ) ;
SetImagination ( 0 ) ;
SetArmor ( 0 ) ;
SetMaxHealth ( 1 ) ;
SetMaxImagination ( 0 ) ;
SetMaxArmor ( 0 ) ;
SetIsSmashable ( true ) ;
}
2023-07-15 08:54:41 +00:00
ComputeBaseStats ( ) ;
if ( ! m_Parent - > IsPlayer ( ) )
{
SetHealth ( GetMaxHealth ( ) ) ;
SetImagination ( GetMaxImagination ( ) ) ;
SetArmor ( GetMaxArmor ( ) ) ;
}
2021-12-05 17:54:36 +00:00
}
2023-08-10 21:33:15 +00:00
void DestroyableComponent : : Serialize ( RakNet : : BitStream * outBitStream , bool bIsInitialUpdate ) {
2021-12-05 17:54:36 +00:00
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 ) {
2023-06-14 22:44:22 +00:00
outBitStream - > Write ( m_IsModuleAssembly ) ;
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 ;
AMFArrayValue args ;
2023-05-13 22:22:00 +00:00
args . Insert ( " amount " , std : : to_string ( difference ) ) ;
args . Insert ( " type " , " health " ) ;
2022-01-15 19:02:14 +00:00
2023-05-13 22:22:00 +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
2023-07-15 20:56:33 +00:00
Game : : entityManager - > 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 ;
AMFArrayValue args ;
2023-05-13 22:22:00 +00:00
args . Insert ( " amount " , std : : to_string ( value ) ) ;
args . Insert ( " type " , " armor " ) ;
2021-12-05 17:54:36 +00:00
2023-05-13 22:22:00 +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
2023-07-15 20:56:33 +00:00
Game : : entityManager - > 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 ;
AMFArrayValue args ;
2023-05-13 22:22:00 +00:00
args . Insert ( " amount " , std : : to_string ( difference ) ) ;
args . Insert ( " type " , " imagination " ) ;
2022-01-15 19:02:14 +00:00
2023-05-13 22:22:00 +00:00
GameMessages : : SendUIMessageServerToSingleClient ( m_Parent , m_Parent - > GetParentUser ( ) - > GetSystemAddress ( ) , " MaxPlayerBarUpdate " , args ) ;
2021-12-05 17:54:36 +00:00
}
2023-07-15 20:56:33 +00:00
Game : : entityManager - > 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 > ( ) ;
2023-05-02 22:39:21 +00:00
if ( characterComponent ! = nullptr & & inventoryComponent ! = nullptr & & characterComponent - > GetCurrentActivity ( ) = = eGameActivity : : QUICKBUILDING ) {
2021-12-05 17:54:36 +00:00
const auto hasPassive = inventoryComponent - > HasAnyPassive ( {
eItemSetPassiveAbilityID : : EngineerRank2 , eItemSetPassiveAbilityID : : EngineerRank3 ,
eItemSetPassiveAbilityID : : SummonerRank2 , eItemSetPassiveAbilityID : : SummonerRank3 ,
eItemSetPassiveAbilityID : : InventorRank2 , eItemSetPassiveAbilityID : : 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 {
2023-07-15 20:56:33 +00:00
return Game : : entityManager - > GetEntity ( m_KillerID ) ;
2021-12-05 17:54:36 +00:00
}
2022-01-02 20:37:03 +00:00
bool DestroyableComponent : : CheckValidity ( const LWOOBJID target , const bool ignoreFactions , const bool targetEnemy , const bool targetFriend ) const {
2023-07-15 20:56:33 +00:00
auto * targetEntity = Game : : entityManager - > 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
2023-05-02 22:39:21 +00:00
if ( state ! = eRebuildState : : COMPLETED ) {
2021-12-05 17:54:36 +00:00
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 ) ;
2023-07-15 20:56:33 +00:00
Game : : entityManager - > SerializeEntity ( m_Parent ) ;
2021-12-05 17:54:36 +00:00
}
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 ) ;
2023-07-15 20:56:33 +00:00
Game : : entityManager - > SerializeEntity ( m_Parent ) ;
2021-12-05 17:54:36 +00:00
}
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 ) ;
2023-07-15 20:56:33 +00:00
Game : : entityManager - > SerializeEntity ( m_Parent ) ;
2021-12-05 17:54:36 +00:00
}
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 ) {
2023-07-15 20:56:33 +00:00
auto possessable = Game : : entityManager - > GetEntity ( possessableId ) ;
2022-09-02 18:49:19 +00:00
if ( possessable ) {
possessor - > Dismount ( possessable ) ;
}
}
}
2021-12-05 17:54:36 +00:00
if ( m_Parent - > GetLOT ( ) ! = 1 ) {
echo = true ;
}
if ( echo ) {
2023-07-15 20:56:33 +00:00
Game : : entityManager - > SerializeEntity ( m_Parent ) ;
2021-12-05 17:54:36 +00:00
}
2022-01-15 19:02:14 +00:00
2023-07-15 20:56:33 +00:00
auto * attacker = Game : : entityManager - > GetEntity ( source ) ;
2021-12-05 17:54:36 +00:00
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 ;
}
2023-01-11 19:08:54 +00:00
//check if hardcore mode is enabled
2023-07-15 20:56:33 +00:00
if ( Game : : entityManager - > GetHardcoreMode ( ) ) {
2023-01-12 19:16:24 +00:00
DoHardcoreModeDrops ( source ) ;
2023-01-11 19:08:54 +00:00
}
2023-01-12 19:16:24 +00:00
2023-01-11 20:50:21 +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 ) ;
2023-07-15 20:56:33 +00:00
Game : : entityManager - > SerializeEntity ( m_Parent ) ;
2021-12-05 17:54:36 +00:00
}
m_KillerID = source ;
2023-07-15 20:56:33 +00:00
auto * owner = Game : : entityManager - > GetEntity ( source ) ;
2021-12-05 17:54:36 +00:00
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 ) {
2023-07-15 20:56:33 +00:00
auto * member = Game : : entityManager - > GetEntity ( memberId ) ;
2021-12-05 17:54:36 +00:00
if ( member = = nullptr ) continue ;
auto * memberMissions = member - > GetComponent < MissionComponent > ( ) ;
if ( memberMissions = = nullptr ) continue ;
memberMissions - > Progress ( eMissionTaskType : : SMASH , m_Parent - > GetLOT ( ) ) ;
2022-04-25 10:25:07 +00:00
memberMissions - > Progress ( eMissionTaskType : : USE_SKILL , m_Parent - > GetLOT ( ) , skillID ) ;
2021-12-05 17:54:36 +00:00
}
} else {
missions - > Progress ( eMissionTaskType : : SMASH , m_Parent - > GetLOT ( ) ) ;
2022-04-25 10:25:07 +00:00
missions - > Progress ( eMissionTaskType : : USE_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
2023-07-15 20:56:33 +00:00
auto * member = Game : : entityManager - > 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
2023-07-15 20:56:33 +00:00
auto * member = Game : : entityManager - > GetEntity ( memberId ) ;
2021-12-24 00:25:52 +00:00
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
2023-07-17 22:55:33 +00:00
if ( Game : : zoneManager - > GetPlayerLoseCoinOnDeath ( ) ) {
2022-02-05 12:27:24 +00:00
auto * character = m_Parent - > GetCharacter ( ) ;
uint64_t coinsTotal = character - > GetCoins ( ) ;
2023-07-17 22:55:33 +00:00
const uint64_t minCoinsToLose = Game : : zoneManager - > GetWorldConfig ( ) - > coinsLostOnDeathMin ;
2023-01-02 00:36:10 +00:00
if ( coinsTotal > = minCoinsToLose ) {
2023-07-17 22:55:33 +00:00
const uint64_t maxCoinsToLose = Game : : zoneManager - > GetWorldConfig ( ) - > coinsLostOnDeathMax ;
const float coinPercentageToLose = Game : : zoneManager - > 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 ) ;
2023-05-02 22:39:21 +00:00
character - > SetCoins ( coinsTotal , eLootSourceType : : PICKUP ) ;
2022-02-05 12:27:24 +00:00
}
}
2021-12-05 17:54:36 +00:00
2023-07-15 20:56:33 +00:00
Entity * zoneControl = Game : : entityManager - > GetZoneControlEntity ( ) ;
2021-12-05 17:54:36 +00:00
for ( CppScripts : : Script * script : CppScripts : : GetEntityScripts ( zoneControl ) ) {
script - > OnPlayerDied ( zoneControl , m_Parent ) ;
}
2023-07-15 20:56:33 +00:00
std : : vector < Entity * > scriptedActs = Game : : entityManager - > GetEntitiesByComponent ( eReplicaComponentType : : SCRIPTED_ACTIVITY ) ;
2021-12-05 17:54:36 +00:00
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 ) ;
}
}
}
2023-07-15 08:54:41 +00:00
std : : vector < Entity * > scripts = EntityManager : : Instance ( ) - > GetEntitiesByComponent ( eReplicaComponentType : : SCRIPT ) ;
for ( Entity * scriptEntity : scripts ) {
// Prevent double triggering
if ( scriptEntity - > GetObjectID ( ) = = zoneControl - > GetObjectID ( ) ) continue ;
if ( std : : find ( scriptedActs . begin ( ) , scriptedActs . end ( ) , scriptEntity ) ! = scriptedActs . end ( ) ) continue ;
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 ) ;
}
}
}
2021-12-05 17:54:36 +00:00
}
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 ( ) ;
}
2023-07-15 08:54:41 +00:00
destroyableComponent - > ComputeBaseStats ( ) ;
2021-12-05 17:54:36 +00:00
// 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
2023-07-15 20:56:33 +00:00
Game : : entityManager - > SerializeEntity ( entity ) ;
2021-12-05 17:54:36 +00:00
}
2023-07-15 08:54:41 +00:00
void DestroyableComponent : : AddStat ( const StatProperty & stat ) {
m_DirtyStats = true ;
const auto & typeIter = m_Stats . find ( stat . type ) ;
if ( typeIter = = m_Stats . end ( ) ) {
m_Stats . emplace ( stat . type , std : : map < eStatModifier , float > ( ) ) ;
}
auto & typeMap = m_Stats . at ( stat . type ) ;
const auto & modifierIter = typeMap . find ( stat . modifier ) ;
if ( modifierIter = = typeMap . end ( ) ) {
typeMap . emplace ( stat . modifier , stat . value ) ;
return ;
}
typeMap . at ( stat . modifier ) + = stat . value ;
}
void DestroyableComponent : : RemoveStat ( const StatProperty & stat ) {
m_DirtyStats = true ;
const auto & typeIter = m_Stats . find ( stat . type ) ;
if ( typeIter = = m_Stats . end ( ) ) {
return ;
}
auto & typeMap = m_Stats . at ( stat . type ) ;
const auto & modifierIter = typeMap . find ( stat . modifier ) ;
if ( modifierIter = = typeMap . end ( ) ) {
return ;
}
typeMap . at ( stat . modifier ) - = stat . value ;
if ( typeMap . at ( stat . modifier ) < = 0 ) {
typeMap . erase ( stat . modifier ) ;
}
if ( typeMap . empty ( ) ) {
m_Stats . erase ( stat . type ) ;
}
}
float DestroyableComponent : : GetStat ( eStatTypes statType , eStatModifier statModifier ) const {
const auto & typeIter = m_Stats . find ( statType ) ;
float baseResistance = 0 ;
if ( statModifier = = eStatModifier : : DamageResistance ) {
if ( m_ResistanceProfile ! = nullptr ) {
baseResistance = m_ResistanceProfile - > GetResistanceProfile ( statType ) ;
Game : : logger - > Log ( " DestroyableComponent " , " Base resistance: %f for %i, type %i " , baseResistance , m_Parent - > GetLOT ( ) , statType ) ;
}
else {
Game : : logger - > Log ( " DestroyableComponent " , " No resistance profile for %i " , m_Parent - > GetLOT ( ) ) ;
}
}
if ( typeIter = = m_Stats . end ( ) ) {
return baseResistance ;
}
const auto & typeMap = m_Stats . at ( statType ) ;
const auto & modifierIter = typeMap . find ( statModifier ) ;
if ( modifierIter = = typeMap . end ( ) ) {
return baseResistance ;
}
return typeMap . at ( statModifier ) ;
}
void DestroyableComponent : : ComputeBaseStats ( bool refill ) {
if ( ! m_Parent - > IsPlayer ( ) )
{
auto * combatAI = m_Parent - > GetComponent < BaseCombatAIComponent > ( ) ;
if ( combatAI = = nullptr )
{
return ;
}
}
// Store the current health, armor and imagination
const auto currentHealth = GetHealth ( ) ;
const auto currentArmor = GetArmor ( ) ;
float maxHealth = 0.0f ;
float maxArmor = 0.0f ;
int32_t level = ( static_cast < int32_t > ( m_Info . level ) < = 1 ) ? 1 : m_Info . level ;
if ( m_Parent - > IsPlayer ( ) )
{
level = m_Parent - > GetComponent < LevelProgressionComponent > ( ) - > GetLevel ( ) ;
m_Info . life = 4 ;
}
maxHealth + = level * m_Info . life ;
maxHealth * = 10 ;
maxHealth + = GetStat ( eStatTypes : : Health , eStatModifier : : Absolute ) ;
maxHealth * = 1.0f + GetStat ( eStatTypes : : Health , eStatModifier : : Percent ) ;
maxArmor = level * m_Info . armor ;
maxArmor * = 10 ;
maxArmor + = GetStat ( eStatTypes : : Armor , eStatModifier : : Absolute ) ;
maxArmor * = 1.0f + GetStat ( eStatTypes : : Armor , eStatModifier : : Percent ) ;
// Set the base stats
SetMaxHealth ( maxHealth ) ;
SetMaxArmor ( maxArmor ) ;
// If any of the current stats are more than their max, set them to the max
if ( currentHealth > maxHealth | | refill ) SetHealth ( maxHealth ) ;
else SetHealth ( currentHealth ) ;
if ( currentArmor > maxArmor | | refill ) SetArmor ( maxArmor ) ;
else SetArmor ( currentArmor ) ;
EntityManager : : Instance ( ) - > SerializeEntity ( m_Parent ) ;
}
std : : map < eStatTypes , float > DestroyableComponent : : ComputeDamage ( uint32_t baseDamage , Entity * source , DamageProfile * damageProfile ) {
const bool debug = true ;
auto * sourceDestroyable = source - > GetComponent < DestroyableComponent > ( ) ;
if ( sourceDestroyable = = nullptr ) {
Game : : logger - > Log ( " Damage " , " Source entity has no destroyable componen %i " , source - > GetLOT ( ) ) ;
return { { eStatTypes : : Physical , baseDamage } } ;
}
std : : stringstream ss ;
std : : map < eStatTypes , float > damageMap = { { eStatTypes : : Physical , baseDamage } } ;
for ( eStatTypes damageType = eStatTypes : : Physical ; damageType < eStatTypes : : MAX ;
damageType = static_cast < eStatTypes > ( static_cast < uint32_t > ( damageType ) + 1 ) ) {
// Get the damage for this damage type
float damage = sourceDestroyable - > GetStat ( damageType , eStatModifier : : DamageAbsolute ) ;
damage * = 1.0f + sourceDestroyable - > GetStat ( damageType , eStatModifier : : DamagePercent ) + : : log ( baseDamage ) ;
if ( damageProfile ! = nullptr )
{
damage + = damageProfile - > GetDamageProfile ( damageType ) ;
}
/*
Level scaling
*/
float ourLevel = ( static_cast < int32_t > ( m_Info . level ) < = 1 ) ? 1 : m_Info . level ;
float sourceLevel ;
if ( source - > IsPlayer ( ) )
{
auto * levelProgressionComponent = source - > GetComponent < LevelProgressionComponent > ( ) ;
if ( levelProgressionComponent = = nullptr )
{
Game : : logger - > Log ( " Damage " , " Source entity is player but has no level progression component %i " , source - > GetLOT ( ) ) ;
return { { eStatTypes : : Physical , baseDamage } } ;
}
sourceLevel = levelProgressionComponent - > GetLevel ( ) ;
// Player max is level 45, scale it to between 1 and 10
sourceLevel = static_cast < int32_t > ( static_cast < float > ( sourceLevel ) / 4.5f ) ;
// Make sure it's between 1 and 10
sourceLevel = std : : max ( sourceLevel , 1.0f ) ;
sourceLevel = std : : min ( sourceLevel , 10.0f ) ;
if ( ourLevel < sourceLevel )
{
damage / = 1 + ( sourceLevel - ourLevel ) ;
}
}
else
{
sourceLevel = ( static_cast < int32_t > ( sourceDestroyable - > m_Info . level ) < = 1 ) ? 1 : sourceDestroyable - > m_Info . level ;
if ( m_Parent - > IsPlayer ( ) )
{
auto * levelProgressionComponent = m_Parent - > GetComponent < LevelProgressionComponent > ( ) ;
if ( levelProgressionComponent = = nullptr )
{
Game : : logger - > Log ( " Damage " , " Source entity is player but has no level progression component %i " , m_Parent - > GetLOT ( ) ) ;
return { { eStatTypes : : Physical , baseDamage } } ;
}
ourLevel = levelProgressionComponent - > GetLevel ( ) ;
// Player max is level 45, scale it to between 1 and 10
ourLevel = static_cast < int32_t > ( static_cast < float > ( ourLevel ) / 4.5f ) ;
// Make sure it's between 1 and 10
ourLevel = std : : max ( ourLevel , 1.0f ) ;
ourLevel = std : : min ( ourLevel , 10.0f ) ;
if ( ourLevel > sourceLevel )
{
damage * = 1 + ( ourLevel - sourceLevel ) ;
}
}
}
// Idk
// If the damage is 0, skip this damage type
if ( damage = = 0 ) {
continue ;
}
// Calculate our resistance for this damage type
const auto resistance = this - > GetStat ( damageType , eStatModifier : : DamageResistance ) ;
// Cap resistance at 80%
const auto cappedResistance = std : : min ( resistance , 0.8f ) ;
// Calculate the damage we take
const auto damageTaken = damage * ( 1.0f - cappedResistance ) ;
// Add the damage to our total damage
const auto & it = damageMap . find ( damageType ) ;
if ( it = = damageMap . end ( ) ) {
damageMap [ damageType ] = damageTaken ;
} else {
damageMap [ damageType ] + = damageTaken ;
}
// Log the calculation
if ( debug ) {
ss < < " Damage type: " < < static_cast < int32_t > ( damageType ) < < std : : endl ;
ss < < " \t Damage: " < < damage < < std : : endl ;
ss < < " \t Resistance: " < < resistance < < std : : endl ;
ss < < " \t Capped resistance: " < < cappedResistance < < std : : endl ;
ss < < " \t Damage taken: " < < damageTaken < < std : : endl ;
}
}
return damageMap ;
}
void DestroyableComponent : : Damage ( const std : : map < eStatTypes , float > & damage , LWOOBJID source , uint32_t skillID , bool echo ) {
float totalDamage = 0 ;
for ( const auto & [ damageType , damageAmount ] : damage ) {
totalDamage + = damageAmount ;
}
// Find the greatest damage type
eStatTypes greatestDamageType = eStatTypes : : Physical ;
for ( const auto & [ damageType , damageAmount ] : damage ) {
if ( damageAmount > damage . at ( greatestDamageType ) ) {
greatestDamageType = damageType ;
}
}
const auto effectName = std : : to_string ( GeneralUtils : : GenerateRandomNumber < size_t > ( 100 , 10000 ) ) ;
const auto & objectId = m_Parent - > GetObjectID ( ) ;
const bool critical = GeneralUtils : : GenerateRandomNumber < size_t > ( 0 , 100 ) < = 10 ;
if ( critical ) {
totalDamage * = 2 ;
}
// Play an effect on us representing the damage
switch ( greatestDamageType ) {
case eStatTypes : : Heat :
if ( ! critical ) GameMessages : : SendPlayFXEffect ( objectId , 50 , u " onhit " , effectName ) ;
else GameMessages : : SendPlayFXEffect ( objectId , 1578 , u " onhit " , effectName ) ;
break ;
case eStatTypes : : Electric :
if ( ! critical ) GameMessages : : SendPlayFXEffect ( objectId , 4027 , u " create " , effectName ) ;
else GameMessages : : SendPlayFXEffect ( objectId , 953 , u " onhit " , effectName ) ;
break ;
case eStatTypes : : Corruption :
if ( ! critical ) GameMessages : : SendPlayFXEffect ( objectId , 7 , u " onhit " , effectName ) ;
else GameMessages : : SendPlayFXEffect ( objectId , 1153 , u " death " , effectName ) ;
break ;
case eStatTypes : : Physical :
default :
if ( ! critical ) GameMessages : : SendPlayFXEffect ( objectId , 5039 , u " on-anim " , effectName ) ;
else GameMessages : : SendPlayFXEffect ( objectId , 4972 , u " onhitObject " , effectName ) ;
break ;
}
m_Parent - > AddCallbackTimer ( 1.5f , [ this , effectName ] ( ) {
GameMessages : : SendStopFXEffect ( m_Parent , true , effectName ) ;
} ) ;
Damage ( totalDamage , source , skillID , echo ) ;
}
void DestroyableComponent : : Update ( float deltaTime ) {
if ( m_DirtyStats ) {
ComputeBaseStats ( ) ;
m_DirtyStats = false ;
}
}
2021-12-05 17:54:36 +00:00
void DestroyableComponent : : AddOnHitCallback ( const std : : function < void ( Entity * ) > & callback ) {
m_OnHitCallbacks . push_back ( callback ) ;
}
2023-01-12 19:16:24 +00:00
void DestroyableComponent : : DoHardcoreModeDrops ( const LWOOBJID source ) {
//check if this is a player:
if ( m_Parent - > IsPlayer ( ) ) {
//remove hardcore_lose_uscore_on_death_percent from the player's uscore:
auto * character = m_Parent - > GetComponent < CharacterComponent > ( ) ;
auto uscore = character - > GetUScore ( ) ;
2023-07-15 20:56:33 +00:00
auto uscoreToLose = uscore * ( Game : : entityManager - > GetHardcoreLoseUscoreOnDeathPercent ( ) / 100 ) ;
2023-01-12 19:16:24 +00:00
character - > SetUScore ( uscore - uscoreToLose ) ;
2023-05-02 22:39:21 +00:00
GameMessages : : SendModifyLEGOScore ( m_Parent , m_Parent - > GetSystemAddress ( ) , - uscoreToLose , eLootSourceType : : MISSION ) ;
2023-01-12 19:16:24 +00:00
2023-07-15 20:56:33 +00:00
if ( Game : : entityManager - > GetHardcoreDropinventoryOnDeath ( ) ) {
2023-01-12 19:16:24 +00:00
//drop all items from inventory:
auto * inventory = m_Parent - > GetComponent < InventoryComponent > ( ) ;
if ( inventory ) {
//get the items inventory:
auto items = inventory - > GetInventory ( eInventoryType : : ITEMS ) ;
if ( items ) {
auto itemMap = items - > GetItems ( ) ;
if ( ! itemMap . empty ( ) ) {
for ( const auto & item : itemMap ) {
//drop the item:
if ( ! item . second ) continue ;
// don't drop the thinkng cap
if ( item . second - > GetLot ( ) = = 6086 ) continue ;
GameMessages : : SendDropClientLoot ( m_Parent , source , item . second - > GetLot ( ) , 0 , m_Parent - > GetPosition ( ) , item . second - > GetCount ( ) ) ;
item . second - > SetCount ( 0 , false , false ) ;
}
2023-07-15 20:56:33 +00:00
Game : : entityManager - > SerializeEntity ( m_Parent ) ;
2023-01-12 19:16:24 +00:00
}
}
}
}
//get character:
auto * chars = m_Parent - > GetCharacter ( ) ;
if ( chars ) {
auto coins = chars - > GetCoins ( ) ;
//lose all coins:
2023-05-02 22:39:21 +00:00
chars - > SetCoins ( 0 , eLootSourceType : : NONE ) ;
2023-01-12 19:16:24 +00:00
//drop all coins:
GameMessages : : SendDropClientLoot ( m_Parent , source , LOT_NULL , coins , m_Parent - > GetPosition ( ) ) ;
}
// Reload the player since we can't normally reduce uscore from the server and we want the UI to update
// do this last so we don't get killed.... again
2023-07-15 20:56:33 +00:00
Game : : entityManager - > DestructEntity ( m_Parent ) ;
Game : : entityManager - > ConstructEntity ( m_Parent ) ;
2023-01-12 19:16:24 +00:00
return ;
}
//award the player some u-score:
2023-07-15 20:56:33 +00:00
auto * player = Game : : entityManager - > GetEntity ( source ) ;
2023-01-12 19:16:24 +00:00
if ( player & & player - > IsPlayer ( ) ) {
auto * playerStats = player - > GetComponent < CharacterComponent > ( ) ;
if ( playerStats ) {
//get the maximum health from this enemy:
auto maxHealth = GetMaxHealth ( ) ;
2023-07-15 20:56:33 +00:00
int uscore = maxHealth * Game : : entityManager - > GetHardcoreUscoreEnemiesMultiplier ( ) ;
2023-01-12 19:16:24 +00:00
playerStats - > SetUScore ( playerStats - > GetUScore ( ) + uscore ) ;
2023-05-02 22:39:21 +00:00
GameMessages : : SendModifyLEGOScore ( player , player - > GetSystemAddress ( ) , uscore , eLootSourceType : : MISSION ) ;
2023-01-12 19:16:24 +00:00
2023-07-15 20:56:33 +00:00
Game : : entityManager - > SerializeEntity ( m_Parent ) ;
2023-01-12 19:16:24 +00:00
}
}
}