2020-12-13 15:01:13 +00:00
|
|
|
//=============================================================================
|
|
|
|
// KFDestructibleActor
|
|
|
|
//=============================================================================
|
|
|
|
// Base class for all placeable destructibles
|
|
|
|
//=============================================================================
|
|
|
|
// Killing Floor 2
|
|
|
|
// Copyright (C) 2015 Tripwire Interactive LLC
|
|
|
|
// - Andrew "Strago" Ladenberger
|
|
|
|
//=============================================================================
|
|
|
|
|
|
|
|
class KFDestructibleActor extends Actor
|
|
|
|
placeable
|
|
|
|
//implements(Interface_NavMeshPathObject)
|
|
|
|
native
|
|
|
|
hidecategories(Physics, Debug, Object, Mobile)
|
|
|
|
showcategories(Navigation);
|
|
|
|
|
|
|
|
/*********************************************************************************************
|
|
|
|
* Network
|
|
|
|
********************************************************************************************* */
|
|
|
|
|
|
|
|
enum EDestructibleRepType
|
|
|
|
{
|
|
|
|
/** Normal (unique net channel) server-side replication */
|
|
|
|
RT_ServerDefault,
|
|
|
|
/** Use a single net channel to replicate many destructible actors */
|
|
|
|
RT_ServerOptimized,
|
|
|
|
/** Client-only, no blocking collision */
|
|
|
|
RT_ClientSide,
|
|
|
|
};
|
|
|
|
|
|
|
|
var() EDestructibleRepType ReplicationMode;
|
|
|
|
|
|
|
|
struct native ReplicatedDamageModInfo
|
|
|
|
{
|
|
|
|
var byte ObjIdx;
|
|
|
|
var byte ModIdx;
|
|
|
|
var bool bPartial;
|
|
|
|
|
|
|
|
structdefaultproperties
|
|
|
|
{
|
|
|
|
ObjIdx=255
|
|
|
|
ModIdx=255
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/** Packed dmg mod info for replication */
|
|
|
|
var repnotify ReplicatedDamageModInfo ReplicatedDamageMods[16];
|
|
|
|
|
|
|
|
/** True if the destructible is fully shut down */
|
|
|
|
var repnotify bool bShutDown;
|
|
|
|
|
|
|
|
/** simplified network damage state */
|
|
|
|
var transient byte ReplicatedDamageFlags;
|
|
|
|
|
|
|
|
/*********************************************************************************************
|
|
|
|
* Damage
|
|
|
|
********************************************************************************************* */
|
|
|
|
/** The damage needed for a hit to be considered instakill */
|
|
|
|
var protected const int INSTAKILL_DAMAGE;
|
|
|
|
/** List of damage types that instantly destroy this object */
|
|
|
|
var() array<class<DamageType> > InstaKillDamageType<AllowAbstract>;
|
|
|
|
/** List of damage types to always ignore */
|
|
|
|
var() array<class<DamageType> > IgnoreDamageType<AllowAbstract>;
|
|
|
|
/** These damage types do increase damage */
|
|
|
|
var const array< class<DamageType> > VulnerableDamageType;
|
|
|
|
/** The amount to scale damage if vulnerable */
|
|
|
|
var const byte VulnerableMultiplier;
|
|
|
|
|
|
|
|
/** If true, ignores all damage */
|
|
|
|
var() protected bool bIgnoreAllDamage;
|
|
|
|
|
|
|
|
/** If true, will ignore all damage done by players */
|
|
|
|
var() protected bool bIgnorePlayerDamage;
|
|
|
|
|
|
|
|
/** If true, will take bump damage from AI, in addition to just players */
|
|
|
|
var() protected bool bAllowBumpDamageFromAI;
|
|
|
|
var transient float LastBumpCheckTime;
|
|
|
|
|
|
|
|
/** If bSelfDestructAll == TRUE for a damage mod, the object will be killed after this delay */
|
|
|
|
var() const float SelfDestructAllDelay;
|
|
|
|
|
|
|
|
/** When destroyed, disable collision on this blocking volume (MUST have bSelfDestructAll=TRUE) */
|
|
|
|
var() const BlockingVolume BlockingVolumeToDisable;
|
|
|
|
|
|
|
|
/** Force destroy these actors on SelfDestructAll. Useful if we have nearby destructibles and need more precision than ExplosionMod */
|
|
|
|
var() const array<Actor> DamageOtherActors<DisplayName=ActorsToDamageOnDestruct>;
|
|
|
|
|
|
|
|
/** Used to attach all of the components only once */
|
|
|
|
var transient bool bComponentsSetUp;
|
|
|
|
/** Used to update physics bodies only once */
|
|
|
|
var transient bool bInitRBPhysCalled;
|
|
|
|
/** If set, TakeRadiusDamage is being executed */
|
|
|
|
var transient bool bIsRadiusDamage;
|
|
|
|
/** Used by reset code to determine if actor is dirtied */
|
|
|
|
var transient bool bAnyDamageModApplied;
|
|
|
|
|
|
|
|
/*********************************************************************************************
|
|
|
|
* Nav Mesh (DEPRECATED)
|
|
|
|
********************************************************************************************* */
|
|
|
|
|
|
|
|
//enum ENavMeshObjectState
|
|
|
|
//{
|
|
|
|
// ENavMeshObjectState_None,
|
|
|
|
// ENavMeshObjectState_ObstacleOnly,
|
|
|
|
// ENavMeshObjectState_PathObjectOnly,
|
|
|
|
// ENavMeshObjectState_BothObstacleAndPathObject,
|
|
|
|
//};
|
|
|
|
//
|
|
|
|
//var(Navigation) KFNavMeshObstacle_Destructible MyNavMeshObstacle;
|
|
|
|
//var(Navigation) KFNavMeshPathObject_Destructible MyNavMeshPathObject;
|
|
|
|
//
|
|
|
|
//var (Navigation) ENavMeshObjectState NavMeshObjectState;
|
|
|
|
|
|
|
|
/*********************************************************************************************
|
|
|
|
* Material Mods
|
|
|
|
********************************************************************************************* */
|
|
|
|
|
|
|
|
struct native MaterialReplaceMod
|
|
|
|
{
|
|
|
|
var() MaterialInterface NewMaterial;
|
|
|
|
|
|
|
|
/** Index in the Materials array to replace with NewMaterial when this action is activated. */
|
|
|
|
var() int MaterialIndex;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct native MaterialScalarParamMod
|
|
|
|
{
|
|
|
|
var() MaterialInstanceConstant MatInst;
|
|
|
|
var() Name ParamName;
|
|
|
|
var() float ScalarVal;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct native MaterialTexParamMod
|
|
|
|
{
|
|
|
|
var() MaterialInstanceConstant MatInst;
|
|
|
|
var() Name ParamName;
|
|
|
|
var() Texture NewTexture;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct native MaterialVectorParamMod
|
|
|
|
{
|
|
|
|
var() MaterialInstanceConstant MatInst;
|
|
|
|
var() Name ParamName;
|
|
|
|
var() LinearColor VectorVal;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct native MaterialLightParamMod
|
|
|
|
{
|
|
|
|
var() int MatID;
|
|
|
|
var() Name ParamName;
|
|
|
|
var() Light LightActor;
|
|
|
|
|
|
|
|
structdefaultproperties
|
|
|
|
{
|
|
|
|
ParamName=BrightnessMultiplier
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/*********************************************************************************************
|
|
|
|
* Physics actors (fragments)
|
|
|
|
********************************************************************************************* */
|
|
|
|
|
|
|
|
/** Parameters to define an actor spawn. */
|
|
|
|
struct native ActorSpawnParams
|
|
|
|
{
|
|
|
|
/** Static mesh to use for physics object */
|
|
|
|
var() StaticMesh RigidBodyMesh;
|
|
|
|
|
|
|
|
/** Lower bound of random initial linear vel */
|
|
|
|
var() vector MinLinearVel;
|
|
|
|
/** Upper bound of random initial linear vel */
|
|
|
|
var() vector MaxLinearVel;
|
|
|
|
|
|
|
|
/** Lower bound of random initial angular vel */
|
|
|
|
var() vector MinAngularVel;
|
|
|
|
/** Upper bound of random initial angular vel */
|
|
|
|
var() vector MaxAngularVel;
|
|
|
|
|
|
|
|
/** Collision type to use for spawned rigid body */
|
|
|
|
var() ECollisionType CollisionType;
|
|
|
|
|
|
|
|
/** RBChannel to use for collision */
|
|
|
|
var() ERBCollisionChannel RBChannel;
|
|
|
|
|
|
|
|
/** Positional offset for actor spawn loc. Note this is relative to the subobject */
|
|
|
|
var() vector RelativeOffset;
|
|
|
|
/** Rotational offset for actor spawn loc. Note this is relative to the subobject */
|
|
|
|
var() rotator RelativeRotation;
|
|
|
|
/** How long before we forcibly destroy the spawned actor, in seconds. <= 0 means don't destroy. */
|
|
|
|
var() float LifeTimeSeconds;
|
|
|
|
/** Do not spawn if no local players are within this range */
|
|
|
|
var() float MaxSpawnDist<EditCondition=!bReplicated>;
|
|
|
|
|
|
|
|
/** (PERF WARNING) If set, the spawned KActor is network replicated */
|
|
|
|
var() bool bReplicated;
|
|
|
|
/** Material changes */
|
|
|
|
var() const array<MaterialReplaceMod> MaterialReplacements;
|
|
|
|
|
|
|
|
structdefaultproperties
|
|
|
|
{
|
|
|
|
LifeTimeSeconds=30.0f
|
|
|
|
MaxSpawnDist=1500.f
|
|
|
|
|
|
|
|
CollisionType=COLLIDE_NoCollision
|
|
|
|
RBChannel=RBCC_EffectPhysics
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/*********************************************************************************************
|
|
|
|
* Effects
|
|
|
|
********************************************************************************************* */
|
|
|
|
|
|
|
|
struct native DestroyedEffectParams
|
|
|
|
{
|
|
|
|
/** Emitters to spawn */
|
|
|
|
var() ParticleSystem ParticleEffect;
|
|
|
|
/** Position offset for detroyed effect. Note this is relative to the subobject */
|
|
|
|
var() vector RelativeOffset;
|
|
|
|
/** Rotational offset for destroyed effect. Note this is relative to the subobject */
|
|
|
|
var() rotator RelativeRotation;
|
|
|
|
/** Do not spawn if no local players are within this range */
|
|
|
|
var() float MaxSpawnDist;
|
|
|
|
|
|
|
|
/** reference to the spawned particle system */
|
|
|
|
var transient ParticleSystemComponent PSC;
|
|
|
|
|
|
|
|
structdefaultproperties
|
|
|
|
{
|
|
|
|
MaxSpawnDist=4000.f
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/** Contains data for an explosion to spawn upon damage */
|
|
|
|
struct native ExplosionMod
|
|
|
|
{
|
|
|
|
/** Explosion archetype to spawn */
|
|
|
|
var() KFGameExplosion ExplosionTemplate;
|
|
|
|
/** Explosion damage */
|
|
|
|
var float Damage;
|
|
|
|
/** Explosion damage radius */
|
|
|
|
var float DamageRadius;
|
|
|
|
/** Translation offset */
|
|
|
|
var() vector RelativeLocation;
|
|
|
|
/** Rotation offset */
|
|
|
|
var() rotator RelativeRotation;
|
|
|
|
/** The actor class to ignore for damage from this explosion **/
|
|
|
|
var class<Actor> ActorClassToIgnoreForDamage;
|
|
|
|
|
|
|
|
structdefaultproperties
|
|
|
|
{
|
|
|
|
RelativeLocation=(Z=10)
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
struct native SplashDamageParams
|
|
|
|
{
|
|
|
|
var() float BaseDamage;
|
|
|
|
var() float DamageRadius;
|
|
|
|
var() class<DamageType> DamageType<AllowAbstract>;
|
|
|
|
/** Translation offset - useful when other sub objects are in the way */
|
|
|
|
var() vector RelativeLocation;
|
|
|
|
|
|
|
|
structdefaultproperties
|
|
|
|
{
|
|
|
|
DamageType=class'KFDT_Explosive'
|
|
|
|
RelativeLocation=(Z=10)
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/** describes a dependency of one subobject on a modification to another */
|
|
|
|
struct native ObjDamageModifierDependency
|
|
|
|
{
|
|
|
|
/** which subobject we're talking about */
|
|
|
|
var() Name DependentSubObjName;
|
|
|
|
|
|
|
|
/** If dependent subobject is above this health, it will be damaged to this level */
|
|
|
|
var() int MaxHealthToAllow;
|
|
|
|
|
|
|
|
/** Optimization to avoid searches. Gets filled in on PostBeginPlay() */
|
|
|
|
var transient int DependentSubObjIdx;
|
|
|
|
|
|
|
|
structdefaultproperties
|
|
|
|
{
|
|
|
|
DependentSubObjIdx=-1
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/** Contains data for an explosion to spawn upon damage */
|
|
|
|
struct native LightDamageParams
|
|
|
|
{
|
|
|
|
/** Placed light actors to recieve this mod */
|
|
|
|
var() Light Light<DisplayName=LightActor>;
|
|
|
|
|
|
|
|
/** New light animation settings */
|
|
|
|
var() const float MaxBrightness<UIMin=0.0|ClampMin=0.0>;
|
|
|
|
var() const float MinBrightness<UIMin=0.0|ClampMin=0.0>;
|
|
|
|
var() ELightAnimationTechnique AnimationType;
|
|
|
|
var() float AnimationFrequency<UIMin=0.01|ClampMin=0.01|UIMax=20.0|ClampMax=20.0>;
|
|
|
|
|
|
|
|
/** If set, this light should be disabled */
|
|
|
|
var() bool bDisabled;
|
|
|
|
|
|
|
|
structdefaultproperties
|
|
|
|
{
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/** A modification to be applied to a subobject after a certain damage threshold is reached. */
|
|
|
|
struct native ObjectDamageModifier
|
|
|
|
{
|
|
|
|
/** Name that can use used to descriptively identify this mod. */
|
|
|
|
var() const Name DamageModName;
|
|
|
|
|
|
|
|
/** apply this Modifier at or below this health */
|
|
|
|
var() const float HealthThreshold;
|
|
|
|
|
|
|
|
/** New static mesh to swap to, or None */
|
|
|
|
var() StaticMesh NewMesh;
|
|
|
|
|
|
|
|
/** Material changes */
|
|
|
|
var() const array<MaterialReplaceMod> MaterialReplacements;
|
|
|
|
var() const array<MaterialScalarParamMod> MaterialScalarParams;
|
|
|
|
var() const array<MaterialTexParamMod> MaterialTexParams;
|
|
|
|
var() const array<MaterialVectorParamMod> MaterialVectorParams;
|
|
|
|
|
|
|
|
/** Sounds to play when applying this modifier */
|
|
|
|
var() const array<AkBaseSoundObject> Sounds;
|
|
|
|
|
|
|
|
/** TRUE to kill the subobject at this stage */
|
|
|
|
var() const bool bSelfDestruct;
|
|
|
|
|
|
|
|
/** TRUE to kill the object (all subobjects) at this stage */
|
|
|
|
var() const bool bSelfDestructAll;
|
|
|
|
|
|
|
|
/** TRUE to stop any attached ambient sound */
|
|
|
|
var() const bool bStopAmbientSound;
|
|
|
|
|
|
|
|
// maybe use this? ParticleSystem'Effects_Level_SP_5.SP_Hospital.Effects.P_Wooden_Barrier_Split'
|
|
|
|
var() array<DestroyedEffectParams> ParticleEffects;
|
|
|
|
|
|
|
|
/** Potential splash damage (if the subj explodes or something) */
|
|
|
|
var() const SplashDamageParams SplashDamage;
|
|
|
|
|
|
|
|
/** actors that will be spawned */
|
|
|
|
var() const array<ActorSpawnParams> ActorsToSpawn;
|
|
|
|
|
|
|
|
/** list of subobjects that are also affected by this modifier */
|
|
|
|
var() const array<ObjDamageModifierDependency> DependentSubObjs;
|
|
|
|
|
|
|
|
/** true when this stage has been applied */
|
|
|
|
var transient bool bApplied;
|
|
|
|
|
|
|
|
/** index of the next actor to be spawned from ActorsToSpawn array */
|
|
|
|
var transient int NextSpawnActorIdx;
|
|
|
|
|
|
|
|
/** Explosion archetype to spawn */
|
|
|
|
var() ExplosionMod ExploMod<DisplayName=ExplosionMod>;
|
|
|
|
|
|
|
|
/** Temporarily allow CanBecomeDynamic (bNeverBecomeDynamic=FALSE) on these actors */
|
|
|
|
var deprecated array<StaticMeshActor> BecomeDynamicActors;
|
|
|
|
|
|
|
|
/** light actors that are effected by this damage mod */
|
|
|
|
var() const array<LightDamageParams> LightParams;
|
|
|
|
|
|
|
|
/** Whether this mod should apply actor blocking or not. @NOTE: Only used by undo mod */
|
|
|
|
var transient bool bMeshBlockActors;
|
|
|
|
};
|
|
|
|
|
|
|
|
/** This sound is played all of the time the object is in the world. (good for radios and such) **/
|
|
|
|
var() protected AkBaseSoundObject AmbientSoundCue;
|
|
|
|
var protected AkComponent AmbientSoundComponent;
|
|
|
|
|
|
|
|
/** Cached squared distance of nearest local player */
|
|
|
|
var transient float ClosestLocalPlayerSqDist;
|
|
|
|
|
|
|
|
/*********************************************************************************************
|
|
|
|
* Sub-objects
|
|
|
|
********************************************************************************************* */
|
|
|
|
|
|
|
|
struct native DestructibleSubobject
|
|
|
|
{
|
|
|
|
/** name of the subobject. should be unique, but not currently enforced */
|
|
|
|
var() Name SubObjName;
|
|
|
|
|
|
|
|
/** the mesh */
|
|
|
|
var() StaticMeshComponent Mesh;
|
|
|
|
|
|
|
|
/** mods that can be applied to this subobject */
|
|
|
|
var() array<ObjectDamageModifier> DamageMods;
|
|
|
|
|
|
|
|
/** hit points this subobject has by default */
|
|
|
|
var() float DefaultHealth;
|
|
|
|
|
|
|
|
/** Light brightness material parameter for this subobject */
|
|
|
|
var() array<MaterialLightParamMod> MaterialLightParams;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This is a modifier that is autogenerated at initialization time which represents how to
|
|
|
|
* get back to our original state
|
|
|
|
* Used in MP to "heal" WDOs between rounds.
|
|
|
|
*/
|
|
|
|
var transient ObjectDamageModifier UndoMod;
|
|
|
|
|
|
|
|
/** Index of damage mod that is currently spawning effects / actors */
|
|
|
|
var transient int ActiveDamageModIdx;
|
|
|
|
|
|
|
|
/** Most recent damage type this subobject was hit with */
|
|
|
|
var transient class<DamageType> LastHitDamageType;
|
|
|
|
|
|
|
|
/** Cached MICs that are generated for material params */
|
|
|
|
var transient array<MaterialInstanceConstant> MICs;
|
|
|
|
|
|
|
|
/** Current health of this SubObject (non-transient) */
|
|
|
|
var int Health;
|
|
|
|
|
|
|
|
structdefaultproperties
|
|
|
|
{
|
|
|
|
Health=50
|
|
|
|
DefaultHealth=50
|
|
|
|
ActiveDamageModIdx=-1
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/** Array of objects that comprise this destructible object (MAX: 8) */
|
|
|
|
var(SubObjects) array<DestructibleSubobject> SubObjects;
|
|
|
|
|
|
|
|
/** Tick optimization -- store all damaged mods in a single-dimensional array */
|
|
|
|
var native private const init array<pointer> DamagedSubObjects{FDestructibleSubobject};
|
|
|
|
|
|
|
|
/** SubObject array size limit based on ReplicationMode */
|
|
|
|
var(SubObjects) const editconst byte SubObjectLimit;
|
|
|
|
/** SubObject array size limit based on ReplicationMode */
|
|
|
|
var(SubObjects) const editconst byte DamageModLimit;
|
|
|
|
|
|
|
|
/** Helper object to display RelativeLocation and RelativeRotation */
|
|
|
|
var(SubObjects) editoronly transient Actor PreviewOffsetActor;
|
|
|
|
/** Last value from PreviewOffsetActor */
|
|
|
|
var(SubObjects) editoronly transient vector PreviewRelativeLocation;
|
|
|
|
var(SubObjects) editoronly transient rotator PreviewRelativeRotation;
|
|
|
|
|
|
|
|
cpptext
|
|
|
|
{
|
|
|
|
private:
|
|
|
|
/** Attaches all of the subojects' components */
|
|
|
|
void AttachSubobjectComponents(void);
|
|
|
|
void SetupHealthVars();
|
|
|
|
void GenerateUndo();
|
|
|
|
void GenerateSubObjUndo(FDestructibleSubobject* SubObj);
|
|
|
|
void UpdateDamageMods();
|
|
|
|
void SetupComplexityLimits();
|
|
|
|
void UpdateLightMatParams();
|
|
|
|
|
|
|
|
/** handle MICs for any material params */
|
|
|
|
UMaterialInstanceConstant* GetSubObjMIC(FDestructibleSubobject* SubObj, INT ElementIndex);
|
|
|
|
void ClearSubObjMICs(INT ObjIdx);
|
|
|
|
|
|
|
|
public:
|
|
|
|
virtual void PostLoad();
|
|
|
|
virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent);
|
|
|
|
|
|
|
|
virtual UBOOL ShouldTrace(UPrimitiveComponent* Primitive,AActor *SourceActor, DWORD TraceFlags);
|
|
|
|
virtual void TickSpecial(FLOAT DeltaSeconds);
|
|
|
|
virtual UBOOL Tick(FLOAT DeltaTime, enum ELevelTick TickType);
|
|
|
|
|
|
|
|
/* Overridden to setup collision for damage meshes that will eventually exist */
|
|
|
|
virtual void InitRBPhys();
|
|
|
|
|
|
|
|
#if WITH_EDITOR
|
|
|
|
virtual void CheckForErrors();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if WITH_EDITOR
|
|
|
|
virtual INT AddMyMarker(AActor *S);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************************************
|
|
|
|
* Replication
|
|
|
|
********************************************************************************************* */
|
|
|
|
|
|
|
|
replication
|
|
|
|
{
|
|
|
|
if (Role == ROLE_Authority)
|
|
|
|
ReplicatedDamageMods, bShutDown;
|
|
|
|
}
|
|
|
|
|
|
|
|
simulated event ReplicatedEvent(name VarName)
|
|
|
|
{
|
|
|
|
local int Idx, ObjIdx, ModIdx;
|
|
|
|
|
|
|
|
if (VarName == nameof(ReplicatedDamageMods))
|
|
|
|
{
|
|
|
|
// look for unprocessed replicated damage mods
|
|
|
|
for (Idx = 0; Idx < ArrayCount(ReplicatedDamageMods); Idx++)
|
|
|
|
{
|
|
|
|
if ( ReplicatedDamageMods[Idx].ObjIdx != 255 )
|
|
|
|
{
|
|
|
|
ObjIdx = ReplicatedDamageMods[Idx].ObjIdx;
|
|
|
|
ModIdx = ReplicatedDamageMods[Idx].ModIdx;
|
|
|
|
if ( !SubObjects[ObjIdx].DamageMods[ModIdx].bApplied )
|
|
|
|
{
|
|
|
|
ApplyDamageMod(ObjIdx, ModIdx, ReplicatedDamageMods[Idx].bPartial);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (VarName == nameof(bShutDown))
|
|
|
|
{
|
|
|
|
if (bShutDown)
|
|
|
|
{
|
|
|
|
ShutDownObject();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
UnShutDownObject();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Fills in an empty slot in the replicated damage mod array to be
|
|
|
|
* sent to the clients for simulation.
|
|
|
|
*/
|
|
|
|
event ReplicateDamageMod(int ObjIdx, int ModIdx, bool bPartial)
|
|
|
|
{
|
|
|
|
local int Idx;
|
|
|
|
|
|
|
|
if ( (ObjIdx < 0) || (ObjIdx > 255)
|
|
|
|
|| (ModIdx < 0) || (ModIdx > 255) )
|
|
|
|
{
|
|
|
|
`warn(self$" replicatedamagemod out of bounds "$ObjIdx@ModIdx);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// find first available
|
|
|
|
for (Idx = 0; Idx < ArrayCount(ReplicatedDamageMods); Idx++)
|
|
|
|
{
|
|
|
|
if (ReplicatedDamageMods[Idx].ObjIdx == 255)
|
|
|
|
{
|
|
|
|
ReplicatedDamageMods[Idx].ObjIdx = ObjIdx;
|
|
|
|
ReplicatedDamageMods[Idx].ModIdx = ModIdx;
|
|
|
|
ReplicatedDamageMods[Idx].bPartial = bPartial;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// immediate net update
|
|
|
|
bForceNetUpdate = TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Pack damage mod into a byte for ReplicateDamageMod */
|
|
|
|
function byte PackDamageMod(int ObjIdx, int ModIdx, bool bPartial)
|
|
|
|
{
|
|
|
|
// 1-4: ObjIdx, 5-6 ModIdx, 8 bPartial
|
|
|
|
local byte DmgModFlags;
|
|
|
|
DmgModFlags = ObjIdx + (ModIdx << 4);
|
|
|
|
if ( bPartial )
|
|
|
|
{
|
|
|
|
DmgModFlags = DmgModFlags | 128;
|
|
|
|
}
|
|
|
|
return DmgModFlags;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Pack damage mod into a byte for ReplicateDamageMod */
|
|
|
|
simulated function UnPackDamageMod(byte DmgModFlags, out int ObjIdx, out int ModIdx, out byte bPartial)
|
|
|
|
{
|
|
|
|
// 1-4: ObjIdx, 5-6 ModIdx, 8 bPartial
|
|
|
|
ObjIdx = (DmgModFlags & 15);
|
|
|
|
ModIdx = (DmgModFlags & 48);
|
|
|
|
bPartial = (DmgModFlags & 128);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************************************
|
|
|
|
* Startup
|
|
|
|
********************************************************************************************* */
|
|
|
|
|
|
|
|
/** attach all subojects, generally get things ready for play */
|
|
|
|
simulated private native function SetupComponents();
|
|
|
|
/** one-time initializations. basically intended to provide some native PostBeginPlay code. */
|
|
|
|
simulated native protected function OneTimeInit();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This starts our ambient sound
|
|
|
|
*/
|
|
|
|
simulated event PostBeginPlay()
|
|
|
|
{
|
|
|
|
OneTimeInit();
|
|
|
|
|
|
|
|
// if we have and AmbientSoundCue we want to go ahead and start playing it.
|
|
|
|
if( ( AmbientSoundCue != None ) && ( AmbientSoundComponent != None ) )
|
|
|
|
{
|
|
|
|
AttachComponent( AmbientSoundComponent );
|
|
|
|
AmbientSoundComponent.OcclusionUpdateInterval = 0.2f;
|
|
|
|
AmbientSoundComponent.PlayEvent( AkEvent(AmbientSoundCue) );
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (ReplicationMode)
|
|
|
|
{
|
|
|
|
case RT_ClientSide:
|
|
|
|
// turn off BlockActors collision
|
|
|
|
SetCollision(, false);
|
|
|
|
if ( BlockingVolumeToDisable != None )
|
|
|
|
{
|
|
|
|
BlockingVolumeToDisable.SetCollision(false, false);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (WorldInfo.NetMode == NM_Client)
|
|
|
|
{
|
|
|
|
// on the client, set role to Authority if we're a clientside only KActor
|
|
|
|
Role = (ReplicationMode == RT_ClientSide) ? ROLE_Authority : ROLE_SimulatedProxy;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// on the server, set role to SimulatedProxy (i.e. replicate it) only if not clientside
|
|
|
|
RemoteRole = (ReplicationMode == RT_ClientSide) ? ROLE_None : ROLE_SimulatedProxy;
|
|
|
|
//InitNavigationHandle();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************************************
|
|
|
|
* Damage
|
|
|
|
********************************************************************************************* */
|
|
|
|
|
|
|
|
/** Apply damage to a subobject */
|
|
|
|
protected native function DamageSubObject(int ObjIdx, int Damage, Controller EventInstigator, class<DamageType> DamType);
|
|
|
|
|
|
|
|
/** Returns TRUE if any subobject has health > 0. Should only be used on the server as a
|
|
|
|
* catch-all for destructibles that don't have a damage mod with bSelfDestructAll set
|
|
|
|
*/
|
|
|
|
native function bool HasAnyHealth();
|
|
|
|
|
|
|
|
native function float GetCurrentHealth();
|
|
|
|
native function float GetMaxHealth();
|
|
|
|
|
|
|
|
/** Network: Server */
|
|
|
|
simulated function TakeRadiusDamage
|
|
|
|
(
|
|
|
|
Controller InstigatedBy,
|
|
|
|
float BaseDamage,
|
|
|
|
float DamageRadius,
|
|
|
|
class<DamageType> DamageType,
|
|
|
|
float Momentum,
|
|
|
|
vector HurtOrigin,
|
|
|
|
bool bFullDamage,
|
|
|
|
Actor DamageCauser,
|
2023-05-11 15:55:04 +00:00
|
|
|
optional float DamageFalloffExponent=1.f,
|
|
|
|
optional bool bAdjustRadiusDamage=true
|
2020-12-13 15:01:13 +00:00
|
|
|
)
|
|
|
|
{
|
|
|
|
bIsRadiusDamage = true;
|
2023-05-11 15:55:04 +00:00
|
|
|
Super.TakeRadiusDamage(InstigatedBy, BaseDamage, DamageRadius, DamageType, Momentum, HurtOrigin, bFullDamage, DamageCauser, DamageFalloffExponent, bAdjustRadiusDamage);
|
2020-12-13 15:01:13 +00:00
|
|
|
bIsRadiusDamage = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Network: Server */
|
|
|
|
event TakeDamage(int Damage, Controller EventInstigator, vector HitLocation, vector Momentum, class<DamageType> DamageType, optional TraceHitInfo HitInfo, optional Actor DamageCauser)
|
|
|
|
{
|
|
|
|
local int Idx;
|
|
|
|
|
|
|
|
// Modify the damage based on the damage type
|
|
|
|
AdjustDamage(Damage, EventInstigator, DamageType);
|
|
|
|
|
|
|
|
// Bail if there is no damage
|
|
|
|
if( Damage <= 0 )
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If this damage type should instakill, don't bother finding a HitIdx
|
|
|
|
if ( Damage >= INSTAKILL_DAMAGE || bIsRadiusDamage )
|
|
|
|
{
|
|
|
|
// no specific hit subobject, so hit them all equally
|
|
|
|
for (Idx=0; Idx<SubObjects.Length; ++Idx)
|
|
|
|
{
|
|
|
|
if (Damage > 0.f)
|
|
|
|
{
|
|
|
|
DamageSubObject(Idx, Damage, EventInstigator, DamageType);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if ( ValidateHitComponent(EventInstigator, HitInfo, HitLocation, Momentum) )
|
|
|
|
{
|
|
|
|
// Search and find which subobject got hit
|
|
|
|
for (Idx=0; Idx<SubObjects.Length; ++Idx)
|
|
|
|
{
|
|
|
|
if (SubObjects[Idx].Mesh != HitInfo.HitComponent)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (SubObjects[Idx].Health > 0.f)
|
|
|
|
{
|
|
|
|
// hit a subobject, damage it
|
|
|
|
DamageSubObject(Idx, Damage, EventInstigator, DamageType);
|
|
|
|
}
|
|
|
|
|
|
|
|
//`log("Got Subobject!");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If object is dead, unregister with navmesh
|
|
|
|
if ( Role == ROLE_Authority && !HasAnyHealth() )
|
|
|
|
{
|
|
|
|
// @todo: pathfinding
|
|
|
|
//TurnOffNavigationObstacle();
|
|
|
|
}
|
|
|
|
|
|
|
|
Super.TakeDamage(Damage, EventInstigator, HitLocation, Momentum, DamageType, HitInfo, DamageCauser);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Adjust damage based on inventory, other attributes */
|
|
|
|
function AdjustDamage(out int InDamage, Controller EventInstigator, class<DamageType> DamageType)
|
|
|
|
{
|
|
|
|
local int Idx;
|
|
|
|
local KFAIController KFAIInstigator;
|
|
|
|
|
|
|
|
if (bIgnoreAllDamage)
|
|
|
|
{
|
|
|
|
InDamage = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if this damage should instakill
|
|
|
|
for ( Idx = 0; Idx < InstaKillDamageType.length; ++Idx )
|
|
|
|
{
|
|
|
|
if ( (DamageType == InstaKillDamageType[Idx]) || ClassIsChildOf(DamageType, InstaKillDamageType[Idx]) )
|
|
|
|
{
|
|
|
|
InDamage = INSTAKILL_DAMAGE;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if this damage should be ignored completely
|
|
|
|
for (Idx = 0; Idx < IgnoreDamageType.length; ++Idx)
|
|
|
|
{
|
|
|
|
if ( (DamageType == IgnoreDamageType[Idx]) || ClassIsChildOf(DamageType, IgnoreDamageType[Idx]) )
|
|
|
|
{
|
|
|
|
InDamage = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// special case handling for players and zeds
|
|
|
|
if ( EventInstigator != None )
|
|
|
|
{
|
|
|
|
// The check below allows NPCs who are set to attack the destructible (see NotifyAttackTarget() in
|
|
|
|
// KFAIController), insta-kill the destructible if their ActorEnemy matches the destructible. This
|
|
|
|
// avoids the previous issue where damage was applied immediately on Bump, which resulted in the
|
|
|
|
// destructible breaking before the NPC's attack animtaion sequence actually began.
|
|
|
|
if( bAllowBumpDamageFromAI && !EventInstigator.bIsPlayer )
|
|
|
|
{
|
|
|
|
KFAIInstigator = KFAIController(EventInstigator);
|
|
|
|
if( KFAIInstigator != none && KFAIInstigator.ActorEnemy == self )
|
|
|
|
{
|
|
|
|
InDamage = INSTAKILL_DAMAGE;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if this damage should be ignored completely (update: human team only)
|
|
|
|
if ( bIgnorePlayerDamage && EventInstigator.bIsPlayer && EventInstigator.GetTeamNum() == 0)
|
|
|
|
{
|
|
|
|
InDamage = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if( bIsRadiusDamage && IsVulnerableTo(DamageType) )
|
|
|
|
{
|
|
|
|
InDamage *= VulnerableMultiplier;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** If returns true, the destructible is vulnerable to this damage type damage */
|
|
|
|
function bool IsVulnerableTo(class<DamageType> DT)
|
|
|
|
{
|
|
|
|
local int Idx;
|
|
|
|
|
|
|
|
for (Idx = 0; Idx < VulnerableDamageType.length; ++Idx)
|
|
|
|
{
|
|
|
|
if ( ClassIsChildOf(DT, VulnerableDamageType[Idx]) )
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Called from UKFReachSpec::PrepareForMove when DestructibleActor is ahead on NPC's path */
|
|
|
|
event bool InstaKillFromLargeNPCBumpDamage()
|
|
|
|
{
|
|
|
|
return ( InstaKillDamageType.Find(class'KFDT_NPCBump_Large') != INDEX_NONE );
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Called from UKFReachSpec::PrepareForMove when DestructibleActor is ahead on NPC's path */
|
|
|
|
event bool InstaKillFromStandardNPCBumpDamage()
|
|
|
|
{
|
|
|
|
return ( InstaKillDamageType.Find(class'KFDT_NPCBump') != INDEX_NONE );
|
|
|
|
}
|
|
|
|
|
|
|
|
/** If we don't have a valid hit component (e.g. CSHD) trace to retrieve */
|
|
|
|
function bool ValidateHitComponent( Controller EventInstigator, out TraceHitInfo HitInfo, vector HitLocation, vector Momentum )
|
|
|
|
{
|
|
|
|
local Actor TraceInstigator;
|
|
|
|
local vector NewHitLocation, HitNormal;
|
|
|
|
local vector Offset;
|
|
|
|
|
|
|
|
if ( HitInfo.HitComponent == None )
|
|
|
|
{
|
|
|
|
// Perform trace to retrieve hit info
|
|
|
|
if ( Momentum == vect(0,0,0) )
|
|
|
|
{
|
|
|
|
Momentum = Location - HitLocation;
|
|
|
|
}
|
|
|
|
Offset = 8.f * Normal(Momentum);
|
|
|
|
|
|
|
|
if( EventInstigator != none )
|
|
|
|
{
|
|
|
|
TraceInstigator = (EventInstigator.Pawn != none && EventInstigator.Pawn.Weapon != none) ? EventInstigator.Pawn.Weapon : EventInstigator.Pawn;
|
|
|
|
}
|
|
|
|
if( TraceInstigator == none )
|
|
|
|
{
|
|
|
|
TraceInstigator = WorldInfo;
|
|
|
|
}
|
|
|
|
|
|
|
|
TraceInstigator.Trace( NewHitLocation, HitNormal, HitLocation + Offset, HitLocation - Offset, true,, HitInfo, TRACEFLAG_Bullet );
|
|
|
|
}
|
|
|
|
|
|
|
|
return HitInfo.HitComponent != None;
|
|
|
|
}
|
|
|
|
|
|
|
|
event ApplySplashDamage(const out vector Origin, const out SplashDamageParams DamageParams)
|
|
|
|
{
|
|
|
|
// DEPRECATED. Use ExplosionTemplate instead
|
|
|
|
//if (Role == ROLE_Authority)
|
|
|
|
//{
|
|
|
|
// HurtRadius(DamageParams.BaseDamage, DamageParams.DamageRadius, DamageParams.DamageType, 1.f, Origin + DamageParams.RelativeLocation, self);
|
|
|
|
//}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Fire the Destroyed kismet event, called when all subobjects are dead */
|
|
|
|
delegate OnDestroyed(KFDestructibleActor DActor);
|
|
|
|
protected event TriggerDestroyedEvent(Controller EventInstigator)
|
|
|
|
{
|
|
|
|
//@note: This will always be called, unlike DelayedShutDownObject/ShutDownObject which is only called if bSelfDestructAll==TRUE
|
|
|
|
|
|
|
|
OnDestroyed(self);
|
|
|
|
TriggerEventClass(class'SeqEvent_Destroyed', EventInstigator);
|
|
|
|
}
|
|
|
|
|
|
|
|
function BumpedByMonster( KFPawn_Monster P, vector HitNormal )
|
|
|
|
{
|
|
|
|
if( bAllowBumpDamageFromAI )
|
|
|
|
{
|
|
|
|
if ( `TimeSince(LastBumpCheckTime) < 0.5f )
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
LastBumpCheckTime = WorldInfo.TimeSeconds;
|
|
|
|
|
|
|
|
if( P.Controller != none )
|
|
|
|
{
|
|
|
|
// Call TakeDamage with 0 because we only want to take bump damage if it is in the array of InstaKillDamageTypes
|
|
|
|
TakeDamage( 0, P.Controller, P.Location, vect(0,0,0), P.BumpDamageType,, P );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function int GetInstaKillDamage()
|
|
|
|
{
|
|
|
|
return INSTAKILL_DAMAGE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************************************
|
|
|
|
* Effects
|
|
|
|
********************************************************************************************* */
|
|
|
|
|
|
|
|
/** Change the static mesh to one stored in a DamageMod
|
|
|
|
*
|
|
|
|
* @param ObjIdx index of subobject getting the mod
|
|
|
|
* @param ModIdx index into mod array indicating which mod to apply
|
|
|
|
*/
|
|
|
|
simulated native protected function SetSubObjectStaticMesh(int ObjIdx, int ModIdx);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Apply one of the stored damage mods to this object.
|
|
|
|
*
|
|
|
|
* @param ObjIdx index of subobject getting the mod
|
|
|
|
* @param ModIdx index into mod array indicating which mod to apply
|
|
|
|
* @param bPartial TRUE for partial application, used if more than one mod triggers
|
|
|
|
* in a frame. Only 1 mod will be fully applied (to prevent overlapping
|
|
|
|
* sounds and whatnot.
|
|
|
|
*/
|
|
|
|
simulated native protected function ApplyDamageMod(int ObjIdx, int ModIdx, bool bPartial, optional Controller DamageInstigator);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This function will remove all of the currently attached decals from the object.
|
|
|
|
* Basically, we want to have decals attach to these objects and then on state change (due to damage usually), we will
|
|
|
|
* just detach them all with the big particle effect that occurs it should not be noticeable.
|
|
|
|
**/
|
|
|
|
simulated native protected function RemoveDecals();
|
|
|
|
|
|
|
|
/** Spawns the explosion associated with the passed in damage mod */
|
|
|
|
simulated event Explosion(const out DestructibleSubobject SubObj, const out ObjectDamageModifier Mod)
|
|
|
|
{
|
|
|
|
local vector EffectLoc;
|
|
|
|
local rotator EffectRot;
|
|
|
|
local GameExplosionActor ExplosionActor;
|
|
|
|
local GameExplosion ExplosionTemplate;
|
|
|
|
//local StaticMeshActor SMActor;
|
|
|
|
//local bool bOldNeverBecomeDynamic;
|
|
|
|
|
|
|
|
// Temporarily enable CanBecomeDynamic and activate these actors. This is useful
|
|
|
|
// for tables and other destructibles with objects stacked on top.
|
|
|
|
//foreach Mod.BecomeDynamicActors(SMActor)
|
|
|
|
//{
|
|
|
|
// bOldNeverBecomeDynamic = SMActor.StaticMeshComponent.bNeverBecomeDynamic;
|
|
|
|
// SMActor.StaticMeshComponent.bNeverBecomeDynamic = false;
|
|
|
|
// class'KActorFromStatic'.Static.MakeDynamic(SMActor.StaticMeshComponent);
|
|
|
|
// SMActor.StaticMeshComponent.bNeverBecomeDynamic = bOldNeverBecomeDynamic;
|
|
|
|
//}
|
|
|
|
|
|
|
|
if( Mod.ExploMod.ExplosionTemplate != None )
|
|
|
|
{
|
|
|
|
EffectLoc = Location + (Mod.ExploMod.RelativeLocation >> Rotation);
|
|
|
|
EffectRot = Rotation + RelativeRotation;
|
|
|
|
|
|
|
|
ExplosionActor = Spawn(class'KFExplosionActor', self,, EffectLoc, EffectRot);
|
|
|
|
ExplosionTemplate = Mod.ExploMod.ExplosionTemplate;
|
|
|
|
|
|
|
|
if (ExplosionActor != None)
|
|
|
|
{
|
|
|
|
// override explosion template
|
|
|
|
/*
|
|
|
|
ExplosionTemplate = new class'KFGameExplosion'(Mod.ExploMod.ExplosionTemplate);
|
|
|
|
ExplosionTemplate.Damage = Mod.ExploMod.Damage;
|
|
|
|
ExplosionTemplate.DamageRadius = Mod.ExploMod.DamageRadius;
|
|
|
|
ExplosionTemplate.DamageFalloffExponent = 1.0f;
|
|
|
|
ExplosionTemplate.ActorClassToIgnoreForDamage = Mod.ExploMod.ActorClassToIgnoreForDamage;
|
|
|
|
ExplosionTemplate.ActorToIgnoreForDamage = self;
|
|
|
|
ExplosionTemplate.DamageDelay = 0.01f;
|
|
|
|
*/
|
|
|
|
|
|
|
|
// Detect environment template and skip line check and damage falloff
|
|
|
|
//if ( ExplosionTemplate.DamageFalloffExponent == 0.f
|
|
|
|
// && ExplosionTemplate.ActorClassToIgnoreForDamage == class'Pawn'
|
|
|
|
// && ExplosionTemplate.MyDamageType == class'KFDT_Environment_KActorImpulse' )
|
|
|
|
//{
|
|
|
|
// ExplosionActor.bAlwaysDoFullDamage = true;
|
|
|
|
//}
|
|
|
|
|
|
|
|
ExplosionActor.Explode(ExplosionTemplate);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************************************
|
|
|
|
* Destroy / UnDestroy
|
|
|
|
********************************************************************************************* */
|
|
|
|
|
|
|
|
/** Restores to it's pristine state. */
|
|
|
|
simulated native function UnDestroy();
|
|
|
|
|
|
|
|
/** "Turn off" this entire object after a delay */
|
|
|
|
simulated protected event DelayedShutDownObject()
|
|
|
|
{
|
|
|
|
local Actor Other;
|
|
|
|
local StaticMeshActor OtherSM;
|
|
|
|
|
|
|
|
SetCollision(false, false, bIgnoreEncroachers);
|
|
|
|
|
|
|
|
// Disable associated blocking volume. Called when bSelfDestructAll is set
|
|
|
|
// @note invalid if this is a clientside destructible
|
|
|
|
if ( ReplicationMode != RT_ClientSide && BlockingVolumeToDisable != None )
|
|
|
|
{
|
|
|
|
BlockingVolumeToDisable.SetCollision(false, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Process DamageOtherActors
|
|
|
|
foreach DamageOtherActors(Other)
|
|
|
|
{
|
|
|
|
if ( Other.bStatic && Other.bWorldGeometry )
|
|
|
|
{
|
|
|
|
OtherSM = StaticMeshActor(Other);
|
|
|
|
if ( OtherSM != None )
|
|
|
|
{
|
|
|
|
// override bNeverBecomeDynamic on this StaticMeshComp
|
|
|
|
OtherSM.StaticMeshComponent.bNeverBecomeDynamic = false;
|
|
|
|
if ( OtherSM.StaticMeshComponent.CanBecomeDynamic() )
|
|
|
|
{
|
|
|
|
Other = class'KActorFromStatic'.Static.MakeDynamic(OtherSM.StaticMeshComponent);
|
|
|
|
if ( Other == None )
|
|
|
|
{
|
|
|
|
// make dynamic failed, move on to next actor
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Other.TakeRadiusDamage(None, 1000, 1000, class'KFDT_Environment_KActorImpulse', 1, Location, true, self);
|
|
|
|
}
|
|
|
|
|
|
|
|
SetTimer(0.2f, false, nameof(ShutDownObject));
|
|
|
|
}
|
|
|
|
|
|
|
|
/** "Turn off" this entire object */
|
|
|
|
simulated function ShutDownObject()
|
|
|
|
{
|
|
|
|
local int Idx;
|
|
|
|
|
|
|
|
bShutDown = true;
|
|
|
|
|
|
|
|
// shut down all subobjects
|
|
|
|
for (Idx=0; Idx<SubObjects.Length; ++Idx)
|
|
|
|
{
|
|
|
|
SubObjects[Idx].Mesh.SetHidden(TRUE);
|
|
|
|
SubObjects[Idx].Mesh.SetTraceBlocking(FALSE, FALSE);
|
|
|
|
SubObjects[Idx].Mesh.SetActorCollision(FALSE, FALSE);
|
|
|
|
SubObjects[Idx].Mesh.SetBlockRigidBody(FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
// note actor collision is already disabled (see DelayedShutDownObject)
|
|
|
|
}
|
|
|
|
|
|
|
|
/** "Turn off" a subobject, no mesh, no collision, nothing left */
|
|
|
|
simulated protected event ShutDownSubObject(const out DestructibleSubobject SubObj)
|
|
|
|
{
|
|
|
|
SubObj.Mesh.SetHidden(TRUE);
|
|
|
|
SubObj.Mesh.SetTraceBlocking(FALSE, FALSE);
|
|
|
|
SubObj.Mesh.SetActorCollision(FALSE, FALSE);
|
|
|
|
SubObj.Mesh.SetBlockRigidBody(FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Reset all objects after TurnOff has been called */
|
|
|
|
simulated protected event UnShutDownObject()
|
|
|
|
{
|
|
|
|
local int SubObjIdx, DamageModIdx;
|
|
|
|
|
|
|
|
bShutDown = false;
|
|
|
|
|
|
|
|
// Reset collision
|
|
|
|
SetCollision(default.bCollideActors, default.bBlockActors, bIgnoreEncroachers);
|
|
|
|
|
|
|
|
// @fixme: better to store original vals here instead of assuming
|
|
|
|
// true across the board, in case that assumption is inaccurate
|
|
|
|
for (SubObjIdx = 0; SubObjIdx < SubObjects.Length; ++SubObjIdx)
|
|
|
|
{
|
|
|
|
SubObjects[SubObjIdx].Mesh.SetHidden(FALSE);
|
|
|
|
SubObjects[SubObjIdx].Mesh.SetTraceBlocking(TRUE, TRUE);
|
|
|
|
SubObjects[SubObjIdx].Mesh.SetActorCollision( TRUE, SubObjects[SubObjIdx].Mesh.BlockActors ); // BlockActors is determined by the undo object
|
|
|
|
SubObjects[SubObjIdx].Mesh.SetBlockRigidBody(TRUE);
|
|
|
|
|
|
|
|
// Reset vars for tracking damage mods
|
|
|
|
SubObjects[SubObjIdx].ActiveDamageModIdx = -1;
|
|
|
|
for (DamageModIdx = 0; DamageModIdx < SubObjects[SubObjIdx].DamageMods.length; ++DamageModIdx)
|
|
|
|
{
|
|
|
|
SubObjects[SubObjIdx].DamageMods[DamageModIdx].NextSpawnActorIdx = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If a pawn is inside of this actor now, teleport it
|
|
|
|
if( Role == ROLE_Authority )
|
|
|
|
{
|
|
|
|
SetTimer(0.01, false, nameof(MoveCollidingPawns));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Moves pawns that are standing on destroyed actor when it is reset */
|
|
|
|
function MoveCollidingPawns()
|
|
|
|
{
|
|
|
|
// @todo
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Level was reset without reloading
|
|
|
|
* Network: ALL. Called on clients to avoid issues that could arise from players joining in progress
|
|
|
|
* and thinking they need to reset this actor when they don't.
|
|
|
|
*/
|
|
|
|
simulated function Reset()
|
|
|
|
{
|
|
|
|
local int SubObjIdx;
|
|
|
|
|
|
|
|
//Undo shutdown if necessary
|
|
|
|
if (bShutDown)
|
|
|
|
{
|
|
|
|
UnShutDownObject();
|
|
|
|
}
|
|
|
|
|
|
|
|
//Reset health
|
|
|
|
for (SubObjIdx = 0; SubObjIdx < SubObjects.Length; ++SubObjIdx)
|
|
|
|
{
|
|
|
|
SubObjects[SubObjIdx].Health = SubObjects[SubObjIdx].DefaultHealth;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( !bAnyDamageModApplied )
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
RemoveDecals();
|
|
|
|
UnDestroy();
|
|
|
|
bForceNetUpdate = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
function ToggleAllDamage(bool bDamageOn)
|
|
|
|
{
|
|
|
|
bIgnoreAllDamage = !bDamageOn;
|
|
|
|
}
|
|
|
|
|
|
|
|
defaultproperties
|
|
|
|
{
|
|
|
|
Begin Object Class=SpriteComponent Name=Sprite
|
|
|
|
Sprite=Texture2D'EditorResources.S_Actor'
|
|
|
|
HiddenGame=True
|
|
|
|
AlwaysLoadOnClient=False
|
|
|
|
AlwaysLoadOnServer=False
|
|
|
|
End Object
|
|
|
|
Components.Add(Sprite)
|
|
|
|
|
|
|
|
Begin Object Class=AkComponent Name=AmbientSoundComponent0
|
|
|
|
bStopWhenOwnerDestroyed=TRUE
|
|
|
|
bForceOcclusionUpdateInterval=true
|
|
|
|
// OcclusionCheckInterval set in PostBeginPlay
|
|
|
|
End Object
|
|
|
|
AmbientSoundComponent=AmbientSoundComponent0
|
|
|
|
|
|
|
|
VulnerableMultiplier=6
|
|
|
|
VulnerableDamageType(0)=class'KFDT_Sonic'
|
|
|
|
|
|
|
|
// no physics by default
|
|
|
|
Physics=PHYS_None
|
|
|
|
bMovable=FALSE
|
|
|
|
|
|
|
|
// many of these properties are pulled in from KActor
|
|
|
|
// since this class was originally derived from KActor
|
|
|
|
bStatic=false
|
|
|
|
bCollideWorld=false
|
|
|
|
bProjTarget=true
|
|
|
|
bBlockActors=true
|
|
|
|
bWorldGeometry=true
|
|
|
|
bNoDelete=true
|
|
|
|
bCollideActors=true
|
|
|
|
bNoEncroachCheck=true
|
|
|
|
bBlocksTeleport=true
|
|
|
|
bBlocksNavigation=true
|
|
|
|
|
|
|
|
bEdShouldSnap=true
|
|
|
|
bGameRelevant=true
|
|
|
|
bPathColliding=false
|
|
|
|
|
|
|
|
// Without this explosions won't do damage to them because they are world geometry
|
|
|
|
bCanBeDamaged=true
|
|
|
|
bIgnoreAllDamage=false
|
|
|
|
|
|
|
|
// network - see EDestructibleRepType
|
|
|
|
RemoteRole=ROLE_None
|
|
|
|
bAlwaysRelevant=true
|
|
|
|
bOnlyDirtyReplication=true
|
|
|
|
bSkipActorPropertyReplication=true
|
|
|
|
NetUpdateFrequency=0.1
|
|
|
|
bIgnoreNetRelevancyCollision=true
|
|
|
|
|
|
|
|
SelfDestructAllDelay=0.2f
|
|
|
|
bAllowBumpDamageFromAI=true
|
|
|
|
INSTAKILL_DAMAGE=100000
|
|
|
|
//NavMeshObjectState=ENavMeshObjectState_None
|
|
|
|
}
|