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 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 ( !GetGame().IsDedicatedServer() ) { PlayerBase player = PlayerBase.Cast( GetGame().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 ( GetGame().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 GetGame().GetCallQueue( CALL_CATEGORY_GAMEPLAY ).CallLater( UpdateNavmesh, GATE_ROTATION_TIME_APPROX, false ); //synchronize SynchronizeBaseState(); } //client or single player if ( !GetGame().IsDedicatedServer() ) { //play sound SoundGateOpenStart(); } //remove BarbedWire AreaDamageTrigger UpdateBarbedWireAreaDamagePos(0,true); //add check GetGame().GetCallQueue( CALL_CATEGORY_GAMEPLAY ).Remove(CheckFenceClosed); GetGame().GetCallQueue( CALL_CATEGORY_GAMEPLAY ).CallLater( CheckFenceOpened, 0, true ); } void CloseFence() { //server or single player if ( GetGame().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 GetGame().GetCallQueue( CALL_CATEGORY_GAMEPLAY ).CallLater( UpdateNavmesh, GATE_ROTATION_TIME_APPROX, false ); //synchronize SynchronizeBaseState(); } //client or single player if ( !GetGame().IsDedicatedServer() ) { //play sound SoundGateCloseStart(); } //remove BarbedWire AreaDamageTrigger UpdateBarbedWireAreaDamagePos(0,true); //add check GetGame().GetCallQueue( CALL_CATEGORY_GAMEPLAY ).Remove(CheckFenceOpened); GetGame().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 GetGame().GetCallQueue( CALL_CATEGORY_GAMEPLAY ).Remove( CheckFenceOpened ); } } protected void CheckFenceClosed() { if ( GetAnimationPhase( "Wall_Gate_Rotate" ) == 0 ) //animation finished - closed { //client or single player if ( !GetGame().IsDedicatedServer() ) { //play sound if ( this ) SoundGateCloseEnd(); } UpdateBarbedWireAreaDamagePos(GetAnimationPhase( "Wall_Gate_Rotate" )); //remove check GetGame().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 = GetGame().GetCurrentCameraDirection(); //ref_dir = GetGame().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 ( !GetGame().IsDedicatedServer() ) { PlaySoundSet( m_SoundGate_Start, SOUND_GATE_OPEN_START, 0.1, 0.1 ); } } protected void SoundGateCloseStart() { //client or single player if ( !GetGame().IsDedicatedServer() ) { PlaySoundSet( m_SoundGate_Start, SOUND_GATE_CLOSE_START, 0.1, 0.1 ); } } protected void SoundGateCloseEnd() { //client or single player if ( !GetGame().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 OnDebugSpawnBuildExcludes() { array 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); } } }