class Fence extends BaseBuildingBase
{
const int GATE_STATE_NONE = 0;
const int GATE_STATE_PARTIAL = 1;
const int GATE_STATE_FULL = 2;
const string ATTACHMENT_SLOT_COMBINATION_LOCK = "Att_CombinationLock";
const string SOUND_GATE_OPEN_START = "DoorWoodTowerOpen_SoundSet";
const string SOUND_GATE_CLOSE_START = "DoorWoodTowerClose_start_SoundSet";
const string SOUND_GATE_CLOSE_END = "DoorWoodTowerClose_end_SoundSet";
//gate openining
const float GATE_ROTATION_ANGLE_DEG = 100;
const float GATE_ROTATION_TIME_APPROX = 2000; //ms
const float MAX_ACTION_DETECTION_ANGLE_RAD = 1.3; //1.3 RAD = ~75 DEG
const float MAX_ACTION_DETECTION_DISTANCE = 2.0; //meters
typename ATTACHMENT_WOODEN_LOG = WoodenLog;
typename ATTACHMENT_COMBINATION_LOCK = CombinationLock;
string ATTSLOT_CAMONET = "Wall_Camonet";
string ATTSLOT_BARBEDWIRE_DOWN = "Wall_Barbedwire_1";
string ATTSLOT_BARBEDWIRE_UP = "Wall_Barbedwire_2";
//protected bool m_HasHinges = false;
//protected bool m_GateFullyConstructed = false;
protected bool m_ToDiscard = false; //for legacy OnStoreLoad handling
protected bool m_IsOpened = false;
protected bool m_IsOpenedClient = false;
protected int m_GateState = 0;
protected EffectSound m_SoundGate_Start;
protected EffectSound m_SoundGate_End;
void Fence()
{
//synchronized variables
//RegisterNetSyncVariableBool( "m_HasHinges" );
//RegisterNetSyncVariableBool( "m_GateFullyConstructed" );
RegisterNetSyncVariableBool( "m_IsOpened" );
RegisterNetSyncVariableInt( "m_GateState" );
}
override string GetConstructionKitType()
{
return "FenceKit";
}
override int GetMeleeTargetType()
{
return EMeleeTargetType.NONALIGNABLE;
}
//Gate
bool HasHinges()
{
return m_GateState > GATE_STATE_NONE;
}
bool HasFullyConstructedGate()
{
return m_GateState == GATE_STATE_FULL;
}
void SetGateState( int state )
{
m_GateState = state;
SetSynchDirty();
}
int GetGateState()
{
return m_GateState;
}
int CheckGateState()
{
ConstructionPart gate_part = GetConstruction().GetGateConstructionPart();
int state = GATE_STATE_NONE;
if( gate_part.IsBuilt() )
{
ConstructionPart req_part;
array<string> req_parts = gate_part.GetRequiredParts();
for (int i = 0; i < req_parts.Count(); i++)
{
req_part = GetConstruction().GetConstructionPart(req_parts.Get(i));
if(!req_part.IsBuilt())
break;
}
if( i != req_parts.Count() )
{
state = GATE_STATE_PARTIAL;
}
else
{
state = GATE_STATE_FULL;
}
}
return state;
}
void SetOpenedState( bool state )
{
m_IsOpened = state;
}
override bool IsOpened()
{
return m_IsOpened;
}
bool IsLocked()
{
CombinationLock combination_lock = GetCombinationLock();
if ( combination_lock && combination_lock.IsLocked() )
{
return true;
}
return false;
}
override bool NameOverride(out string output)
{
if ( m_GateState != GATE_STATE_NONE )
{
output = "#str_cfgvehicles_construction_part_gate"; //changes object displayed name if in 'gate' state
output.ToUpper();
return true;
}
return false;
}
//
CombinationLock GetCombinationLock()
{
CombinationLock combination_lock = CombinationLock.Cast( FindAttachmentBySlotName( ATTACHMENT_SLOT_COMBINATION_LOCK ) );
return combination_lock;
}
CamoNet GetCamoNet()
{
CamoNet camonet = CamoNet.Cast( FindAttachmentBySlotName( "Wall_Camonet" ) );
return camonet;
}
BarbedWire GetBarbedWire1()
{
BarbedWire barbedwire = BarbedWire.Cast( FindAttachmentBySlotName( "Wall_Barbedwire_1" ) );
return barbedwire;
}
BarbedWire GetBarbedWire2()
{
BarbedWire barbedwire = BarbedWire.Cast( FindAttachmentBySlotName( "Wall_Barbedwire_2" ) );
return barbedwire;
}
//--- CONSTRUCTION KIT
override vector GetKitSpawnPosition()
{
if ( MemoryPointExists( "kit_spawn_position" ) )
{
vector position;
position = GetMemoryPointPos( "kit_spawn_position" );
return ModelToWorld( position );
}
return GetPosition();
}
// --- INVENTORY
override bool CanDisplayAttachmentSlot( int slot_id )
{
if (!super.CanDisplayAttachmentSlot(slot_id))
return false;
string slot_name = InventorySlots.GetSlotName(slot_id);
if ( slot_name == "Att_CombinationLock" )
{
if ( !HasFullyConstructedGate() )
{
return false;
}
}
if ( !GateAttachmentConditions(InventorySlots.GetSlotIdFromString(slot_name)) )
return false;
return true;
}
override bool CanDisplayAttachmentCategory( string category_name )
{
if ( category_name == "Attachments" || category_name == "Material" )
{
if ( !HasBase() )
{
return false;
}
}
return true;
}
// ---
// --- EVENTS
override void OnStoreSave( ParamsWriteContext ctx )
{
super.OnStoreSave( ctx );
//write
ctx.Write( m_GateState );
ctx.Write( m_IsOpened );
if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] OnStoreSave - build=" + m_GateState + " opened=" + m_IsOpened);
}
override bool OnStoreLoad( ParamsReadContext ctx, int version )
{
if ( !super.OnStoreLoad( ctx, version ) )
return false;
//--- Fence data ---
//has gate
if (version < 110)
{
if ( !ctx.Read( m_ToDiscard ) )
{
m_ToDiscard = false;
return false;
}
m_GateState = GATE_STATE_NONE;
}
else if ( !ctx.Read( m_GateState ) )
{
m_GateState = GATE_STATE_NONE;
return false;
}
//is opened
if ( !ctx.Read( m_IsOpened ) )
{
m_IsOpened = false;
return false;
}
if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] OnStoreLoad - build=" + m_GateState + " opened=" + m_IsOpened);
//---
return true;
}
override void AfterStoreLoad()
{
super.AfterStoreLoad();
//set gate state
ConstructionPart gate_part = GetConstruction().GetGateConstructionPart();
SetGateState( CheckGateState() );
//update gate state visual
if ( IsOpened() )
{
OpenFence();
}
UpdateVisuals();
if (LogManager.IsBaseBuildingLogEnable()) bsbDebugPrint("[bsb] AfterStoreLoad - build=" + gate_part.IsBuilt() + " opened=" + IsOpened());
}
override void OnVariablesSynchronized()
{
super.OnVariablesSynchronized();
if ( m_IsOpenedClient != m_IsOpened )
{
m_IsOpenedClient = m_IsOpened;
if ( m_IsOpenedClient )
{
OpenFence();
}
else
{
CloseFence();
}
}
}
//--- BUILD EVENTS
//CONSTRUCTION EVENTS
override void OnPartBuiltServer( notnull Man player, string part_name, int action_id )
{
ConstructionPart constrution_part = GetConstruction().GetConstructionPart( part_name );
super.OnPartBuiltServer( player, part_name, action_id );
SetGateState(CheckGateState());
//update visuals (server)
UpdateVisuals();
}
override void OnPartDismantledServer( notnull Man player, string part_name, int action_id )
{
ConstructionPart constrution_part = GetConstruction().GetConstructionPart( part_name );
//check gate state
if ( constrution_part.IsGate() )
{
if ( IsLocked() )
{
CombinationLock combination_lock = CombinationLock.Cast( FindAttachmentBySlotName( ATTACHMENT_SLOT_COMBINATION_LOCK ) );
combination_lock.UnlockServer( player , this );
}
}
super.OnPartDismantledServer( player, part_name, action_id );
SetGateState( CheckGateState() );
//update visuals (server)
UpdateVisuals();
}
override void OnPartDestroyedServer( Man player, string part_name, int action_id, bool destroyed_by_connected_part = false )
{
super.OnPartDestroyedServer( player, part_name, action_id );
//check gate state
ConstructionPart constrution_part = GetConstruction().GetConstructionPart( part_name );
if ( constrution_part.IsGate() && destroyed_by_connected_part ) //avoids attachment dropping on regular hinges destruction
{
//drop regular attachments
HandleDropAttachment(GetBarbedWire1());
HandleDropAttachment(GetBarbedWire2());
HandleDropAttachment(GetCamoNet());
HandleDropAttachment(GetCombinationLock());
//rotate back to place
if ( IsOpened() )
CloseFence();
}
if ( part_name == "wall_base_down" )
{
HandleDropAttachment(GetBarbedWire1());
HandleDropAttachment(GetCombinationLock());
}
if ( part_name == "wall_base_up" )
{
HandleDropAttachment(GetBarbedWire2());
HandleDropAttachment(GetCamoNet());
HandleDropAttachment(GetCombinationLock());
}
SetGateState( CheckGateState() );
//update visuals (server)
UpdateVisuals();
}
//--- ATTACHMENT & CONDITIONS
override bool CanReceiveAttachment( EntityAI attachment, int slotId )
{
if ( !super.CanReceiveAttachment(attachment, slotId) )
return false;
//manage action initiator (AT_ATTACH_TO_CONSTRUCTION)
if ( !g_Game.IsDedicatedServer() )
{
PlayerBase player = PlayerBase.Cast( g_Game.GetPlayer() );
if ( player )
{
ConstructionActionData construction_action_data = player.GetConstructionActionData();
//reset action initiator
construction_action_data.SetActionInitiator( NULL );
}
}
//conditions
if ( attachment.Type() != ATTACHMENT_WOODEN_LOG )
{
if ( !HasBase() )
{
return false;
}
}
if ( attachment.IsInherited( ATTACHMENT_COMBINATION_LOCK ) )
{
return ( HasFullyConstructedGate() && !IsOpened() );
}
if ( !GateAttachmentConditions(slotId) )
return false;
return true;
}
//hands
override bool CanPutIntoHands( EntityAI parent )
{
if( !super.CanPutIntoHands( parent ) )
{
return false;
}
if ( HasBase() )
{
return false;
}
return true;
}
override bool CanBeRepairedToPristine()
{
return true;
}
//--- OPEN/CLOSE ACTIONS
bool CanOpenFence()
{
if ( HasHinges() && !IsOpened() && !IsLocked() )
{
return true;
}
return false;
}
bool CanCloseFence()
{
if ( HasHinges() && IsOpened() )
{
return true;
}
return false;
}
void OpenFence()
{
//server or single player
if (g_Game.IsServer())
{
float value = GATE_ROTATION_ANGLE_DEG;
SetAnimationPhase( "Wall_Interact_Rotate", value );
SetAnimationPhase( "Wall_Barbedwire_1_Mounted_Rotate", value );
SetAnimationPhase( "Wall_Barbedwire_2_Mounted_Rotate", value );
SetAnimationPhase( "Wall_Camonet_Rotate", value );
SetAnimationPhase( "Wall_Gate_Rotate", value );
SetAnimationPhase( "Wall_Base_Down_Rotate", value );
SetAnimationPhase( "Wall_Base_Up_Rotate", value );
SetAnimationPhase( "Wall_Wood_Down_Rotate", value );
SetAnimationPhase( "Wall_Wood_Up_Rotate", value );
SetAnimationPhase( "Wall_Metal_Down_Rotate", value );
SetAnimationPhase( "Wall_Metal_Up_Rotate", value );
SetOpenedState( true );
//regenerate navmesh
g_Game.GetCallQueue( CALL_CATEGORY_GAMEPLAY ).CallLater( UpdateNavmesh, GATE_ROTATION_TIME_APPROX, false );
//synchronize
SynchronizeBaseState();
}
//client or single player
if (!g_Game.IsDedicatedServer())
{
//play sound
SoundGateOpenStart();
}
//remove BarbedWire AreaDamageTrigger
UpdateBarbedWireAreaDamagePos(0,true);
//add check
g_Game.GetCallQueue(CALL_CATEGORY_GAMEPLAY).Remove(CheckFenceClosed);
g_Game.GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(CheckFenceOpened, 0, true);
}
void CloseFence()
{
//server or single player
if (g_Game.IsServer())
{
float value = 0;
SetAnimationPhase( "Wall_Interact_Rotate", value );
SetAnimationPhase( "Wall_Barbedwire_1_Mounted_Rotate", value );
SetAnimationPhase( "Wall_Barbedwire_2_Mounted_Rotate", value );
SetAnimationPhase( "Wall_Camonet_Rotate", value );
SetAnimationPhase( "Wall_Gate_Rotate", value );
SetAnimationPhase( "Wall_Base_Down_Rotate", value );
SetAnimationPhase( "Wall_Base_Up_Rotate", value );
SetAnimationPhase( "Wall_Wood_Down_Rotate", value );
SetAnimationPhase( "Wall_Wood_Up_Rotate", value );
SetAnimationPhase( "Wall_Metal_Down_Rotate", value );
SetAnimationPhase( "Wall_Metal_Up_Rotate", value );
SetOpenedState( false );
//regenerate navmesh
g_Game.GetCallQueue( CALL_CATEGORY_GAMEPLAY ).CallLater( UpdateNavmesh, GATE_ROTATION_TIME_APPROX, false );
//synchronize
SynchronizeBaseState();
}
//client or single player
if (!g_Game.IsDedicatedServer())
{
//play sound
SoundGateCloseStart();
}
//remove BarbedWire AreaDamageTrigger
UpdateBarbedWireAreaDamagePos(0,true);
//add check
g_Game.GetCallQueue(CALL_CATEGORY_GAMEPLAY).Remove(CheckFenceOpened);
g_Game.GetCallQueue(CALL_CATEGORY_GAMEPLAY).CallLater(CheckFenceClosed, 0, true);
}
protected void CheckFenceOpened()
{
if ( GetAnimationPhase( "Wall_Gate_Rotate" ) == GATE_ROTATION_ANGLE_DEG ) //animation finished - open
{
UpdateBarbedWireAreaDamagePos(GetAnimationPhase( "Wall_Gate_Rotate" ));
//remove check
g_Game.GetCallQueue(CALL_CATEGORY_GAMEPLAY).Remove(CheckFenceOpened);
}
}
protected void CheckFenceClosed()
{
if ( GetAnimationPhase( "Wall_Gate_Rotate" ) == 0 ) //animation finished - closed
{
//client or single player
if ( !g_Game.IsDedicatedServer() )
{
//play sound
if ( this ) SoundGateCloseEnd();
}
UpdateBarbedWireAreaDamagePos(GetAnimationPhase( "Wall_Gate_Rotate" ));
//remove check
g_Game.GetCallQueue(CALL_CATEGORY_GAMEPLAY).Remove(CheckFenceClosed);
}
}
//Damage triggers
override void CreateAreaDamage( string slot_name, float rotation_angle = 0 )
{
if ( IsOpened() )
{
rotation_angle = 100;
}
super.CreateAreaDamage( slot_name, rotation_angle );
}
//BarbedWire update
void UpdateBarbedWireAreaDamagePos(float rotation_angle = 0, bool to_delete = false )
{
int slot_id;
string slot_name;
string slot_name_mounted;
if ( GetBarbedWire1() && GetBarbedWire1().IsMounted() )
{
GetBarbedWire1().GetInventory().GetCurrentAttachmentSlotInfo(slot_id,slot_name);
slot_name_mounted = slot_name + "_Mounted";
if (to_delete)
{
DestroyAreaDamage( slot_name_mounted );
}
else
{
super.CreateAreaDamage( slot_name_mounted, rotation_angle );
}
}
if ( GetBarbedWire2() && GetBarbedWire2().IsMounted() )
{
GetBarbedWire2().GetInventory().GetCurrentAttachmentSlotInfo(slot_id,slot_name);
slot_name_mounted = slot_name + "_Mounted";
if (to_delete)
{
DestroyAreaDamage( slot_name_mounted );
}
else
{
super.CreateAreaDamage( slot_name_mounted, rotation_angle );
}
}
}
//Here deal damage to BarbedWire when entity taking damage from it
override void PostAreaDamageActions()
{
/*if (GetBarbedWire1())
{
//DecreaseHealth("Wall_BarbedWire_1", "", 1000); //why no dmg to wire???
//BarbedWire wire = GetBarbedWire1();
//wire.PostAreaDamageActions();
//Print(GetHealth("BarbedWire1", ""));
}*/
}
//--- ACTION CONDITIONS
override bool IsPlayerInside( PlayerBase player, string selection )
{
vector player_pos = player.GetPosition();
vector fence_pos = GetPosition();
vector ref_dir = GetDirection();
ref_dir[1] = 0;
ref_dir.Normalize();
vector x[2];
vector b1,b2;
GetCollisionBox(x);
b1 = x[0];
b2 = x[1];
vector dir_to_fence = fence_pos - player_pos;
dir_to_fence[1] = 0;
float len = dir_to_fence.Length();
dir_to_fence.Normalize();
vector ref_dir_angle = ref_dir.VectorToAngles();
vector dir_to_fence_angle = dir_to_fence.VectorToAngles();
vector test_angles = dir_to_fence_angle - ref_dir_angle;
vector test_position = test_angles.AnglesToVector() * len;
if(test_position[0] < b1[0] || test_position[0] > b2[0] || test_position[2] < 0.2 || test_position[2] > 2.2 )
{
return false;
}
else
{
return true;
}
}
override bool IsFacingPlayer( PlayerBase player, string selection )
{
vector fence_pos = GetPosition();
vector player_pos = player.GetPosition();
vector ref_dir = GetDirection();
//vector fence_player_dir = player_pos - fence_pos;
vector fence_player_dir = player.GetDirection();
fence_player_dir.Normalize();
fence_player_dir[1] = 0; //ignore height
ref_dir.Normalize();
ref_dir[1] = 0; //ignore height
if ( ref_dir.Length() != 0 )
{
float angle = Math.Acos( fence_player_dir * ref_dir );
if ( angle >= MAX_ACTION_DETECTION_ANGLE_RAD )
{
return true;
}
}
return false;
}
override bool IsFacingCamera( string selection )
{
vector ref_dir = GetDirection();
vector cam_dir = g_Game.GetCurrentCameraDirection();
//ref_dir = g_Game.GetCurrentCameraPosition() - GetPosition();
ref_dir.Normalize();
ref_dir[1] = 0; //ignore height
cam_dir.Normalize();
cam_dir[1] = 0; //ignore height
if ( ref_dir.Length() != 0 )
{
float angle = Math.Acos( cam_dir * ref_dir );
if ( angle >= MAX_ACTION_DETECTION_ANGLE_RAD )
{
return true;
}
}
return false;
}
override bool HasProperDistance( string selection, PlayerBase player )
{
if ( MemoryPointExists( selection ) )
{
vector selection_pos = ModelToWorld( GetMemoryPointPos( selection ) );
float distance = vector.Distance( selection_pos, player.GetPosition() );
if ( distance >= MAX_ACTION_DETECTION_DISTANCE )
{
return false;
}
}
return true;
}
override bool CanUseConstructionBuild()
{
return !IsOpened();
}
//================================================================
// SOUNDS
//================================================================
protected void SoundGateOpenStart()
{
//client or single player
if ( !g_Game.IsDedicatedServer() )
{
PlaySoundSet( m_SoundGate_Start, SOUND_GATE_OPEN_START, 0.1, 0.1 );
}
}
protected void SoundGateCloseStart()
{
//client or single player
if ( !g_Game.IsDedicatedServer() )
{
PlaySoundSet( m_SoundGate_Start, SOUND_GATE_CLOSE_START, 0.1, 0.1 );
}
}
protected void SoundGateCloseEnd()
{
//client or single player
if ( !g_Game.IsDedicatedServer() )
{
PlaySoundSet( m_SoundGate_End, SOUND_GATE_CLOSE_END, 0.1, 0.1 );
}
}
void GateAttachmentsSanityCheck()
{
ConstructionPart wall_base_down = GetConstruction().GetConstructionPart("wall_base_down");
ConstructionPart wall_base_up = GetConstruction().GetConstructionPart("wall_base_up");
if(GetBarbedWire1() && !wall_base_down.IsBuilt())
{
HandleDropAttachment(GetBarbedWire1());
HandleDropAttachment(GetCombinationLock());
}
if( ( GetCamoNet() || GetBarbedWire2() ) && !wall_base_up.IsBuilt() )
{
HandleDropAttachment(GetBarbedWire2());
HandleDropAttachment(GetCamoNet());
HandleDropAttachment(GetCombinationLock());
}
}
void HandleDropAttachment(ItemBase item)
{
BarbedWire wire;
CombinationLock lock;
if (Class.CastTo(wire,item)) //special barbed wire beh.
{
wire.SetMountedState( false );
GetInventory().DropEntity(InventoryMode.SERVER, this, wire);
}
else if (Class.CastTo(lock,item))
{
lock.UnlockServer(null,this);
}
else if (item)//generic behaviour
{
GetInventory().DropEntity(InventoryMode.SERVER, this, item);
}
}
bool GateAttachmentConditions(int slotId)
{
if ( GetGateState() == GATE_STATE_PARTIAL )
{
ConstructionPart wall_base_down = GetConstruction().GetConstructionPart("wall_base_down");
ConstructionPart wall_base_up = GetConstruction().GetConstructionPart("wall_base_up");
string slot_name = InventorySlots.GetSlotName(slotId);
if ( !wall_base_up.IsBuilt() )
{
if ( slot_name == ATTSLOT_CAMONET || slot_name == ATTSLOT_BARBEDWIRE_UP )
{
return false;
}
}
if ( !wall_base_down.IsBuilt() )
{
if ( slot_name == ATTSLOT_BARBEDWIRE_DOWN )
{
return false;
}
}
}
return true;
}
//specific selection for camonet attaching (other ones might be animated via rotation!)
override bool TranslateSlotFromSelection(string selection_name, out int slot_id)
{
if ( selection_name == "wall_camonet_attach" )
{
slot_id = InventorySlots.GetSlotIdFromString("Wall_Camonet");
return true;
}
return false;
}
override void SetActions()
{
super.SetActions();
AddAction(ActionTogglePlaceObject);
AddAction(ActionPlaceObject);
AddAction(ActionFoldBaseBuildingObject);
//AddAction(ActionDialCombinationLockOnTarget);
//AddAction(ActionNextCombinationLockDialOnTarget);
AddAction(ActionOpenFence);
AddAction(ActionCloseFence);
}
//================================================================
// DEBUG
//================================================================
/*
override void DebugCustomState()
{
//debug
m_SyncParts01 = 881; //full fence with gate
m_HasHinges = true;
m_HasBase = true;
OnVariablesSynchronized();
}
*/
//! Excludes certain parts from being built by OnDebugSpawn, uses Contains to compare
override array<string> OnDebugSpawnBuildExcludes()
{
array<string> excludes = {};
#ifdef DIAG_DEVELOPER
bool bWood = DiagMenu.GetBool(DiagMenuIDs.BASEBUILDING_WOOD);
#else
bool bWood = false;
#endif
if (bWood)
{
excludes.Insert("_metal_");
}
else
{
excludes.Insert("_wood_");
}
#ifdef DIAG_DEVELOPER
bool bGate = DiagMenu.GetBool(DiagMenuIDs.BASEBUILDING_GATE);
#else
bool bGate = false;
#endif
if (bGate)
{
excludes.Insert("platform");
}
else
{
excludes.Insert("gate");
}
return excludes;
}
//Debug menu Spawn Ground Special
override void OnDebugSpawn()
{
super.OnDebugSpawn();
GetInventory().CreateInInventory("CamoNet");
for (int i = 0; i < 2; ++i)
{
BarbedWire wire = BarbedWire.Cast(GetInventory().CreateInInventory("BarbedWire"));
wire.SetMountedState(true);
}
}
}