3 [
Attribute(
"{59A6F1EBC6C64F79}Prefabs/Logic/SeizingTrigger.et", UIWidgets.ResourceNamePicker,
"",
"et")]
4 protected ResourceName m_sTriggerPrefab;
8 ResourceName GetTriggerPrefab()
21 protected int m_iRadius;
23 [
Attribute(
"7",
desc:
"Units in a vehicle (most probably aircraft) above this altitude will be ignored.")]
24 protected int m_iMaximumAltitude;
27 protected float m_fMaximumSeizingTime;
30 protected float m_fMinimumSeizingTime;
32 [
Attribute(
"5",
"How many characters need to be seizing at once to achieve the minimum seizing time.")]
33 protected int m_iMaximumSeizingCharacters;
35 [
Attribute(
"0",
desc:
"How long after respawn is player able to start seizing or defending.")]
36 protected float m_fRespawnCooldownPeriod;
38 [
Attribute(
"0",
desc:
"When checked, the seizing timer will decrease gradually when the seizing is interrupted.")]
39 protected bool m_bGradualTimerReset;
41 [
Attribute(
"0",
desc:
"Allow seizing for playable factions only.")]
42 protected bool m_bIgnoreNonPlayableAttackers;
44 [
Attribute(
"0",
desc:
"Allow defending for playable factions only.")]
45 protected bool m_bIgnoreNonPlayableDefenders;
48 protected bool m_bShowNotifications;
56 [
RplProp(onRplName:
"OnSeizingTimestampChanged")]
57 protected WorldTimestamp m_fSeizingStartTimestamp;
59 [
RplProp(onRplName:
"OnSeizingTimestampChanged")]
60 protected WorldTimestamp m_fSeizingEndTimestamp;
62 protected static const float TRIGGER_CHECK_PERIOD_IDLE = 3;
63 protected static const float TRIGGER_CHECK_PERIOD_ACTIVE = 1;
65 protected ref ScriptInvoker m_OnCaptureStart;
66 protected ref ScriptInvoker m_OnCaptureInterrupt;
67 protected ref ScriptInvoker m_OnCaptureFinish;
70 protected WorldTimestamp m_fInterruptedCaptureTimestamp;
71 protected float m_fCurrentSeizingTime;
72 protected float m_fInterruptedCaptureDuration;
75 protected BaseGameTriggerEntity m_Trigger;
76 protected bool m_bQueryFinished =
true;
77 protected bool m_bEnabled =
true;
78 protected RplComponent m_RplComponent;
79 protected bool m_bCharacterPresent;
81 protected int m_iSeizingCharacters;
82 protected ref map<int, WorldTimestamp> m_mSpawnTimers =
new map<int, WorldTimestamp>();
83 protected bool m_bDeleteDisabledAIs =
false;
86 protected bool IsProxy()
101 void AllowNotifications(
bool allow)
103 m_bShowNotifications = allow;
109 bool NotificationsAllowed()
111 return m_bShowNotifications;
116 ScriptInvoker GetOnCaptureStart()
118 if (!m_OnCaptureStart)
119 m_OnCaptureStart =
new ScriptInvoker();
121 return m_OnCaptureStart;
126 ScriptInvoker GetOnCaptureInterrupt()
128 if (!m_OnCaptureInterrupt)
129 m_OnCaptureInterrupt =
new ScriptInvoker();
131 return m_OnCaptureInterrupt;
136 ScriptInvoker GetOnCaptureFinish()
138 if (!m_OnCaptureFinish)
139 m_OnCaptureFinish =
new ScriptInvoker();
141 return m_OnCaptureFinish;
148 if (!m_OnTimerChange)
151 return m_OnTimerChange;
155 protected void EvaluatePrevailingFaction()
161 if (m_bCharacterPresent)
162 delay = Math.RandomFloatInclusive(TRIGGER_CHECK_PERIOD_ACTIVE, TRIGGER_CHECK_PERIOD_ACTIVE + (TRIGGER_CHECK_PERIOD_ACTIVE * 0.2));
164 delay = Math.RandomFloatInclusive(TRIGGER_CHECK_PERIOD_IDLE, TRIGGER_CHECK_PERIOD_IDLE + (TRIGGER_CHECK_PERIOD_IDLE * 0.2));
166 GetGame().GetCallqueue().CallLater(EvaluatePrevailingFaction, delay * 1000);
168 if (!m_bQueryFinished)
171 m_Trigger.GetOnQueryFinished().Insert(OnQueryFinished);
172 m_Trigger.QueryEntitiesInside();
173 m_bQueryFinished =
false;
177 protected void OnQueryFinished(BaseGameTriggerEntity trigger)
179 m_bQueryFinished =
true;
181 array<IEntity> presentEntities = {};
182 int presentEntitiesCnt = m_Trigger.GetEntitiesInside(presentEntities);
183 m_bCharacterPresent = presentEntitiesCnt != 0;
186 if (!m_bCharacterPresent)
188 if (m_PrevailingFaction)
190 m_PrevailingFactionPrevious = m_PrevailingFaction;
191 m_PrevailingFaction =
null;
192 OnPrevailingFactionChanged();
198 map<SCR_Faction, int> factionsPresence =
new map<SCR_Faction, int>();
204 for (
int i = 0; i < presentEntitiesCnt; i++)
206 IEntity entity = presentEntities[i];
208 if (m_bDeleteDisabledAIs && IsDisabledAI(entity))
210 RplComponent.DeleteRplEntity(entity,
false);
214 evaluatedEntityFaction = EvaluateEntityFaction(presentEntities[i]);
216 if (!evaluatedEntityFaction)
219 factionCnt = factionsPresence.Get(evaluatedEntityFaction);
223 factionsPresence.Insert(evaluatedEntityFaction, 1);
225 factionsPresence.Set(evaluatedEntityFaction, factionCnt + 1);
227 m_bDeleteDisabledAIs =
false;
229 int highestAttackingPresence;
230 int highestDefendingPresence;
231 int curSeizingCharacters;
235 for (
int i = 0, cnt = factionsPresence.Count(); i < cnt; i++)
238 if (m_bIgnoreNonPlayableAttackers && !factionsPresence.GetKey(i).IsPlayable())
241 presence = factionsPresence.GetElement(i);
243 if (presence > highestAttackingPresence)
245 highestAttackingPresence = presence;
246 prevailingFaction = factionsPresence.GetKey(i);
248 else if (presence == highestAttackingPresence)
250 prevailingFaction =
null;
255 if (prevailingFaction)
257 for (
int i = 0, cnt = factionsPresence.Count(); i < cnt; i++)
260 if (m_bIgnoreNonPlayableDefenders && !factionsPresence.GetKey(i).IsPlayable())
264 if (factionsPresence.GetKey(i) == prevailingFaction)
267 highestDefendingPresence = Math.Max(factionsPresence.GetElement(i), highestDefendingPresence);
271 if (prevailingFaction && highestAttackingPresence > highestDefendingPresence)
273 curSeizingCharacters = Math.Min(highestAttackingPresence - highestDefendingPresence, m_iMaximumSeizingCharacters);
277 prevailingFaction =
null;
278 curSeizingCharacters = 0;
282 if (prevailingFaction != m_PrevailingFaction)
284 m_iSeizingCharacters = curSeizingCharacters;
285 m_PrevailingFactionPrevious = m_PrevailingFaction;
286 m_PrevailingFaction = prevailingFaction;
287 OnPrevailingFactionChanged();
289 else if (prevailingFaction && curSeizingCharacters != m_iSeizingCharacters)
291 m_iSeizingCharacters = curSeizingCharacters;
292 RefreshSeizingTimer();
297 protected SCR_Faction EvaluateEntityFaction(IEntity ent)
299 SCR_ChimeraCharacter
char = SCR_ChimeraCharacter.Cast(ent);
306 CharacterControllerComponent charControl =
char.GetCharacterController();
311 int playerId =
GetGame().GetPlayerManager().GetPlayerIdFromControlledEntity(ent);
314 AIControlComponent ctrComp = charControl.GetAIControlComponent();
317 AIAgent ai = ctrComp.GetAIAgent();
318 if (ai && ai.GetLOD() == AIAgent.GetMaxLOD())
326 if (m_fRespawnCooldownPeriod > 0)
329 if (playerId != 0 && m_mSpawnTimers.Contains(playerId))
332 if (m_mSpawnTimers.Get(playerId).Greater(world.GetServerTimestamp()))
335 m_mSpawnTimers.Remove(playerId)
341 return entityFaction;
345 protected void OnPrevailingFactionChanged()
347 if (!m_PrevailingFaction ||
m_FactionControl.GetAffiliatedFaction() == m_PrevailingFaction)
349 if (m_fSeizingEndTimestamp != 0)
352 m_fInterruptedCaptureTimestamp = world.GetServerTimestamp();
353 m_fInterruptedCaptureDuration = m_fInterruptedCaptureTimestamp.DiffMilliseconds(m_fSeizingStartTimestamp);
354 m_fSeizingEndTimestamp =
null;
355 m_fSeizingStartTimestamp =
null;
356 int factionIndex =
GetGame().GetFactionManager().GetFactionIndex(m_PrevailingFactionPrevious);
357 Rpc(RpcDo_OnCaptureInterrupt, factionIndex);
358 RpcDo_OnCaptureInterrupt(factionIndex);
364 m_fSeizingStartTimestamp = world.GetServerTimestamp();
365 RefreshSeizingTimer();
366 int factionIndex =
GetGame().GetFactionManager().GetFactionIndex(m_PrevailingFaction);
367 Rpc(RpcDo_OnCaptureStart, factionIndex);
368 RpcDo_OnCaptureStart(factionIndex);
371 OnSeizingTimestampChanged();
372 Replication.BumpMe();
376 protected void OnSeizingTimestampChanged()
379 m_OnTimerChange.Invoke(m_fSeizingStartTimestamp, m_fSeizingEndTimestamp);
385 if (m_fSeizingStartTimestamp == 0 && m_fSeizingEndTimestamp == 0)
386 ClearEventMask(
GetOwner(), EntityEvent.FRAME);
388 SetEventMask(
GetOwner(), EntityEvent.FRAME);
393 void RefreshSeizingTimer()
395 float seizingTimeVar = m_fMaximumSeizingTime - m_fMinimumSeizingTime;
398 if (m_iMaximumSeizingCharacters > 1)
400 float deductPerPlayer = seizingTimeVar / (m_iMaximumSeizingCharacters - 1);
401 deduct = deductPerPlayer * (m_iSeizingCharacters - 1);
404 m_fSeizingEndTimestamp = m_fSeizingStartTimestamp.PlusSeconds(m_fMaximumSeizingTime - deduct);
406 if (m_bGradualTimerReset && m_fInterruptedCaptureDuration != 0)
407 HandleGradualReset();
409 Replication.BumpMe();
410 OnSeizingTimestampChanged();
415 protected void HandleGradualReset()
417 float timeSinceInterrupt = m_fSeizingStartTimestamp.DiffMilliseconds(m_fInterruptedCaptureTimestamp);
419 if (timeSinceInterrupt < m_fInterruptedCaptureDuration)
421 float diff = m_fInterruptedCaptureDuration - timeSinceInterrupt;
422 m_fSeizingStartTimestamp = m_fSeizingStartTimestamp.PlusMilliseconds(-diff);
423 m_fSeizingEndTimestamp = m_fSeizingEndTimestamp.PlusMilliseconds(-diff);
426 m_fInterruptedCaptureDuration = 0;
427 m_fInterruptedCaptureTimestamp =
null;
432 WorldTimestamp GetSeizingStartTimestamp()
434 return m_fSeizingStartTimestamp;
439 WorldTimestamp GetSeizingEndTimestamp()
441 return m_fSeizingEndTimestamp;
445 [
RplRpc(RplChannel.Reliable, RplRcver.Broadcast)]
446 protected void RpcDo_OnCaptureStart(
int factionIndex)
448 FactionManager factionManager =
GetGame().GetFactionManager();
458 if (m_OnCaptureStart)
459 m_OnCaptureStart.Invoke(faction,
this);
463 [
RplRpc(RplChannel.Reliable, RplRcver.Broadcast)]
464 protected void RpcDo_OnCaptureInterrupt(
int factionIndex)
466 FactionManager factionManager =
GetGame().GetFactionManager();
476 if (m_OnCaptureInterrupt)
477 m_OnCaptureInterrupt.Invoke(faction,
this);
481 [
RplRpc(RplChannel.Reliable, RplRcver.Broadcast)]
482 protected void RpcDo_OnCaptureFinish(
int factionIndex)
484 m_bDeleteDisabledAIs =
true;
485 FactionManager factionManager =
GetGame().GetFactionManager();
495 if (m_OnCaptureFinish)
496 m_OnCaptureFinish.Invoke(faction,
this);
498 UpdateFlagsInHierarchy(faction);
500 if (m_bShowNotifications)
501 NotifyPlayerInRadius(faction);
511 protected void OnPlayerSpawned(
int playerId, IEntity controlledEntity)
513 if (!controlledEntity)
517 if (vector.DistanceSqXZ(controlledEntity.GetOrigin(),
GetOwner().
GetOrigin()) > (m_iRadius * m_iRadius))
521 m_mSpawnTimers.Set(playerId, world.GetServerTimestamp().PlusSeconds(m_fRespawnCooldownPeriod));
525 protected void UpdateFlagsInHierarchy(notnull
SCR_Faction faction)
527 array<IEntity> queue = {
GetOwner()};
528 SCR_FlagComponent flag;
529 IEntity processedEntity;
530 IEntity nextInHierarchy;
532 while (!queue.IsEmpty())
534 processedEntity = queue[0];
537 flag = SCR_FlagComponent.Cast(processedEntity.FindComponent(SCR_FlagComponent));
539 flag.ChangeMaterial(faction.GetFactionFlagMaterial());
541 nextInHierarchy = processedEntity.GetChildren();
543 while (nextInHierarchy)
545 queue.Insert(nextInHierarchy);
546 nextInHierarchy = nextInHierarchy.GetSibling();
552 protected bool GetIsLocalPlayerPresent()
558 return (vector.DistanceSqXZ(player.GetOrigin(),
GetOwner().
GetOrigin()) <= (m_iRadius * m_iRadius));
562 protected void NotifyPlayerInRadius(notnull
SCR_Faction faction)
568 if (!GetIsLocalPlayerPresent())
571 if (playerFaction == faction)
572 SCR_NotificationsComponent.SendLocal(m_eCapturedByFriendliesNotification);
574 SCR_NotificationsComponent.SendLocal(m_eCapturedByEnemiesNotification);
588 override void OnBaseFactionChanged(
Faction faction)
590 super.OnBaseFactionChanged(faction);
591 m_PrevailingFaction =
null;
594 bool IsDisabledAI(IEntity ent)
597 SCR_ChimeraCharacter
char = SCR_ChimeraCharacter.Cast(ent);
601 CharacterControllerComponent charControl =
char.GetCharacterController();
608 int playerId =
GetGame().GetPlayerManager().GetPlayerIdFromControlledEntity(ent);
612 AIControlComponent ctrComp = charControl.GetAIControlComponent();
616 if (ctrComp.IsAIActivated())
630 ClearEventMask(
GetOwner(), EntityEvent.FRAME);
632 foreach (SCR_MilitaryBaseComponent base :
m_aBases)
637 base.UnregisterLogicComponent(
this);
643 gameMode.GetOnPlayerSpawned().Remove(OnPlayerSpawned);
645 GetGame().GetCallqueue().Remove(EvaluatePrevailingFaction);
649 override void OnPostInit(IEntity owner)
651 super.OnPostInit(owner);
653 m_RplComponent = RplComponent.Cast(owner.FindComponent(RplComponent));
665 Print(
"SCR_SeizingComponent: Invalid area radius (" + m_iRadius +
")! Terminating...", LogLevel.ERROR);
669 if (m_fMaximumSeizingTime < 0 || m_fMinimumSeizingTime < 0 || m_fMaximumSeizingTime < m_fMinimumSeizingTime)
671 Print(
"SCR_SeizingComponent: Invalid seizing time setting (" + m_fMinimumSeizingTime +
", " + m_fMaximumSeizingTime +
")! Terminating...", LogLevel.ERROR);
675 if (m_iMaximumSeizingCharacters <= 0)
677 Print(
"SCR_SeizingComponent: Invalid maximum seizing characters (" + m_iMaximumSeizingCharacters +
")! Terminating...", LogLevel.ERROR);
691 Print(
"SCR_SeizingComponent: Owner is missing SCR_FactionAffiliationComponent! Terminating...", LogLevel.ERROR);
695 Resource triggerResource = Resource.Load(componentData.GetTriggerPrefab());
696 if (!triggerResource)
698 Print(
"SCR_SeizingComponent: Trigger resource failed to load! Terminating...", LogLevel.ERROR);
703 m_Trigger = BaseGameTriggerEntity.Cast(
GetGame().SpawnEntityPrefabLocal(triggerResource,
GetGame().GetWorld()));
706 Print(
"SCR_SeizingComponent: Trigger failed to spawn! Terminating...", LogLevel.ERROR);
710 m_Trigger.SetSphereRadius(m_iRadius);
711 owner.AddChild(m_Trigger, -1);
714 if (m_fRespawnCooldownPeriod > 0)
719 gameMode.GetOnPlayerSpawned().Insert(OnPlayerSpawned);
722 GetGame().GetCallqueue().CallLater(EvaluatePrevailingFaction, Math.RandomFloatInclusive(TRIGGER_CHECK_PERIOD_IDLE, TRIGGER_CHECK_PERIOD_IDLE + (TRIGGER_CHECK_PERIOD_IDLE * 0.2)) * 1000);
726 override void EOnFrame(IEntity owner,
float timeSlice)
728 if (m_fSeizingEndTimestamp == 0)
732 if (world.GetServerTimestamp().Less(m_fSeizingEndTimestamp))
735 m_fSeizingEndTimestamp =
null;
736 m_fSeizingStartTimestamp =
null;
737 OnSeizingTimestampChanged();
739 Replication.BumpMe();
743 int factionIndex =
GetGame().GetFactionManager().GetFactionIndex(m_PrevailingFaction);
744 Rpc(RpcDo_OnCaptureFinish, factionIndex);
745 RpcDo_OnCaptureFinish(factionIndex);
749 int factionIndex =
GetGame().GetFactionManager().GetFactionIndex(m_PrevailingFactionPrevious);
750 Rpc(RpcDo_OnCaptureInterrupt, factionIndex);
751 RpcDo_OnCaptureInterrupt(factionIndex);
764 gameMode.GetOnPlayerSpawned().Remove(OnPlayerSpawned);
766 GetGame().GetCallqueue().Remove(EvaluatePrevailingFaction);