enum SoundTypeTrap
{
ACTIVATING = 5,
}
class TrapBase extends ItemBase
{
#ifdef SERVER
protected const int SPAWN_FLAGS = ECE_CREATEPHYSICS;
#else
protected const int SPAWN_FLAGS = ECE_LOCAL;
#endif
protected const int DAMAGE_TRIGGER_MINE = 75;
protected const float UPDATE_TIMER_INTERVAL = 0.05;
float m_InitWaitTime; //After this time after deployment, the trap is activated
bool m_NeedActivation; //If activation of trap is needed
float m_DefectRate; //Added damage after trap activation
float m_DamagePlayers; //How much damage player gets when caught
float m_DamageOthers; //How much damage player gets when caught
bool m_AddActivationDefect; // Damage trap after activation
bool m_AddDeactivationDefect; // Damage trap after deactivation
protected bool m_IsActive; // True means that the trap is ready to detonate
protected bool m_IsInProgress;
protected bool m_Disarmed = false; //! DEPRECATED Used for explosive traps to prevent detonation after destroying through disarm action
bool m_WasActivatedOrDeactivated;
string m_AnimationPhaseGrounded;
string m_AnimationPhaseSet;
string m_AnimationPhaseTriggered;
string m_InfoSetup;
string m_InfoDeactivated;
string m_InfoDamageManipulation;
string m_InfoDamage;
string m_InfoActivationTime;
protected ref Timer m_Timer;
protected ref Timer m_UpdateTimer;
protected TrapTrigger m_TrapTrigger;
protected ref array<int> m_ClothingDmg;
void TrapBase()
{
m_IsInProgress = false;
m_NeedActivation = true;
m_InitWaitTime = 5; //After this time after deployment, the trap is activated
m_DefectRate = 15; //Added damage after trap activation
m_DamagePlayers = 25; //How much damage player gets when caught
m_DamageOthers = 100; //How much damage others gets when caught
m_AddActivationDefect = false;
m_AddDeactivationDefect = false;
m_WasActivatedOrDeactivated = false;
m_AnimationPhaseGrounded = "";
m_AnimationPhaseSet = "";
m_AnimationPhaseTriggered = "";
m_InfoSetup = "#STR_TrapBase0";
m_InfoDeactivated = "#STR_TrapBase1";
m_InfoDamageManipulation = "#STR_TrapBase2";
m_InfoDamage = "#STR_TrapBase3";
m_InfoActivationTime = "#STR_TrapBase4" + m_InitWaitTime.ToString() + "#STR_TrapBase5";
m_UpdateTimer = new Timer(); //! timer calling OnUpdate in configured interval
RegisterNetSyncVariableBool("m_IsActive");
RegisterNetSyncVariableBool("m_IsInProgress");
}
void OnUpdate(EntityAI victim);
TrapTrigger GetTrapTrigger()
{
return m_TrapTrigger;
}
//! this event is called all variables are synchronized on client
override void OnVariablesSynchronized()
{
super.OnVariablesSynchronized();
if (g_Game.IsMultiplayer())
{
if (m_IsActive && !m_IsInProgress)
SetActive();
if (m_IsInProgress && !m_IsActive)
StartActivate(null);
}
}
override void EEDelete(EntityAI parent)
{
super.EEDelete(parent);
if (g_Game && m_TrapTrigger)
{
g_Game.ObjectDelete(m_TrapTrigger);
m_TrapTrigger = null;
}
}
override void OnStoreSave(ParamsWriteContext ctx)
{
super.OnStoreSave(ctx);
ctx.Write(m_IsActive);
ctx.Write(m_IsInProgress);
}
//----------------------------------------------------------------
override bool OnStoreLoad(ParamsReadContext ctx, int version)
{
if ( !super.OnStoreLoad(ctx, version) )
return false;
bool b_is_active = false;
if ( !ctx.Read( b_is_active ) )
b_is_active = false;
bool b_is_in_progress = false;
if ( !ctx.Read( b_is_in_progress ) )
b_is_in_progress = false;
if ( b_is_active )
{
SetActive();
}
if (b_is_in_progress && !b_is_active)
{
StartActivate(null);
}
return true;
}
bool IsActive()
{
return m_IsActive && m_IsInProgress == false && GetHierarchyRootPlayer() == null;
}
bool IsInactive()
{
return !IsActive() && m_IsInProgress == false && GetHierarchyRootPlayer() == null;
}
// trap cannot be taken when is activated
override bool IsTakeable()
{
if ( m_IsInProgress == false && !IsActive() )
{
return true;
}
return false;
}
bool IsActivable()
{
return !IsActive() && GetHierarchyRootPlayer() == null && GetHierarchyParent() == null && m_IsInProgress == false && !IsRuined() && m_NeedActivation;
}
bool IsPlaceable()
{
if ( GetHierarchyRootPlayer() != null && GetHierarchyRootPlayer().GetEntityInHands() == this )
{
PlayerBase player = PlayerBase.Cast( GetHierarchyRootPlayer() );
vector player_pos = player.GetPosition();
vector aim_pos = player.GetAimPosition();
if ( vector.DistanceSq( player_pos, aim_pos ) <= ( Math.SqrFloat( 1.5 ) ) )
{
return IsPlaceableAtPosition( aim_pos );
}
}
return false;
}
bool IsPlaceableAtPosition( vector position )
{
if ( position[1] < g_Game.SurfaceGetSeaLevelMax() + 0.03 )
{
return false;
}
else if ( g_Game.SurfaceIsPond( position[0], position[2] ) )
{
return false;
}
return true;
}
void Disarm()
{
SetInactive(false);
RefreshState();
g_Game.RPCSingleParam(this, ERPCs.RPC_TRAP_DISARM, null, true);
OnDisarm();
}
//! also called from RPC on client
void OnDisarm();
void SnapOnObject(EntityAI victim)
{
if ( g_Game.IsServer() )
{
if ( m_Timer )
{
m_Timer.Stop();
}
RefreshState();
if (m_DamagePlayers > 0)
{
if (victim)
{
if ( victim.IsInherited(SurvivorBase))
{
victim.DecreaseHealth("", "", m_DamagePlayers);
}
else if (victim.IsInherited(DayZCreatureAI))
{
victim.DecreaseHealth("", "", m_DamageOthers);
}
else if (victim.IsInherited(ItemBase))
{
ItemBase victim_item = ItemBase.Cast(victim);
float damage_coef = 1;
if (victim_item.HasQuantity() && victim_item.GetQuantityMax() != 0 && victim_item.GetQuantity() > 0)
{
damage_coef = victim_item.GetQuantityMax() / victim_item.GetQuantity(); // Lower quantity increases damage exposure
}
if (damage_coef > 0)
{
int item_size_x = 1;
int item_size_y = 1;
g_Game.GetInventoryItemSize(victim_item, item_size_x, item_size_y);
float add_damage = 300 * damage_coef / Math.Clamp(item_size_x * item_size_y, 1, int.MAX);
victim_item.DecreaseHealth("", "", add_damage);
}
}
}
}
Synch(victim);
}
OnSteppedOn(victim);
}
void RemoveFromObject(EntityAI victim)
{
OnSteppedOut(victim);
}
void OnSteppedOn(EntityAI victim)
{
SetInactive(false);
}
void OnSteppedOut(EntityAI victim); //! keeping "step" here for consistency only
// Synchronizes states
protected void Synch(EntityAI victim)
{
if (g_Game.IsServer())
{
if (victim && !victim.GetAllowDamage())
return;
Param1<EntityAI> p = new Param1<EntityAI>(victim);
g_Game.RPCSingleParam(this, ERPCs.RPC_TRAP_VICTIM, p, true);
}
}
// On server -> client synchronization
override void OnRPC(PlayerIdentity sender, int rpc_type, ParamsReadContext ctx)
{
super.OnRPC(sender, rpc_type, ctx);
if ( !g_Game.IsDedicatedServer() )
{
switch (rpc_type)
{
case ERPCs.RPC_TRAP_VICTIM:
Param1<EntityAI> victim = new Param1<EntityAI>(null);
if (ctx.Read(victim))
{
if (victim.param1)
SnapOnObject(victim.param1);
}
break;
case ERPCs.RPC_TRAP_DISARM:
OnDisarm();
break;
}
}
}
void RefreshState()
{
if ( !m_WasActivatedOrDeactivated )
{
return;
}
if ( g_Game.IsServer() )
{
// item is owned by player
if ( GetHierarchyRootPlayer() != NULL && m_AnimationPhaseGrounded != "" )
{
SetAnimationPhase( m_AnimationPhaseSet, 1 );
if ( m_AnimationPhaseTriggered != m_AnimationPhaseGrounded )
{
SetAnimationPhase( m_AnimationPhaseTriggered, 1 );
}
SetAnimationPhase( m_AnimationPhaseGrounded, 0 );
}
// item is set active
else if ( IsActive() )
{
if ( m_AnimationPhaseGrounded != "" )
{
SetAnimationPhase( m_AnimationPhaseGrounded, 1 );
}
if ( m_AnimationPhaseSet != "" && m_AnimationPhaseTriggered != "" )
{
SetAnimationPhase( m_AnimationPhaseTriggered, 1 );
SetAnimationPhase( m_AnimationPhaseSet, 0 );
}
}
// item is inactive and not owned by player (on the ground)
else if ( IsInactive() )
{
if ( m_AnimationPhaseGrounded != "" && m_AnimationPhaseTriggered != m_AnimationPhaseGrounded )
{
SetAnimationPhase( m_AnimationPhaseGrounded, 1 );
}
if ( m_AnimationPhaseSet != "" && m_AnimationPhaseTriggered != "" )
{
SetAnimationPhase( m_AnimationPhaseSet, 1 );
SetAnimationPhase( m_AnimationPhaseTriggered, 0 );
}
}
}
}
void SetupTrap()
{
if (g_Game.IsServer())
{
if (GetHierarchyRootPlayer() && GetHierarchyRootPlayer().CanDropEntity(this))
SetupTrapPlayer(PlayerBase.Cast(GetHierarchyRootPlayer()));
}
}
void SetupTrapPlayer( PlayerBase player, bool set_position = true )
{
if (g_Game.IsServer())
{
if (set_position)
{
player.LocalDropEntity(this);
vector trapPos = player.GetDirection() * 1.5;
trapPos[1] = 0;
SetPosition(player.GetPosition() + trapPos);
}
if (m_NeedActivation == false)
SetActive();
}
}
void AddDefect()
{
if ( g_Game.IsServer() )
{
DecreaseHealth( "", "", m_DefectRate );
}
}
void SetActive()
{
m_WasActivatedOrDeactivated = true;
m_IsInProgress = false;
m_IsActive = true;
if (m_AddActivationDefect)
{
AddDefect();
}
if (g_Game.IsServer())
{
CreateTrigger();
RefreshState();
SetSynchDirty();
}
OnActivate();
}
void OnActivate();
void StartActivate(PlayerBase player)
{
if (g_Game.IsServer())
{
m_Timer = new Timer(CALL_CATEGORY_SYSTEM);
HideSelection("safety_pin");
if (m_InitWaitTime > 0)
{
m_IsInProgress = true;
m_Timer.Run(m_InitWaitTime, this, "SetActive");
SetSynchDirty();
}
else
SetActive();
}
}
void StartDeactivate(PlayerBase player);
void SetInactive(bool stop_timer = true)
{
m_WasActivatedOrDeactivated = true;
m_IsActive = false;
if (m_Timer && stop_timer)
m_Timer.Stop();
if (m_AddDeactivationDefect)
AddDefect();
SetSynchDirty();
DeleteTrigger();
RefreshState();
}
void CreateTrigger()
{
if (Class.CastTo(m_TrapTrigger, g_Game.CreateObjectEx("TrapTrigger", GetPosition(), SPAWN_FLAGS)))
{
vector mins = "-0.01 -0.05 -0.01";
vector maxs = "0.01 0.5 0.01";
m_TrapTrigger.SetOrientation(GetOrientation());
m_TrapTrigger.SetExtents(mins, maxs);
m_TrapTrigger.SetParentObject(this);
g_Game.GetCallQueue(CALL_CATEGORY_SYSTEM).Call(DeferredEnableTrigger);
}
}
void DeleteTrigger()
{
if (m_TrapTrigger)
{
m_TrapTrigger.SetParentObject(null);
m_TrapTrigger.DeleteSafe();
}
}
void DeferredEnableTrigger()
{
if (m_TrapTrigger)
m_TrapTrigger.SetEnabled();
}
override void OnItemLocationChanged(EntityAI old_owner, EntityAI new_owner)
{
super.OnItemLocationChanged(old_owner, new_owner);
if (g_Game.IsServer())
{
RefreshState();
// TAKE ACTIVE TRAP FROM VICINITY
if (old_owner == NULL && new_owner != NULL && IsActive())
{
// TAKE INTO HANDS
if ( new_owner.IsPlayer() )
{
SnapOnObject(new_owner);
}
else if (new_owner.GetHierarchyRootPlayer())
{
SnapOnObject(new_owner.GetHierarchyRootPlayer());
}
}
}
}
override void EEItemAttached(EntityAI item, string slot_name)
{
super.EEItemAttached(item, slot_name);
if (g_Game.IsServer())
RefreshState();
}
override void EEItemDetached(EntityAI item, string slot_name)
{
super.EEItemDetached(item, slot_name);
if (g_Game.IsServer())
RefreshState();
}
override void OnPlacementComplete(Man player, vector position = "0 0 0", vector orientation = "0 0 0")
{
super.OnPlacementComplete(player, position, orientation);
if (g_Game.IsServer())
{
SetOrientation(orientation);
SetPosition(position);
PlaceOnSurface();
SetSynchDirty();
}
}
override bool CanPutInCargo( EntityAI parent )
{
if ( !super.CanPutInCargo(parent) )
{
return false;
}
return IsTakeable();
}
override bool CanPutIntoHands( EntityAI parent )
{
if ( !super.CanPutIntoHands( parent ) )
{
return false;
}
return IsTakeable();
}
override bool CanRemoveFromHands( EntityAI parent )
{
return IsTakeable();
}
override bool CanBePlaced( Man player, vector position )
{
return IsPlaceableAtPosition( position );
}
//! DEPRECATED Set if trap can be disarmed using ActionClapBearTrapWithThisItem
bool CanBeClapped()
{
return true;
}
//Set if trap can be disarmed using trap-specific action
bool CanBeDisarmed()
{
return false;
}
//! DEPRECATED
void SetDisarmed( bool disarmed )
{
m_Disarmed = disarmed;
}
//! DEPRECATED
bool GetDisarmed()
{
return m_Disarmed;
}
//================================================================
// ADVANCED PLACEMENT
//================================================================
override void SetActions()
{
super.SetActions();
AddAction(ActionActivateTrap);
}
// HELPERS
protected EntityAI GetClosestCarWheel(EntityAI victim)
{
//! carscript specific handling (not all traps are uses this)
vector trapPosXZ = GetPosition();
trapPosXZ[1] = 0;
GameInventory inv = victim.GetInventory();
for (int i = 0; i < inv.AttachmentCount(); i++)
{
//! ruined wheel, bail out
EntityAI wheelEntity = inv.GetAttachmentFromIndex(i);
if (wheelEntity && wheelEntity.Type() == CarWheel_Ruined)
{
continue;
}
//! ignore all spare wheel
int slotId;
string slotName;
wheelEntity.GetInventory().GetCurrentAttachmentSlotInfo(slotId, slotName);
slotName.ToLower();
if (slotName.Contains("spare"))
{
continue
}
//! actual, healthy wheel, let it pass
if (wheelEntity && wheelEntity.IsInherited(CarWheel))
{
vector entPosXZ = wheelEntity.GetPosition();
entPosXZ[1] = 0;
if (vector.Distance(trapPosXZ, entPosXZ) < 1)
{
return wheelEntity;
}
}
}
return null;
}
protected void DamageClothing(PlayerBase player)
{
//Array used to find all relevant information about currently equipped clothes
array<ClothingBase> equippedClothes = new array<ClothingBase>;
equippedClothes.Insert(ClothingBase.Cast(player.GetItemOnSlot("LEGS")));
equippedClothes.Insert(ClothingBase.Cast(player.GetItemOnSlot("BACK")));
equippedClothes.Insert(ClothingBase.Cast(player.GetItemOnSlot("VEST")));
equippedClothes.Insert(ClothingBase.Cast(player.GetItemOnSlot("HeadGear")));
equippedClothes.Insert(ClothingBase.Cast(player.GetItemOnSlot("Mask")));
equippedClothes.Insert(ClothingBase.Cast(player.GetItemOnSlot("BODY")));
equippedClothes.Insert(ClothingBase.Cast(player.GetItemOnSlot("FEET")));
equippedClothes.Insert(ClothingBase.Cast(player.GetItemOnSlot("GLOVES")));
//Damage all currently equipped clothes
for (int i = 0; i < equippedClothes.Count(); i++)
{
//If no item is equipped on slot, slot is ignored
if (equippedClothes[i] == null)
{
continue;
}
equippedClothes[i].DecreaseHealth(m_ClothingDmg[i], false);
}
}
//!DEPRECATED
protected ref EffectSound m_DeployLoopSound; //DEPRECATED in favor of m_DeployLoopSoundEx
void PlayDeployLoopSound(); //!DEPRECATED
void StopDeployLoopSound(); //!DEPRECATED
}