class TerritoryFlag extends BaseBuildingBase
{
const float MAX_ACTION_DETECTION_ANGLE_RAD = 1.3; //1.3 RAD = ~75 DEG
const float MAX_ACTION_DETECTION_DISTANCE = 2.0; //meters
bool m_RefresherActive;
bool m_RefresherActiveLocal;
bool m_RefresherInitialized;
int m_RefresherTimeRemaining;
int m_RefreshTimeCounter;
protected int m_FlagRefresherFrequency = GameConstants.REFRESHER_FREQUENCY_DEFAULT; //how often does the flag increase lifetimes
protected int m_FlagRefresherMaxDuration = GameConstants.REFRESHER_MAX_DURATION_DEFAULT; //how long will the refresher run; multiple of m_FlagRefresherFrequency by default
void TerritoryFlag()
{
m_RefresherActive = false;
m_RefresherActiveLocal = false;
m_RefresherInitialized = false;
m_RefresherTimeRemaining = 0;
if ( GetCEApi() )
{
InitRefresherData();
}
RegisterNetSyncVariableBool("m_RefresherActive");
}
void ~TerritoryFlag()
{
RemoveRefresherPosition();
}
void InitRefresherData()
{
int frequency = GetCEApi().GetCEGlobalInt("FlagRefreshFrequency");
int max_duration = GetCEApi().GetCEGlobalInt("FlagRefreshMaxDuration");
if ( frequency > 0)
{
m_FlagRefresherFrequency = frequency;
}
if ( max_duration > 0 )
{
m_FlagRefresherMaxDuration = max_duration;
}
m_RefresherInitialized = true;
}
override string GetConstructionKitType()
{
return "TerritoryFlagKit";
}
override int GetMeleeTargetType()
{
return EMeleeTargetType.NONALIGNABLE;
}
/*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;
}*/
//--- CONSTRUCTION KIT
override vector GetKitSpawnPosition()
{
if ( MemoryPointExists( "kit_spawn_position" ) )
{
vector position;
position = GetMemoryPointPos( "kit_spawn_position" );
return ModelToWorld( position );
}
return GetPosition();
}
override bool CanDisplayAttachmentCategory( string category_name )
{
if ( category_name == "Base" && !HasBase() )
return true;
else if ( category_name == "Support" && HasBase() && !GetConstruction().IsPartConstructed("support") )
return true;
else if ( category_name == "Pole" && GetConstruction().IsPartConstructed("support") && !GetConstruction().IsPartConstructed("pole") )
return true;
else if ( category_name == "Flag" && GetConstruction().IsPartConstructed("pole") )
return true;
else
return false;
}
// ---
// --- EVENTS
override void OnStoreSave( ParamsWriteContext ctx )
{
super.OnStoreSave( ctx );
ctx.Write( m_RefresherTimeRemaining );
ctx.Write( m_RefreshTimeCounter );
ctx.Write( m_FlagRefresherMaxDuration );
ctx.Write( m_RefresherActive );
}
override bool OnStoreLoad( ParamsReadContext ctx, int version )
{
if ( !super.OnStoreLoad( ctx, version ) )
return false;
//int loaded_frequency;
int loaded_max_duration;
if ( !ctx.Read( m_RefresherTimeRemaining ) )
{
return false;
}
if ( !ctx.Read( m_RefreshTimeCounter ) )
{
return false;
}
if ( !ctx.Read( loaded_max_duration ) )
{
return false;
}
if ( version >= 118 && !ctx.Read( m_RefresherActive ) )
{
return false;
}
CheckLoadedVariables(loaded_max_duration);
AnimateFlag(1 - GetRefresherTime01());
return true;
}
override void AfterStoreLoad()
{
super.AfterStoreLoad();
if (!m_RefresherInitialized && GetCEApi())
{
InitRefresherData();
}
SetSynchDirty();
UpdateVisuals();
}
override void OnCEUpdate()
{
super.OnCEUpdate();
int time_elapsed_rounded = Math.Round(m_ElapsedSinceLastUpdate);
if ( m_RefresherTimeRemaining > 0 )
{
m_RefreshTimeCounter += time_elapsed_rounded;
if ( m_RefreshTimeCounter >= m_FlagRefresherFrequency )
{
GetCEApi().RadiusLifetimeReset(GetPosition(),GameConstants.REFRESHER_RADIUS);
m_RefresherTimeRemaining = Math.Clamp(m_RefresherTimeRemaining - m_RefreshTimeCounter,0,m_FlagRefresherMaxDuration);
m_RefreshTimeCounter = 0; //possibly carry over the rest?
AnimateFlag( 1 - GetRefresherTime01() );
}
}
SetRefresherActive(m_RefresherTimeRemaining > 0);
}
//! Saves positions of active lifetime refreshers to MissionGameplay / MissionServer
void HandleRefreshers()
{
Mission mission = g_Game.GetMission();
if (!mission.m_ActiveRefresherLocations)
return;
int idx = mission.m_ActiveRefresherLocations.Find(GetPosition());
if ( m_RefresherActive && idx == -1 )
{
InsertRefresherPosition();
}
else if ( !m_RefresherActive && idx > -1 )
{
RemoveRefresherPosition(idx);
}
}
void SetRefresherActive(bool state)
{
if ( m_RefresherActive != state )
{
m_RefresherActive = state;
SetSynchDirty();
HandleRefreshers();
//update on refresher activation / last update on refresher deactivation
if (GetCEApi())
GetCEApi().RadiusLifetimeReset(GetPosition(),GameConstants.REFRESHER_RADIUS);
}
}
void InsertRefresherPosition()
{
Mission mission = g_Game.GetMission();
mission.m_ActiveRefresherLocations.Insert(GetPosition());
}
void RemoveRefresherPosition(int idx = -2)
{
if (!g_Game)
return;
Mission mission = g_Game.GetMission();
if (!mission || !mission.m_ActiveRefresherLocations)
return;
if (idx == -2)
{
idx = mission.m_ActiveRefresherLocations.Find(GetPosition());
}
if (idx > -1)
{
mission.m_ActiveRefresherLocations.Remove(idx);
}
}
override void OnVariablesSynchronized()
{
super.OnVariablesSynchronized();
if ( m_RefresherActive != m_RefresherActiveLocal )
{
HandleRefreshers();
m_RefresherActiveLocal = m_RefresherActive;
}
}
//--- 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 );
//update visuals (server)
UpdateVisuals();
}
override void OnPartDismantledServer( notnull Man player, string part_name, int action_id )
{
ConstructionPart constrution_part = GetConstruction().GetConstructionPart( part_name );
super.OnPartDismantledServer( player, part_name, action_id );
//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 );
//update visuals (server)
UpdateVisuals();
}
//--- ATTACHMENT & CONDITIONS
override void EEItemDetached( EntityAI item, string slot_name )
{
super.EEItemDetached( item, slot_name );
if (g_Game.IsServer()) // reset totem state is flag is decrafted while raised
{
float state = GetAnimationPhase("flag_mast");
if (state < 1)
{
AnimateFlag(1);
SetRefreshTimer01(0);
}
}
}
override bool CanReceiveAttachment( EntityAI attachment, int slotId ) //TODO
{
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
string slot_name = InventorySlots.GetSlotName(slotId);
//flag item attachment
if ( slot_name == "Material_FPole_Flag" )
{
if ( GetConstruction().IsPartConstructed("pole") )
return true;
else
return false;
}
//materials
ConstructionPart part;
ConstructionPart part_lowest;
int part_id = -1;
for (int i = 0; i < GetConstruction().GetConstructionParts().Count(); i++)
{
part = GetConstruction().GetConstructionParts().GetElement(i);
if (!part.IsBuilt())
{
if (part_id == -1 || part_id > part.GetId())
{
part_lowest = part;
part_id = part.GetId();
}
}
}
if (part_lowest /*&& !part_lowest.IsBuilt()*/)
{
string cfg_path = "cfgVehicles " + GetType() + " Construction " + part_lowest.GetMainPartName() + " " + part_lowest.GetPartName() + " Materials";
string material_path;
if ( g_Game.ConfigIsExisting(cfg_path) )
{
string child_name;
string child_slot_name;
for ( i = 0; i < g_Game.ConfigGetChildrenCount( cfg_path ); i++ )
{
g_Game.ConfigGetChildName( cfg_path, i, child_name );
material_path = "" + cfg_path + " " + child_name + " slot_name";
if ( g_Game.ConfigGetText(material_path,child_slot_name) && child_slot_name == slot_name )
{
return true;
}
}
}
}
return false;
}
//hands
override bool CanPutIntoHands( EntityAI parent )
{
return false;
}
override bool CanBeRepairedToPristine()
{
return true;
}
//--- ACTION CONDITIONS
override bool IsPlayerInside( PlayerBase player, string selection )
{
return true; //TODO
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 )
{
return true; //TODO
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 )
{
return false; //TODO
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 )
{
//TODO
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;
}
//================================================================
// SOUNDS
//================================================================
//TODO
override void SetActions()
{
super.SetActions();
AddAction(ActionRaiseFlag);
AddAction(ActionLowerFlag);
AddAction(ActionFoldBaseBuildingObject);
}
/*override void UpdateVisuals()
{
super.UpdateVisuals();
}*/
//================================================================
// FLAG MANIPULATION + REFRESHER
//================================================================
void AnimateFlagEx(float delta, PlayerBase player = null)
{
float temp = Math.Clamp( delta,0,1 );
float phase_new;
if (temp < 0.01 || temp > 0.99)
{
phase_new = Math.Round(temp);
}
else
phase_new = temp;
SetAnimationPhase("flag_mast",phase_new);
if (player)
LogAnimateFlag(phase_new, player);
GetInventory().SetSlotLock(InventorySlots.GetSlotIdFromString("Material_FPole_Flag"),phase_new != 1);
}
void AnimateFlag(float delta)
{
AnimateFlagEx(delta);
}
protected void LogAnimateFlag(float newPhase, notnull PlayerBase player)
{
PluginAdminLog logs = PluginAdminLog.Cast(GetPlugin(PluginAdminLog));
if (newPhase == 0)
{
// at the top(it's inverted)
logs.TotemFlagChange(true, player, this);
}
else if (newPhase == 1)
{
// at the bottom(it's inverted)
logs.TotemFlagChange(false, player, this);
}
}
void SetRefreshTimer01(float fraction)
{
float temp = Math.Clamp(m_FlagRefresherMaxDuration * fraction, 0, m_FlagRefresherMaxDuration);
m_RefresherTimeRemaining = Math.Round(temp);
}
/*void AddRefresherTimeFlat(float seconds) //seconds?
{
float temp = Math.Clamp(m_RefresherTimeRemaining + seconds, 0, m_FlagRefresherMaxDuration);
m_RefresherTimeRemaining = Math.Round(temp);
SetRefresherActive(m_RefresherTimeRemaining > 0);
}*/
void AddRefresherTime01(float fraction)
{
float temp = Math.Clamp(m_RefresherTimeRemaining + (fraction * m_FlagRefresherMaxDuration), 0, m_FlagRefresherMaxDuration);
m_RefresherTimeRemaining = Math.Round(temp);
SetRefresherActive(m_RefresherTimeRemaining > 0);
//Print(m_RefresherTimeRemaining);
}
float GetRefresherTime01()
{
return m_RefresherTimeRemaining / m_FlagRefresherMaxDuration;
}
void CheckLoadedVariables(int max_duration)
{
if (max_duration != m_FlagRefresherMaxDuration)
{
m_RefresherTimeRemaining = m_FlagRefresherMaxDuration * m_RefresherTimeRemaining / max_duration;
}
}
//================================================================
// DEBUG
//================================================================
/*
override void DebugCustomState()
{
//debug
m_SyncParts01 = 881; //full fence with gate
m_HasHinges = true;
m_HasBase = true;
OnVariablesSynchronized();
}
*/
//Debug menu Spawn Ground Special
override void OnDebugSpawn()
{
super.OnDebugSpawn();
GetInventory().CreateInInventory("Flag_DayZ");
AnimateFlag(0);
AddRefresherTime01(1);
}
}