Arma Reforger Explorer 1.7.0.54
Arma Reforger Code Explorer by Zeroy - Thanks to MisterOutofTime
Loading...
Searching...
No Matches
SCR_BaseGameMode.c
Go to the documentation of this file.
1#ifdef GMSTATS
2#define GM_AI_STATS
3#endif
4#ifdef AISTATS
5#define GM_AI_STATS
6#endif
7
15
17//------------------------------------------------------------------------------------------------
18/*
19 SCR_BaseGameMode
20
21Brief:
22 SCR_BaseGameMode is the skeleton of all game modes which connects some of the critical aspects of spawning.
23
24Important:
25 A. Components and expansion:
26 1. SCR_BaseGameMode automatically gathers all attached components of SCR_BaseGameModeComponent type.
27 These can be used to receive events from the game mode and expand game mode functionality in modular blocks.
28
29 2. SCR_RespawnSystemComponent is a component that allows to store and select player faction, loadout and spawn points
30 This system is closely tied to game modes and also requires FactionManager or LoadoutManager in the world.
31
32 3. SCR_RespawnHandlerComponent is a component that provides a game mode with a way of respawning.
33 These can be either very generic (automatic, menu selection) and interchangable or completely game mode specific.
34
35 4. Additional components like
36 SCR_RespawnTimerComponent: That allows to add a timer between each respawn
37 SCR_ScoringSystemComponent: That allows handling of score when a player kills or is killed
38
39
40 If you want to make a system which only requires to hook on game mode events, SCR_BaseGameMode is most likely the way to go.
41 Inherit the component and implement methods which you require. If you need to have a very game specific implementation that
42 cannot work standalone you will have to inherit SCR_BaseGameMode and plug the communication yourself.
43
44
45 B. Instances and logic:
46 1. SCR_BaseGameMode is a unique entity in the world, there can currently be only one game mode.
47 2. SCR_RespawnSystemComponent is a unique component attached to the game mode, there can currently be only one respawn system.
48 3. SCR_RespawnHandlerComponent is a unique component attached to the game mode, there can currently be only one respawn handler.
49 4. SCR_RespawnComponent is a component attached to individual PlayerController instances.
50 Due to the nature of PlayerController, only the server can access all RespawnComponent(s).
51 The local client will always be only to access their local RespawnComponent.
52
53 For the local component you can use GetGame().GetPlayerController().GetRespawnComponent();
54 For other clients as the authority you can use GetGame().GetPlayerManager().GetPlayerRespawnComponent(int playerId);
55
56 This component serves as communication between the authority and client for the SCR_RespawnSystemComponent.
57 If you need to request a faction, loadout or spawn point selection as the client, navigate through the component.
58
59 If you need to SET a faction, loadout or spawn point as the AUTHORITY, use SCR_RespawnSystemComponent's
60 DoSetPlayerFaction, DoSetPlayerLoadout and DoSetPlayerSpawnPoint method(s).
61
62 If you need to convert object instances to their id/indices, you can use SCR_RespawnSystemComponent's
63 GetFactionIndex, GetLoadoutIndex, GetSpawnPointIndex method(s) and/or
64 GetFactionByIndex, GetLoadoutByIndex, GetSpawnPointByIdentity.
65
66
67 C. Game State
68 There are three game states in total that you can use, two of which are optional.
69 1. Pre-game:
70 This state is automatically skipped unless a
71 SCR_BaseGameModeStateComponent that has SCR_EGameModeState.PREGAME as its affiliated state
72 is attached to the game mode.
73
74 The SCR_PreGameGameModeStateComponent can be used to allow a time-based pre-game duration.
75
76 2. Game
77 This is the core game loop.
78
79 By default this state is infinite, unless a
80 SCR_BaseGameModeStateComponent that has SCR_EGameModeState.GAME as its affiliated state
81 is attached to the game mode with a duration set.
82
83 The SCR_GameGameModeStateComponent can be used to allow a time-based pre-game duration.
84
85 3. Post-game
86 This is your game-over state, you can automatically transition into new world, restart the session,
87 have player voting, display scoreboard or end-game screens, as desired.
88
89 In addition this transition carries along SCR_GameModeEndData, providing the authority and all
90 clients with additional data that can notify the gamemode about e.g. win condition, or end reason
91 as implemented per game mode.
92
93 This state can also have its logic expanded similarly to the Pre-game by using
94 SCR_BaseGameModeStateComponent that has SCR_EGameModeState.POSTGAME as its affiliated state
95 attached to the game mode.
96
97 The SCR_PostGameGameModeStateComponent can be used for this.
98
99
100 You can always retrieve current game state by calling GetState() (see SCR_GameModeState)
101 or IsRunning() if you're interested in the core game loop only.
102
103 On each state change SCR_BaseGameMode.OnGameStateChanged() is called on both the server and all clients.
104 In additionaly as implemented in this class it will automatically call OnGameModeStart() and OnGameModeEnd()
105 based on the state the game is transitioning into.
106*/
107
108//------------------------------------------------------------------------------------------------
109//~ ScriptInvokers
110void SCR_BaseGameMode_OnPlayerDisconnected(int playerId, KickCauseCode cause = KickCauseCode.NONE, int timeout = -1);
112
113//~ Player or controllable entity was killed/destroyed
116
117//~ Generic Event that sends over player ID
118void SCR_BaseGameMode_PlayerId(int playerId);
120
121//~ Generic event that sends over player ID and Entity
124
125//~ On Player role changed
128
129void SCR_BaseGameMode_OnResourceEnabledChanged(array<EResourceType> disabledResourceTypes);
131
132void OnPreloadFinished();
134typedef ScriptInvokerBase<OnPreloadFinished> OnPreloadFinishedInvoker;
135
136
137//------------------------------------------------------------------------------------------------
138class SCR_BaseGameMode : BaseGameMode
139{
140 #ifdef ENABLE_DIAG
141 #define GAME_MODE_DEBUG
142 #endif
143
144 #ifdef GAME_MODE_DEBUG
145 static bool s_DebugRegistered = false;
146 #endif
147
148 const static string WB_GAME_MODE_CATEGORY = "Game Mode";
149
152
153 //~ Player events
154 protected ref ScriptInvokerBase<SCR_BaseGameMode_PlayerId> m_OnPlayerAuditSuccess = new ScriptInvokerBase<SCR_BaseGameMode_PlayerId>();
155 protected ref ScriptInvokerBase<SCR_BaseGameMode_PlayerId> m_OnPlayerAuditFail = new ScriptInvokerBase<SCR_BaseGameMode_PlayerId>();
156 protected ref ScriptInvokerBase<SCR_BaseGameMode_PlayerId> m_OnPlayerAuditTimeouted = new ScriptInvokerBase<SCR_BaseGameMode_PlayerId>();
157 protected ref ScriptInvokerBase<SCR_BaseGameMode_PlayerId> m_OnPlayerAuditRevived = new ScriptInvokerBase<SCR_BaseGameMode_PlayerId>();
158 protected ref ScriptInvokerBase<SCR_BaseGameMode_PlayerId> m_OnPlayerConnected = new ScriptInvokerBase<SCR_BaseGameMode_PlayerId>();
159 protected ref ScriptInvokerBase<SCR_BaseGameMode_PlayerId> m_OnPlayerRegistered = new ScriptInvokerBase<SCR_BaseGameMode_PlayerId>();
160 protected ref ScriptInvokerBase<SCR_BaseGameMode_OnPlayerDisconnected> m_OnPlayerDisconnected = new ScriptInvokerBase<SCR_BaseGameMode_OnPlayerDisconnected>();
161 protected ref ScriptInvokerBase<SCR_BaseGameMode_OnPlayerDisconnected> m_OnPostCompPlayerDisconnected = new ScriptInvokerBase<SCR_BaseGameMode_OnPlayerDisconnected>(); //~ Called after the GameMode Components are notified that a player was disconnected
162 protected ref ScriptInvokerBase<SCR_BaseGameMode_PlayerIdAndEntity> m_OnPlayerSpawned = new ScriptInvokerBase<SCR_BaseGameMode_PlayerIdAndEntity>();
163 protected ref ScriptInvokerBase<SCR_BaseGameMode_OnControllableDestroyed> m_OnPlayerKilled = new ScriptInvokerBase<SCR_BaseGameMode_OnControllableDestroyed>();
164 protected ref ScriptInvokerBase<SCR_BaseGameMode_OnControllableDestroyed> m_OnControllableDestroyed = new ScriptInvokerBase<SCR_BaseGameMode_OnControllableDestroyed>();
165 protected ref ScriptInvokerBase<SCR_BaseGameMode_PlayerIdAndEntity> m_OnPlayerDeleted = new ScriptInvokerBase<SCR_BaseGameMode_PlayerIdAndEntity>();
166 protected ref ScriptInvokerBase<SCR_BaseGameMode_OnPlayerRoleChanged> m_OnPlayerRoleChange = new ScriptInvokerBase<SCR_BaseGameMode_OnPlayerRoleChanged>();
167
172
173 protected ref ScriptInvokerBase<SCR_BaseGameMode_OnResourceEnabledChanged> m_OnResourceTypeEnabledChanged;
174
176
177 //------------------------------------------------------------------------------------------------
178 //
179 // World Editor attributes meant for Debug or Testing
180 //
181 [Attribute("0", uiwidget: UIWidgets.Flags, "Test Game Flags for when you run mission via WE.", "", ParamEnumArray.FromEnum(EGameFlags), WB_GAME_MODE_CATEGORY)]
183
184 [Attribute("1", uiwidget: UIWidgets.CheckBox, "When false, then Game mode need to handle its very own spawning. If true, then simple default logic is used to spawn and respawn automatically.", category: WB_GAME_MODE_CATEGORY)]
185 protected bool m_bAutoPlayerRespawn;
186
187 [Attribute("1", uiwidget: UIWidgets.CheckBox, "When true, allows players to freely swap their faction after initial assignment.", category: WB_GAME_MODE_CATEGORY)]
188 protected bool m_bAllowFactionChange;
189
190 [Attribute("30", UIWidgets.Slider, params: "-1 600 1", desc: "Time in seconds after which the mission is reloaded upon completion or -1 to disable it.", category: WB_GAME_MODE_CATEGORY)]
191 private float m_fAutoReloadTime;
192
193 //------------------------------------------------------------------------------------------------
194 //
195 // Game End Screen States info
196 //
197
199 [RplProp(onRplName: "OnGameStateChanged")]
200 private SCR_EGameModeState m_eGameState = SCR_EGameModeState.PREGAME;
201
203 [RplProp()]
204 private ref SCR_GameModeEndData m_pGameEndData = new SCR_GameModeEndData();
205
206 [Attribute("1", uiwidget: UIWidgets.CheckBox, "If checked the elapsed time will only advance if at least one player is present on the server.", category: WB_GAME_MODE_CATEGORY)]
208
213 [RplProp(condition: RplCondition.NoOwner)]
214 protected float m_fTimeElapsed;
215
217 [RplProp()]
218 protected bool m_bAllowControls = true;
219
221 protected float m_fTimeCorrectionInterval = 10.0;
222
224 protected float m_fLastTimeCorrection;
225
227 [RplProp()]
228 protected bool m_bIsHosted;
229
230 //------------------------------------------------------------------------------------------------
231 //
232 // Required components
233 //
234 protected RplComponent m_RplComponent;
235 protected SCR_GameModeHealthSettings m_pGameModeHealthSettings;
236 protected SCR_RespawnSystemComponent m_pRespawnSystemComponent;
239
243 protected ref array<SCR_BaseGameModeComponent> m_aAdditionalGamemodeComponents = new array<SCR_BaseGameModeComponent>();
244
247
250
252 [Attribute("1", category: WB_GAME_MODE_CATEGORY)]
253 protected bool m_bUseSpawnPreload;
254
256 [Attribute("0", desc: "Is the autotune radio feature enabled in this gamemode")]
257 protected bool m_bAutotuneEnabled;
258
261
262 //~ Any Resource types that is set here is a disabled resource type
263 [Attribute(desc: "List of disabled Resource Types in the GameMode.", uiwidget: UIWidgets.SearchComboBox, enums: ParamEnumArray.FromEnum(EResourceType), category: "Game Mode"), RplProp(onRplName: "OnResourceTypeEnabledChanged")]
264 protected ref array<EResourceType> m_aDisabledResourceTypes;
265
266 //~ Time stamp when end game was called
268
269 //------------------------------------------------------------------------------------------------
272 {
273 return !m_aDisabledResourceTypes.Contains(resourceType);
274 }
275
276 //------------------------------------------------------------------------------------------------
279 int GetDisabledResourceTypes(inout notnull array<EResourceType> disabledResourceTypes)
280 {
281 disabledResourceTypes.Copy(m_aDisabledResourceTypes);
282 return disabledResourceTypes.Count();
283 }
284
285 //------------------------------------------------------------------------------------------------
290 void SetResourceTypeEnabled(bool enable, EResourceType resourceType = EResourceType.SUPPLIES, int playerID = -1)
291 {
292 int index = m_aDisabledResourceTypes.Find(resourceType);
293
294 //~ Already Enabled/Disabled
295 if ((index < 0) == enable)
296 return;
297
298 if (!enable)
299 m_aDisabledResourceTypes.Insert(resourceType);
300 else
302
303 Replication.BumpMe();
304
305 //~ Call on resourceType changed for server as well
306 if (IsMaster())
308
309 if (resourceType == EResourceType.SUPPLIES)
310 {
311 if (enable)
312 SCR_NotificationsComponent.SendToEveryone(ENotification.EDITOR_ATTRIBUTES_ENABLE_GLOBAL_SUPPLY_USAGE, playerID);
313 else
314 SCR_NotificationsComponent.SendToEveryone(ENotification.EDITOR_ATTRIBUTES_DISABLE_GLOBAL_SUPPLY_USAGE, playerID);
315 }
316 }
317
318 //------------------------------------------------------------------------------------------------
325
326 //------------------------------------------------------------------------------------------------
328 ScriptInvokerBase<SCR_BaseGameMode_OnResourceEnabledChanged> GetOnResourceTypeEnabledChanged()
329 {
331 m_OnResourceTypeEnabledChanged = new ScriptInvokerBase<SCR_BaseGameMode_OnResourceEnabledChanged>();
332
334 }
335
336 //------------------------------------------------------------------------------------------------
338 {
339 ChimeraWorld world = GetGame().GetWorld();
340 return (!world.IsGameTimePaused() && m_bUseSpawnPreload);
341 }
342
343 //------------------------------------------------------------------------------------------------
349 {
350 return m_eGameState == SCR_EGameModeState.GAME;
351 }
352
353 //------------------------------------------------------------------------------------------------
359 {
360 return m_eGameState;
361 }
362
363 //------------------------------------------------------------------------------------------------
367 sealed bool IsMaster()
368 {
369 return (!m_RplComponent || m_RplComponent.IsMaster());
370 }
371
372 //------------------------------------------------------------------------------------------------
378 {
379 return m_fTimeElapsed;
380 }
381
382 //------------------------------------------------------------------------------------------------
388 {
389 SCR_BaseGameModeStateComponent stateComponent = GetStateComponent(SCR_EGameModeState.GAME);
390 if (stateComponent)
391 return stateComponent.GetDuration();
392
393 return 0.0;
394 }
395
396 //------------------------------------------------------------------------------------------------
402 {
403 float timeLimit = GetTimeLimit();
404 if (!IsRunning() || timeLimit <= 0)
405 return -1;
406
407 float time = timeLimit - GetElapsedTime();
408 if (time < 0)
409 time = 0;
410
411 return time;
412 }
413
414 //------------------------------------------------------------------------------------------------
418 bool IsHosted()
419 {
420 return m_bIsHosted;
421 }
422
423 //------------------------------------------------------------------------------------------------
428 protected void OnGameStateChanged()
429 {
430 SCR_EGameModeState newState = GetState();
431 Print("SCR_BaseGameMode::OnGameStateChanged = " + SCR_Enum.GetEnumName(SCR_EGameModeState, newState));
432
433 //We handle the OnGameModeEnd and OnGameModeStart events here
434 //Because this is the only replicated call.
435 //And we propagate it to the components here for robusty in case of overriding those methods
436 switch (newState)
437 {
438 case SCR_EGameModeState.POSTGAME:
439 OnGameModeEnd(m_pGameEndData);
441 {
442 component.OnGameModeEnd(m_pGameEndData);
443 }
444 break;
445
446 case SCR_EGameModeState.GAME:
449 {
450 component.OnGameModeStart();
451 }
452 break;
453 }
454
455 // Dispatch events
457 {
458 component.OnGameStateChanged(newState);
459 }
460 }
461
462 //------------------------------------------------------------------------------------------------
464 {
465 m_mPlayerSpawnPosition.Set(playerID, position);
466 }
467
468 //------------------------------------------------------------------------------------------------
473 SCR_RespawnSystemComponent GetRespawnSystemComponent()
474 {
476 }
477
478 //------------------------------------------------------------------------------------------------
479 SCR_GameModeHealthSettings GetGameModeHealthSettings()
480 {
482 }
483
484 //------------------------------------------------------------------------------------------------
493
494 //------------------------------------------------------------------------------------------------
500 {
501 if (!IsMaster())
502 return;
503
504 if (IsRunning())
505 {
506 Print("Trying to start a gamemode that is already running.", LogLevel.WARNING);
507 return;
508 }
509
510#ifdef ENABLE_DIAG
511 if (!Replication.IsRunning()) // You cannot pause in MP, thus this option is only available in SP
512 DiagMenu.RegisterBool(SCR_DebugMenuID.DEBUGUI_SINGLEPLAYER_DISABLE_TIME_PAUSE, "", "Disable time pause", "Game");
513#endif
514
515 m_fTimeElapsed = 0.0;
516 m_eGameState = SCR_EGameModeState.GAME;
517 Replication.BumpMe();
518
519 // Raise event for authority
521 }
522
523 //------------------------------------------------------------------------------------------------
533 protected bool CanStartGameMode()
534 {
535 SCR_BaseGameModeStateComponent pregame = GetStateComponent(SCR_EGameModeState.PREGAME);
536 if (!pregame)
537 return true;
538
539 return pregame.CanAdvanceState(SCR_EGameModeState.GAME);
540 }
541
542 //------------------------------------------------------------------------------------------------
551 {
552 if (!IsMaster())
553 return;
554
555 // Already over
556 if (!IsRunning())
557 {
558 Print("Trying to end a gamemode that is not running.", LogLevel.WARNING);
559 return;
560 }
561
562 if (endData == null)
563 endData = SCR_GameModeEndData.CreateSimple();
564
565 m_pGameEndData = endData;
566 m_eGameState = SCR_EGameModeState.POSTGAME;
567 Replication.BumpMe();
568
569 // Raise event for authority
571 }
572
578 {
579 if (m_eGameState != SCR_EGameModeState.POSTGAME)
580 return null;
581 else
582 return m_pGameEndData;
583 }
584
590 {
591 return m_OnGameEnd;
592 }
593 ScriptInvokerBase<SCR_BaseGameMode_PlayerId> GetOnPlayerAuditSuccess()
594 {
596 }
597 ScriptInvokerBase<SCR_BaseGameMode_PlayerId> GetOnPlayerAuditFail()
598 {
599 return m_OnPlayerAuditFail;
600 }
601 ScriptInvokerBase<SCR_BaseGameMode_PlayerId> GetOnPlayerAuditTimeouted()
602 {
604 }
605 ScriptInvokerBase<SCR_BaseGameMode_PlayerId> GetOnPlayerAuditRevived()
606 {
608 }
609 ScriptInvokerBase<SCR_BaseGameMode_PlayerId> GetOnPlayerConnected()
610 {
611 return m_OnPlayerConnected;
612 }
613 ScriptInvokerBase<SCR_BaseGameMode_PlayerId> GetOnPlayerRegistered()
614 {
616 }
617 ScriptInvokerBase<SCR_BaseGameMode_OnPlayerDisconnected> GetOnPlayerDisconnected()
618 {
620 }
621
625 ScriptInvokerBase<SCR_BaseGameMode_OnPlayerDisconnected> GetOnPostCompPlayerDisconnected()
626 {
628 }
629 ScriptInvokerBase<SCR_BaseGameMode_PlayerIdAndEntity> GetOnPlayerSpawned()
630 {
631 return m_OnPlayerSpawned;
632 }
633 ScriptInvokerBase<SCR_BaseGameMode_OnControllableDestroyed> GetOnPlayerKilled()
634 {
635 return m_OnPlayerKilled;
636 }
637 ScriptInvokerBase<SCR_BaseGameMode_PlayerIdAndEntity> GetOnPlayerDeleted()
638 {
639 return m_OnPlayerDeleted;
640 }
641 ScriptInvokerBase<SCR_BaseGameMode_OnPlayerRoleChanged> GetOnPlayerRoleChange()
642 {
644 }
653 ScriptInvokerBase<SCR_BaseGameMode_OnControllableDestroyed> GetOnControllableDestroyed()
654 {
656 }
661
669
670 //------------------------------------------------------------------------------------------------
676 protected void OnGameModeStart()
677 {
678 #ifdef GM_AI_STATS
679 if (IsGameModeStatisticsEnabled())
680 {
681 if (!SCR_GameModeStatistics.IsRecording())
682 SCR_GameModeStatistics.StartRecording();
683 }
684 #endif
685 }
686
687 //------------------------------------------------------------------------------------------------
694 protected void OnGameModeEnd(SCR_GameModeEndData endData)
695 {
697
698 ChimeraWorld world = GetGame().GetWorld();
699 if (world)
700 m_GameEndTimeStamp = world.GetLocalTimestamp();
701
702 m_OnGameModeEnd.Invoke(endData);
703
704 #ifdef GM_AI_STATS
705 if (IsGameModeStatisticsEnabled())
706 {
707 if (SCR_GameModeStatistics.IsRecording())
708 SCR_GameModeStatistics.StopRecording();
709 }
710 #endif
711
712 // Automatically restart the session on game mode end if enabled
713 float reloadTime = GetAutoReloadDelay();
714 if (reloadTime > 0)
715 GetGame().GetCallqueue().CallLater(RestartSession, reloadTime * 1000.0, false);
716 else if (reloadTime == 0)
718 else
720 }
721
722 //------------------------------------------------------------------------------------------------
724 {
725 if (!IsMaster())
726 return;
727
728 // After the game completes no more saving needs to be done
729 GetGame().GetSaveGameManager().SetSavingAllowed(false);
730
731 const PersistenceSystem persistence = PersistenceSystem.GetInstance();
732 if (System.IsCLIParam("keepSessionSave") || (persistence && persistence.ShouldKeepSessionData()))
733 return;
734
735 // We only delete on DS or workbench for now
736 #ifndef WORKBENCH
737 if (RplSession.Mode() != RplMode.Dedicated)
738 return;
739 #endif
740
741 if (persistence)
742 persistence.ClearStorage(PersistenceSessionStorage);
743
744 const SaveGame currentSave = GetGame().GetSaveGameManager().GetActiveSave();
745 if (currentSave)
746 GetGame().GetSaveGameManager().Purge(currentSave.GetMissionResource(), currentSave.GetPlaythroughNumber());
747 }
748
749 //------------------------------------------------------------------------------------------------
757
758 //------------------------------------------------------------------------------------------------
763 {
764 // Allow the server owner to override it via -autoReload=TIME
765 string autoReloadTimeString;
766 if (System.GetCLIParam("autoreload", autoReloadTimeString))
767 {
768 float val = -1;
769 if (!autoReloadTimeString.IsEmpty() && autoReloadTimeString != "disabled")
770 val = Math.Clamp(autoReloadTimeString.ToFloat(), -1, 600);
771
772 return val;
773 }
774
775 return m_fAutoReloadTime;
776 }
777
781 protected void RestartSession()
782 {
783 if (!IsMaster())
784 return;
785
786 if (TryShutdownServer())
787 return;
788
789 GameStateTransitions.RequestScenarioRestart();
790 }
791
792 //------------------------------------------------------------------------------------------------
793 // Attempts to shutdown the server when a CLI parameter is present
795 protected sealed bool TryShutdownServer()
796 {
797 if (!System.IsConsoleApp() || !Replication.IsServer())
798 return false;
799
800 string outValue;
801 if (System.GetCLIParam("autoshutdown", outValue))
802 {
803 GameStateTransitions.RequestGameTerminateTransition();
804 return true;
805 }
806
807 return false;
808 }
809
810 //------------------------------------------------------------------------------------------------
811 protected override void OnGameStart()
812 {
813 BackendApi backendApi = GetGame().GetBackendApi();
814
815 if (IsMaster())
816 backendApi.NewSession();
817
818 super.OnGameStart();
819 Event_OnGameStart.Invoke();
820 }
821
822 //------------------------------------------------------------------------------------------------
824 {
825 m_OnGameEnd.Invoke();
826
828 {
829 comp.OnGameEnd();
830 }
831 }
832
833 //------------------------------------------------------------------------------------------------
835 {
837 }
838
839 //------------------------------------------------------------------------------------------------
840 override void OnPlayerAuditSuccess(int iPlayerID)
841 {
842 m_OnPlayerAuditSuccess.Invoke(iPlayerID);
843
844 if (IsMaster())
845 m_pRespawnSystemComponent.OnPlayerAuditSuccess_S(iPlayerID);
846
847 // Dispatch event to child components
849 {
850 comp.OnPlayerAuditSuccess(iPlayerID);
851 }
852 }
853
854 //------------------------------------------------------------------------------------------------
855 override void OnPlayerAuditFail(int iPlayerID)
856 {
857 m_OnPlayerAuditFail.Invoke(iPlayerID);
858
859 // Dispatch event to child components
861 {
862 comp.OnPlayerAuditFail(iPlayerID);
863 }
864 }
865
866 //------------------------------------------------------------------------------------------------
867 override void OnPlayerAuditTimeouted(int iPlayerID)
868 {
869 super.OnPlayerAuditTimeouted(iPlayerID);
870 m_OnPlayerAuditTimeouted.Invoke(iPlayerID);
871
872 // Dispatch event to child components
874 {
875 comp.OnPlayerAuditTimeouted(iPlayerID);
876 }
877 };
878
879 //------------------------------------------------------------------------------------------------
880 override void OnPlayerAuditRevived(int iPlayerID)
881 {
882 super.OnPlayerAuditRevived(iPlayerID);
883 m_OnPlayerAuditRevived.Invoke(iPlayerID);
884
885 // Dispatch event to child components
887 {
888 comp.OnPlayerAuditRevived(iPlayerID);
889 }
890 };
891
892 //------------------------------------------------------------------------------------------------
897 override void OnPlayerConnected(int playerId)
898 {
899 if (!m_bIsHosted)
900 {
901 m_bIsHosted = GetGame().GetPlayerManager().GetPlayerController(playerId).GetRplIdentity() == RplIdentity.Local();
902 Replication.BumpMe();
903 }
904
905 m_OnPlayerConnected.Invoke(playerId);
906 // Dispatch event to child components
908 {
909 comp.OnPlayerConnected(playerId);
910 }
911
912
913 }
914
915 //------------------------------------------------------------------------------------------------
920 protected override void OnPlayerDisconnected(int playerId, KickCauseCode cause, int timeout)
921 {
922 m_OnPlayerDisconnected.Invoke(playerId, cause, timeout);
924 {
925 comp.OnPlayerDisconnected(playerId, cause, timeout);
926 }
927 m_OnPostCompPlayerDisconnected.Invoke(playerId, cause, timeout);
928
929 if (IsMaster())
930 {
931 IEntity character = GetGame().GetPlayerManager().GetPlayerControlledEntity(playerId);
932 if (character)
933 {
934 // If driving a vehicle it should be stopped as gracefully as possible to avoid crashing into nearby obstacles with passengers.
935 const CompartmentAccessComponent compAccess = CompartmentAccessComponent.Cast(character.FindComponent(CompartmentAccessComponent));
936 if (compAccess)
937 {
938 const PilotCompartmentSlot pilotCompartment = PilotCompartmentSlot.Cast(compAccess.GetCompartment());
939 if (pilotCompartment)
940 {
941 IEntity vehicle = pilotCompartment.GetVehicle();
943 if (carController)
944 {
945 carController.Shutdown();
946 carController.StopEngine(false);
947 carController.SetPersistentHandBrake(true);
948 }
949 else
950 {
952 if (heliController)
953 {
954 heliController.LockPilotControls(false);
955 heliController.SetPersistentWheelBrake(true);
956 heliController.SetAutohoverEnabled(true);
957 }
958 }
959 }
960 }
961 }
962
963 // If conditions to allow reconnect pass, skip the entity delete
965 if (reconnect && reconnect.HandlePlayerDisconnect(playerId, cause))
966 {
967 if (character)
968 {
969 CharacterControllerComponent charController = CharacterControllerComponent.Cast(character.FindComponent(CharacterControllerComponent));
970 if (charController)
971 charController.SetMovement(0, vector.Forward);
972
973 character = null; // Avoid deleting it
974 }
975 }
976
977 // RespawnSystemComponent is not a SCR_BaseGameModeComponent, so for now we have to propagate these events manually.
978 m_pRespawnSystemComponent.OnPlayerDisconnected_S(playerId, cause, timeout);
979
980 if (character)
981 {
982 m_pRespawnSystemComponent.OnPlayerEntityCleanup_S(character);
983 RplComponent.DeleteRplEntity(character, false);
984 }
985 }
986 }
987
988 //------------------------------------------------------------------------------------------------
993 protected override void OnPlayerRegistered(int playerId)
994 {
995 m_OnPlayerRegistered.Invoke(playerId);
996
997 // RespawnSystemComponent is not a SCR_BaseGameModeComponent, so for now we have to
998 // propagate these events manually.
999 if (IsMaster())
1000 m_pRespawnSystemComponent.OnPlayerRegistered_S(playerId);
1001
1003 {
1004 comp.OnPlayerRegistered(playerId);
1005 }
1006
1007 #ifdef GMSTATS
1008 if (IsGameModeStatisticsEnabled())
1009 SCR_GameModeStatistics.RecordConnection(playerId, GetGame().GetPlayerManager().GetPlayerName(playerId));
1010 #endif
1011
1012 // TODO: Remove once peertools properly invoke the audit success from gamecode and identity is available already during registered event.
1013 if (RplSession.Mode() == RplMode.Listen && playerId > 1)
1014 {
1015 OnPlayerAuditSuccess(playerId);
1016 }
1017 else if (RplSession.Mode() == RplMode.Dedicated)
1018 {
1019 string configPath;
1020 if (!System.GetCLIParam("config", configPath) || configPath.IsEmpty())
1021 OnPlayerAuditSuccess(playerId);
1022 }
1023 }
1024
1025 //------------------------------------------------------------------------------------------------
1026 protected override bool HandlePlayerKilled(int playerId, IEntity playerEntity, IEntity killerEntity, notnull Instigator killer)
1027 {
1029 {
1030 // First component that returns false overrides the behaviour and can handle
1031 // the kill based on their respective needs
1032 if (!comp.HandlePlayerKilled(playerId, playerEntity, killerEntity, killer))
1033 {
1034 OnPlayerKilledHandled(playerId, playerEntity, killerEntity, killer);
1035 return false;
1036 }
1037 }
1038
1039 // Handle automatically
1040 return true;
1041 }
1042
1043 //------------------------------------------------------------------------------------------------
1046 protected void LogKillEvent(notnull SCR_InstigatorContextData instigatorContext)
1047 {
1048 int victimPlayerId = instigatorContext.GetVictimPlayerID();
1049 if (victimPlayerId < 1)
1050 return; // only log killed players
1051
1052 string incidentType;
1053 SCR_ECharacterDeathStatusRelations relation = instigatorContext.GetVictimKillerRelation();
1054 switch (relation)
1055 {
1056 case SCR_ECharacterDeathStatusRelations.KILLED_BY_ENEMY_PLAYER:
1057 case SCR_ECharacterDeathStatusRelations.KILLED_BY_ENEMY_AI:
1058 incidentType = " ENEMY";
1059 break;
1060
1061 case SCR_ECharacterDeathStatusRelations.KILLED_BY_FRIENDLY_PLAYER:
1062 case SCR_ECharacterDeathStatusRelations.KILLED_BY_FRIENDLY_AI:
1063 incidentType = " TK";
1064 break;
1065
1066 case SCR_ECharacterDeathStatusRelations.KILLED_BY_UNLIMITED_EDITOR:
1067 incidentType = " GM";
1068 break;
1069
1070 default:
1071 incidentType = " " + typename.EnumToString(SCR_ECharacterDeathStatusRelations, relation);
1072 break;
1073 }
1074
1075 string victimIdentity = SCR_PlayerIdentityUtils.GetPlayerLogInfo(victimPlayerId);
1076
1077 int killerPlayerId = instigatorContext.GetKillerPlayerID();
1078 string killerIdentity;
1079 if (killerPlayerId < 1) // AI
1080 killerIdentity = " was killed by AI";
1081 else if (victimPlayerId == killerPlayerId) // suicide
1082 killerIdentity = " killed himself!";
1083 else
1084 killerIdentity = " was killed by " + SCR_PlayerIdentityUtils.GetPlayerLogInfo(killerPlayerId);
1085
1086 SCR_ChimeraCharacter victim = SCR_ChimeraCharacter.Cast(instigatorContext.GetVictimEntity());
1087 vector corpsePosition;
1088 string victimPosition;
1089 string causeOfDeath;
1090 if (victim)
1091 {
1092 Faction faction = victim.GetFaction();
1093 if (faction)
1094 victimIdentity += " from " + faction.GetFactionKey() + " faction";
1095
1096 corpsePosition = victim.GetOrigin();
1097 victimPosition = " at " + corpsePosition.ToString();
1098
1099 if (relation != SCR_ECharacterDeathStatusRelations.KILLED_BY_UNLIMITED_EDITOR)
1100 {
1101 SCR_CharacterDamageManagerComponent dmgMgr = SCR_CharacterDamageManagerComponent.Cast(victim.GetDamageManager());
1102 array<ref BaseDamageEffect> damageHistory = {};
1103 dmgMgr.GetDamageHistory(damageHistory);
1104 BaseDamageEffect effect;
1105 EDamageType dmgType;
1106 HitZone hitZone;
1107 HitZone defaultHitZone = dmgMgr.GetDefaultHitZone();
1108 for (int i = damageHistory.Count() - 1; i >= 0; i--)
1109 {
1110 effect = damageHistory[i];
1111 if (!effect)
1112 {
1113 effect = null;
1114 continue;
1115 }
1116
1117 dmgType = effect.GetDamageType();
1118 if (dmgType == EDamageType.HEALING && dmgType == EDamageType.REGENERATION)
1119 {
1120 effect = null;
1121 continue;
1122 }
1123
1124 if (dmgType == EDamageType.BLEEDING && effect.GetTotalDamage() < 1)
1125 {
1126 effect = null;
1127 continue; // bleeding can be added at the moment when projectile deals lethal damage, and thus it may have not be the cause of death
1128 }
1129
1130 if (effect.GetInstigator().GetInstigatorPlayerID() != killerPlayerId)
1131 {
1132 effect = null;
1133 continue;
1134 }
1135
1136 hitZone = effect.GetAffectedHitZone();
1137 if (hitZone && hitZone != defaultHitZone && hitZone.GetName() != "Resilience")
1138 break;
1139
1140 effect = null;
1141 }
1142
1143 if (effect)
1144 {
1145 causeOfDeath = " With last inflicted damage type " + typename.EnumToString(EDamageType, dmgType);
1146 if (hitZone)
1147 causeOfDeath += " to the '" + hitZone.GetName() + "' hit zone";
1148 }
1149 }
1150 }
1151
1152 LogLevel logLevel = LogLevel.NORMAL;
1153
1154 string messageType = "INFO";
1155 string killerPosition;
1156 string victimDisguiseState;
1157 if (relation != SCR_ECharacterDeathStatusRelations.KILLED_BY_UNLIMITED_EDITOR && victimPlayerId != killerPlayerId)
1158 {
1159 SCR_ChimeraCharacter killer = SCR_ChimeraCharacter.Cast(instigatorContext.GetKillerEntity());
1160 if (killer)
1161 {
1162 Faction faction = killer.GetFaction();
1163 if (faction)
1164 killerIdentity += " from " + faction.GetFactionKey() + " faction";
1165
1166 vector pos = killer.GetOrigin();
1167 killerPosition = string.Format(" who was at that time at %1 [%2m away from the corpse].", pos.ToString(), vector.Distance(pos, corpsePosition).ToString(lenDec: 1));
1168
1169 if (killerPlayerId > 0)
1170 { // only check the position of the killer in case of players
1171 TraceParam param = new TraceParam();
1172 param.Start = pos + vector.Up * 0.1;
1173 param.End = param.Start + vector.Up * (-1);
1174 param.Flags = TraceFlags.WORLD | TraceFlags.ENTS;
1175 param.Exclude = killer;
1176 param.LayerMask = EPhysicsLayerPresets.Projectile;
1177 float traceDistance = killer.GetWorld().TraceMove(param, FilterCallback);
1178 if (traceDistance >= 1 && param.TraceEnt == null)
1179 {
1180 logLevel = LogLevel.WARNING;
1181 killerPosition += " No surface found within 1m under that position!";
1182 messageType = "WARNING";
1183 }
1184 }
1185 }
1186 else if (killerPlayerId > 0)
1187 {
1188 Faction faction = SCR_FactionManager.SGetPlayerFaction(killerPlayerId);
1189 if (faction)
1190 killerIdentity += " from " + faction.GetFactionKey() + " faction";
1191 }
1192
1193 if (instigatorContext.GetVictimDisguiseType() == SCR_ECharacterDisguiseType.HOSTILE_FACTION)
1194 victimDisguiseState = " while being disguised as enemy";
1195 }
1196
1197 PrintFormat("%1: KILL%2: %3%4%5%6%7%8", messageType, incidentType, victimIdentity, victimPosition, victimDisguiseState, killerIdentity, killerPosition, causeOfDeath, level: logLevel);
1198 }
1199
1200 //------------------------------------------------------------------------------------------------
1201 protected bool FilterCallback(IEntity e)
1202 {
1203 if (!e)
1204 return false;
1205
1206 IEntity parent = e.GetParent();
1207 while (parent)
1208 {
1209 if (ChimeraCharacter.Cast(parent))
1210 return false;
1211
1212 parent = parent.GetParent();
1213 }
1214
1215 return true;
1216 }
1217
1218 //------------------------------------------------------------------------------------------------
1220 protected override void OnPlayerKilled(int playerId, IEntity playerEntity, IEntity killerEntity, notnull Instigator killer)
1221 {
1222 super.OnPlayerKilled(playerId, playerEntity, killerEntity, killer);
1223
1224 //~ Create instigator context data to determine what the relation is between victim and killer and control types of the victim and killer
1225 SCR_InstigatorContextData instigatorContextData = new SCR_InstigatorContextData(playerId, playerEntity, killerEntity, killer);
1226
1227 //~ Send on player killed event
1228 m_OnPlayerKilled.Invoke(instigatorContextData);
1229
1230
1231 // RespawnSystemComponent is not a SCR_BaseGameModeComponent, so for now we have to
1232 // propagate these events manually.
1233 if (IsMaster())
1234 m_pRespawnSystemComponent.OnPlayerKilled_S(playerId, playerEntity, killerEntity, killer);
1235
1236 // Dispatch events to children components
1238 {
1239 comp.OnPlayerKilled(instigatorContextData);
1240 }
1241
1242 #ifdef GMSTATS
1243 if (IsGameModeStatisticsEnabled())
1244 SCR_GameModeStatistics.RecordDeath(playerId, playerEntity, killerEntity, killer);
1245 #endif
1246
1247 //~ Call extended OnPlayerKilled
1248 OnPlayerKilledEx(instigatorContextData);
1249 }
1250
1251 //------------------------------------------------------------------------------------------------
1254 protected void OnPlayerKilledEx(notnull SCR_InstigatorContextData instigatorContextData);
1255
1256 //------------------------------------------------------------------------------------------------
1258 protected void OnPlayerKilledHandled(int playerId, IEntity playerEntity, IEntity killerEntity, notnull Instigator killer)
1259 {
1261 comp.OnPlayerKilledHandled(playerId, playerEntity, killerEntity, killer);
1262 }
1263
1264 //------------------------------------------------------------------------------------------------
1265 protected void OnPlayerDeleted(int playerId, IEntity player)
1266 {
1267 // RespawnSystemComponent is not a SCR_BaseGameModeComponent, so for now we have to
1268 // propagate these events manually.
1269 if (IsMaster())
1270 m_pRespawnSystemComponent.OnPlayerDeleted_S(playerId);
1271
1272 //super.OnPlayerDeleted(playerId, player);
1273 m_OnPlayerDeleted.Invoke(playerId, player);
1274
1275 // Dispatch events to children components
1277 {
1278 comp.OnPlayerDeleted(playerId, player);
1279 }
1280 }
1281
1282 //------------------------------------------------------------------------------------------------
1288 protected override void OnPlayerRoleChange(int playerId, EPlayerRole roleFlags)
1289 {
1290 m_OnPlayerRoleChange.Invoke(playerId, roleFlags);
1291
1292 // Dispatch events to children components
1294 {
1295 comp.OnPlayerRoleChange(playerId, roleFlags);
1296 }
1297 }
1298
1299 //------------------------------------------------------------------------------------------------
1304 override event void OnWorldPostProcess(World world)
1305 {
1306 m_OnWorldPostProcess.Invoke(world);
1307
1309 {
1310 comp.OnWorldPostProcess(world);
1311 }
1312 };
1313
1314 //------------------------------------------------------------------------------------------------
1315 /*
1316 When a controllable entity is spawned, this event is raised.
1317 \param entity Spawned entity that raised this event
1318 */
1319 protected override void OnControllableSpawned(IEntity entity)
1320 {
1321 super.OnControllableSpawned(entity);
1322 m_OnControllableSpawned.Invoke(entity);
1323
1325 {
1326 comp.OnControllableSpawned(entity);
1327 }
1328 }
1329
1330 //------------------------------------------------------------------------------------------------
1331 /*
1332 When a controllable entity is destroyed, this event is raised.
1333 \param entity Destroyed entity that raised this event
1334 \param killerEntity Entity that killed the entity (can be null)
1335 \param killer Instigator entity that destroyed our victim
1336 */
1337 protected override void OnControllableDestroyed(IEntity entity, IEntity killerEntity, notnull Instigator instigator)
1338 {
1339 super.OnControllableDestroyed(entity, killerEntity, instigator);
1340
1341 //~ Create instigator context data to determine what the relation is between victim and killer and control types of the victim and killer
1342 SCR_InstigatorContextData instigatorContextData = new SCR_InstigatorContextData(-1, entity, killerEntity, instigator);
1343 m_OnControllableDestroyed.Invoke(instigatorContextData);
1344
1345 //~ Debug print to see death types of characters
1346 if (ChimeraCharacter.Cast(entity) && DiagMenu.GetBool(SCR_DebugMenuID.DEBUGUI_CHARACTER_KILLED_LOG))
1347 {
1348 Print("Character Killed: " + typename.EnumToString(SCR_ECharacterDeathStatusRelations, instigatorContextData.GetVictimKillerRelation()) +
1349 " | Victim: " + typename.EnumToString(SCR_ECharacterControlType, instigatorContextData.GetVictimCharacterControlType()) + ", id: " + instigatorContextData.GetVictimPlayerID() + ", Disguise type: " + typename.EnumToString(SCR_ECharacterDisguiseType, instigatorContextData.GetVictimDisguiseType()) +
1350 " | Killer: " + typename.EnumToString(SCR_ECharacterControlType, instigatorContextData.GetKillerCharacterControlType()) + ", id: " + instigatorContextData.GetKillerPlayerID() + ", Disguise type: " + typename.EnumToString(SCR_ECharacterDisguiseType, instigatorContextData.GetKillerDisguiseType()),
1351 LogLevel.NORMAL);
1352 }
1353
1355 {
1356 comp.OnControllableDestroyed(instigatorContextData);
1357 }
1358
1359 //~ Call extended OnControllableDestroyed
1360 OnControllableDestroyedEx(instigatorContextData);
1361 if (IsMaster())
1362 LogKillEvent(instigatorContextData);
1363 }
1364
1365 //------------------------------------------------------------------------------------------------
1368 protected void OnControllableDestroyedEx(notnull SCR_InstigatorContextData instigatorContextData);
1369
1370 //------------------------------------------------------------------------------------------------
1371 /*
1372 Prior to a controllable entity being DELETED, this event is raised.
1373 Controllable entity is such that has BaseControllerComponent and can be
1374 possessed either by a player, an AI or stay unpossessed.
1375 \param entity Entity about to be deleted
1376 */
1377 protected override void OnControllableDeleted(IEntity entity)
1378 {
1379 super.OnControllableDeleted(entity);
1380 m_OnControllableDeleted.Invoke(entity);
1381
1383 {
1384 comp.OnControllableDeleted(entity);
1385 }
1386
1387 // Ensure that controlled entity is a player
1388 const int playerId = GetGame().GetPlayerManager().GetPlayerIdFromControlledEntity(entity);
1389 if (playerId > 0)
1390 OnPlayerDeleted(playerId, entity);
1391 }
1392
1393 //------------------------------------------------------------------------------------------------
1401 void OnSpawnPlayerEntityFailure_S(SCR_SpawnRequestComponent requestComponent, SCR_SpawnHandlerComponent handlerComponent, IEntity entity, SCR_SpawnData data, SCR_ESpawnResult reason)
1402 {
1404 component.OnSpawnPlayerEntityFailure_S(requestComponent, handlerComponent, entity, data, reason);
1405 }
1406
1407 //------------------------------------------------------------------------------------------------
1412 void OnPlayerEntityChanged_S(int playerId, IEntity previousEntity, IEntity newEntity)
1413 {
1414 }
1415
1429 bool PreparePlayerEntity_S(SCR_SpawnRequestComponent requestComponent, SCR_SpawnHandlerComponent handlerComponent, SCR_SpawnData data, IEntity entity)
1430 {
1431 // Example(s):
1432 // - load some data from storage, fill inventory with stored items
1433 // - apply loaded data by setting provided entity to bleed for a certain amount
1434 // - drop character unconscious?
1435
1437 {
1438 if (!component.PreparePlayerEntity_S(requestComponent, handlerComponent, data, entity))
1439 return false;
1440 }
1441
1442 // Entity preparation has succeeded
1443 return true;
1444 }
1445
1446 //------------------------------------------------------------------------------------------------
1457 void OnPlayerSpawnFinalize_S(SCR_SpawnRequestComponent requestComponent, SCR_SpawnHandlerComponent handlerComponent, SCR_SpawnData data, IEntity entity)
1458 {
1460 if (spawnPointData)
1461 {
1462 OnPlayerSpawnOnPoint_S(requestComponent, handlerComponent, entity, spawnPointData);
1463 }
1464
1466 component.OnPlayerSpawnFinalize_S(requestComponent, handlerComponent, data, entity);
1467
1468 SCR_PlayerLoadoutComponent loadoutComp = SCR_PlayerLoadoutComponent.Cast(requestComponent.GetPlayerController().FindComponent(SCR_PlayerLoadoutComponent));
1469 if (!SCR_PossessSpawnData.Cast(data) // hotfix when player is spawned through game master, their loadout would change to the saved loadout
1470 && loadoutComp && loadoutComp.GetLoadout())
1471 loadoutComp.GetLoadout().OnLoadoutSpawned(GenericEntity.Cast(entity), requestComponent.GetPlayerId());
1472
1473 m_OnPlayerSpawned.Invoke(requestComponent.GetPlayerId(), entity);
1474 }
1475
1476 //------------------------------------------------------------------------------------------------
1482 void OnPlayerSpawnOnPoint_S(SCR_SpawnRequestComponent requestComponent, SCR_SpawnHandlerComponent handlerComponent, IEntity entity, SCR_SpawnPointSpawnData spawnPointData)
1483 {
1484 SCR_PlayerLoadoutComponent loadoutComp = SCR_PlayerLoadoutComponent.Cast(requestComponent.GetPlayerController().FindComponent(SCR_PlayerLoadoutComponent));
1485
1486 //~ Update supplies of base or spawn point
1487 ConsumeSuppliesOnPlayerSpawn_S(requestComponent.GetPlayerId(), spawnPointData.GetSpawnPoint(), loadoutComp);
1488
1489 //~ Consume Military Supply Allocation
1490 ConsumeMilitarySupplyAllocationOnPlayerSpawn_S(requestComponent, spawnPointData.GetSpawnPoint(), loadoutComp);
1491 }
1492
1493 //------------------------------------------------------------------------------------------------
1494 //~ Consume supplies for spawning
1495 protected void ConsumeSuppliesOnPlayerSpawn_S(int playerID, IEntity spawnPoint, SCR_PlayerLoadoutComponent loadoutComp)
1496 {
1498 SCR_ResourceComponent resourceComp;
1499
1500 int spawnSupplyCost = 0;
1501 if (loadoutComp)
1502 spawnSupplyCost = SCR_ArsenalManagerComponent.GetLoadoutCalculatedSupplyCost(loadoutComp.GetLoadout(), false, playerID, null, spawnPoint, base, resourceComp);
1503
1504 if (spawnSupplyCost > 0)
1505 {
1506 if (base)
1507 base.AddSupplies(spawnSupplyCost * -1);
1508 else if (resourceComp)
1509 SCR_ResourceSystemHelper.ConsumeResources(resourceComp, spawnSupplyCost, false);
1510 }
1511 }
1512
1513 //------------------------------------------------------------------------------------------------
1514 // Consume Military Supply Allocation for spawning
1515 // param[in] requestComponent
1516 // param[in] spawnPoint
1517 // param[in] loadoutComponent
1518 protected void ConsumeMilitarySupplyAllocationOnPlayerSpawn_S(SCR_SpawnRequestComponent requestComponent, IEntity spawnPoint, SCR_PlayerLoadoutComponent loadoutComponent)
1519 {
1520 if (!SCR_ArsenalManagerComponent.IsMilitarySupplyAllocationEnabled())
1521 return;
1522
1523 SCR_ArsenalManagerComponent arsenalManager;
1524 if (!SCR_ArsenalManagerComponent.GetArsenalManager(arsenalManager))
1525 return;
1526
1527 int spawnCost = arsenalManager.GetLoadoutMilitarySupplyAllocationCost(loadoutComponent.GetLoadout(), requestComponent.GetPlayerId());
1528 if (spawnCost <= 0)
1529 return;
1530
1531 SCR_PlayerController playerController = SCR_PlayerController.Cast(requestComponent.GetPlayerController());
1532 if (!playerController)
1533 return;
1534
1535 SCR_PlayerSupplyAllocationComponent playerSupplyAllocationComponent = SCR_PlayerSupplyAllocationComponent.Cast(playerController.FindComponent(SCR_PlayerSupplyAllocationComponent));
1536 if (!playerSupplyAllocationComponent)
1537 return;
1538
1539 playerSupplyAllocationComponent.AddPlayerAvailableAllocatedSupplies(-1 * spawnCost);
1540 }
1541
1542 //------------------------------------------------------------------------------------------------
1550
1551 //------------------------------------------------------------------------------------------------
1557 {
1558 }
1559
1560 //------------------------------------------------------------------------------------------------
1570 bool CanPlayerSpawn_S(SCR_SpawnRequestComponent requestComponent, SCR_SpawnHandlerComponent handlerComponent, SCR_SpawnData data, out SCR_ESpawnResult result = SCR_ESpawnResult.SPAWN_NOT_ALLOWED)
1571 {
1573 {
1574 int playerId = requestComponent.GetPlayerId();
1575
1576 // If player has not been enqueued yet, ignore the spawn timer
1577 if (m_RespawnTimerComponent.IsPlayerEnqueued(playerId))
1578 {
1579 float spawnPointTime = 0;
1580 if (data.IsInherited(SCR_SpawnPointSpawnData))
1581 {
1583 spawnPointTime = spData.GetSpawnPoint().GetRespawnTime();
1584 }
1585
1586 if (!m_RespawnTimerComponent.GetCanPlayerSpawn(playerId, spawnPointTime))
1587 {
1588 result = SCR_ESpawnResult.NOT_ALLOWED_TIMER;
1589 return false;
1590 }
1591 }
1592 }
1593
1595 if (!spawnPointData)
1596 return true;
1597
1598 //~ Check if spawn point has enough supplies
1600 SCR_ResourceComponent resourceComp;
1601
1602 int spawnSupplyCost = 0;
1603
1604 SCR_PlayerLoadoutComponent loadoutComp = SCR_PlayerLoadoutComponent.Cast(requestComponent.GetPlayerController().FindComponent(SCR_PlayerLoadoutComponent));
1605 if (loadoutComp)
1606 spawnSupplyCost = SCR_ArsenalManagerComponent.GetLoadoutCalculatedSupplyCost(loadoutComp.GetLoadout(), false, requestComponent.GetPlayerId(), null, spawnPointData.GetSpawnPoint(), base, resourceComp);
1607
1608 if (base)
1609 {
1610 //~ Check if there are enough supplies for base
1611 if (base.GetSupplies() < spawnSupplyCost)
1612 {
1613 result = SCR_ESpawnResult.NOT_ALLOWED_NOT_ENOUGH_SUPPLIES;
1614 return false;
1615 }
1616 }
1617 else if (resourceComp)
1618 {
1619 //~ Check if there are enough supplies for Resource comp
1620 float availableSupplies;
1621 if (SCR_ResourceSystemHelper.GetAvailableResources(resourceComp, availableSupplies))
1622 {
1623 if (availableSupplies < spawnSupplyCost)
1624 {
1625 result = SCR_ESpawnResult.NOT_ALLOWED_NOT_ENOUGH_SUPPLIES;
1626 return false;
1627 }
1628 }
1629 }
1630
1631 if (SCR_ArsenalManagerComponent.IsMilitarySupplyAllocationEnabled())
1632 {
1633 SCR_PlayerArsenalLoadout arsenalLoadout = SCR_PlayerArsenalLoadout.Cast(loadoutComp.GetLoadout());
1634 PlayerController playerController = requestComponent.GetPlayerController();
1635 SCR_ArsenalManagerComponent arsenalManager;
1636
1637 //~ Check if player has enough Available Allocated Supplies to spawn with a custom loadout
1638 if (arsenalLoadout && playerController && SCR_ArsenalManagerComponent.GetArsenalManager(arsenalManager))
1639 {
1640 SCR_PlayerSupplyAllocationComponent playerSupplyAllocationComponent = SCR_PlayerSupplyAllocationComponent.Cast(playerController.FindComponent(SCR_PlayerSupplyAllocationComponent));
1641 if (playerSupplyAllocationComponent && !playerSupplyAllocationComponent.HasPlayerEnoughAvailableAllocatedSupplies(arsenalManager.GetLoadoutMilitarySupplyAllocationCost(arsenalLoadout, playerController.GetPlayerId())))
1642 {
1643 result = SCR_ESpawnResult.NOT_ALLOWED_NOT_ENOUGH_AVAILABLE_ALLOCATED_SUPPLIES;
1644 return false;
1645 }
1646 }
1647 }
1648
1649 result = SCR_ESpawnResult.OK;
1650 return true;
1651 }
1652
1653 //------------------------------------------------------------------------------------------------
1656 {
1657 // Respawn timers
1659 return m_RespawnTimerComponent.GetPlayerRemainingTime(playerID);
1660
1661 return 0;
1662 }
1663
1664 //------------------------------------------------------------------------------------------------
1665 override bool RplSave(ScriptBitWriter writer)
1666 {
1667 EGameFlags gameFlags = GetGame().GetGameFlags();
1668 writer.WriteIntRange(gameFlags, 0, (EGameFlags.Last<<1) - 1);
1669
1670 return true;
1671 }
1672
1673 //------------------------------------------------------------------------------------------------
1674 override bool RplLoad(ScriptBitReader reader)
1675 {
1676 EGameFlags gameFlags;
1677 reader.ReadIntRange(gameFlags, 0, (EGameFlags.Last<<1) - 1);
1678
1679 GetGame().SetGameFlags(gameFlags, true);
1680
1681 return true;
1682 }
1683
1684 #ifdef GAME_MODE_DEBUG
1685
1686 //------------------------------------------------------------------------------------------------
1688 void Diag_DrawPlayersWindow()
1689 {
1690 //DbgUI.Begin("SCR_BaseGameMode: Players Diag");
1691 array<int> connectedPlayers = {};
1692 GetGame().GetPlayerManager().GetPlayers(connectedPlayers);
1693
1694 array<int> disconnectedPlayers = {};
1695 GetGame().GetPlayerManager().GetDisconnectedPlayers(disconnectedPlayers);
1696
1697 DbgUI.Begin("SCR_BaseGameMode: Connected players");
1698 {
1699 for (int i = 0, cnt = connectedPlayers.Count(); i < cnt; i++)
1700 {
1701 int playerId = connectedPlayers[i];
1702 Diag_DrawPlayerInfo(playerId);
1703 }
1704 }
1705 DbgUI.End();
1706 DbgUI.Begin("SCR_BaseGameMode: Disconnected players");
1707 {
1708 for (int i = 0, cnt = disconnectedPlayers.Count(); i < cnt; i++)
1709 {
1710 int playerId = disconnectedPlayers[i];
1711 Diag_DrawPlayerInfo(playerId);
1712 }
1713 }
1714 DbgUI.End();
1715 }
1716
1717 //------------------------------------------------------------------------------------------------
1720 void Diag_DrawPlayerInfo(int playerId)
1721 {
1722 string tmp = string.Format("Player %1: (name: %2)", playerId, GetGame().GetPlayerManager().GetPlayerName(playerId));
1723 array<string> extra = {};
1724
1725 // Fetch faction info (if present)
1726 SCR_FactionManager factionManager = SCR_FactionManager.Cast(GetGame().GetFactionManager());
1727 if (factionManager)
1728 {
1729 string factionKey = "None";
1730 Faction faction = factionManager.GetPlayerFaction(playerId);
1731 if (faction)
1732 {
1733 factionKey = faction.GetFactionKey();
1734 }
1735
1736 extra.Insert(string.Format("Faction: %1", factionKey));
1737 }
1738
1739 // Fetch loadout info (if present)
1740 SCR_LoadoutManager loadoutManager = GetGame().GetLoadoutManager();
1741 if (loadoutManager)
1742 {
1743 string loadoutKey = "None";
1744 SCR_BasePlayerLoadout loadout = loadoutManager.GetPlayerLoadout(playerId);
1745 if (loadout)
1746 {
1747 loadoutKey = loadout.GetLoadoutName();
1748 }
1749
1750 extra.Insert(string.Format("Loadout: %1", loadoutKey));
1751 }
1752
1753 // If any additional info is present, start formatting
1754 if (!extra.IsEmpty())
1755 {
1756 const string separator = "\n ";
1757 for (int i = 0, count = extra.Count(); i < count; i++)
1758 {
1759 tmp += string.Format("%1%2", separator, extra[i]);
1760 }
1761 }
1762
1763 DbgUI.Text(tmp);
1764 }
1765
1766 //------------------------------------------------------------------------------------------------
1768 void Diag_DrawControlledEntitiesWindow()
1769 {
1770 array<int> allPlayers = {};
1771 GetGame().GetPlayerManager().GetAllPlayers(allPlayers);
1772
1773 DbgUI.Begin("SCR_BaseGameMode: Controlled Entities");
1774 {
1775 for (int i = 0, cnt = allPlayers.Count(); i < cnt; i++)
1776 {
1777 int playerId = allPlayers[i];
1778 Diag_DrawControlledEntityInfo(playerId);
1779 }
1780 }
1781 DbgUI.End();
1782 }
1783
1784 //------------------------------------------------------------------------------------------------
1787 void Diag_DrawControlledEntityInfo(int playerId)
1788 {
1789 string tmp = string.Format("Player %1: (name: %2)", playerId, GetGame().GetPlayerManager().GetPlayerName(playerId));
1790 array<string> extra = {};
1791
1792 // IsConnected?
1793 if (GetGame().GetPlayerManager().IsPlayerConnected(playerId))
1794 {
1795 extra.Insert("IsConnected: True");
1796 }
1797 else
1798 extra.Insert("IsConnected: False");
1799
1800 // Controlled entity
1801 IEntity controlledEntity = GetGame().GetPlayerManager().GetPlayerControlledEntity(playerId);
1802 if (controlledEntity)
1803 {
1804 extra.Insert(string.Format("Entity: %1", controlledEntity));
1805
1806 RplComponent rplComponent = RplComponent.Cast(controlledEntity.FindComponent(RplComponent));
1807 if (rplComponent)
1808 {
1809 extra.Insert(string.Format("RplId: %1", rplComponent.Id()));
1810 }
1811 }
1812 else
1813 {
1814 extra.Insert("Entity: None");
1815 }
1816
1817
1818 // If any additional info is present, start formatting
1819 if (!extra.IsEmpty())
1820 {
1821 const string separator = "\n ";
1822 for (int i = 0, count = extra.Count(); i < count; i++)
1823 {
1824 tmp += string.Format("%1%2", separator, extra[i]);
1825 }
1826 }
1827
1828 DbgUI.Text(tmp);
1829 }
1830
1831 //------------------------------------------------------------------------------------------------
1833 void Diag_DrawComponentsWindow()
1834 {
1835 DbgUI.Begin("SCR_BaseGameMode: Components");
1836 {
1837 int numComponents = m_aAdditionalGamemodeComponents.Count();
1838 DbgUI.Text(string.Format("NumComponents: %1", numComponents));
1839
1840 for (int i = 0; i < numComponents; i++)
1841 {
1842 Diag_DrawComponentInfo(i, m_aAdditionalGamemodeComponents[i]);
1843 }
1844 }
1845 DbgUI.End();
1846 }
1847
1848 //------------------------------------------------------------------------------------------------
1850 void Diag_DrawComponentInfo(int index, SCR_BaseGameModeComponent component)
1851 {
1852 DbgUI.Text(string.Format("%1: %2", index, component));
1853 }
1854
1855 //------------------------------------------------------------------------------------------------
1857 void Diag_DrawGameModeWindow()
1858 {
1859 DbgUI.Begin("SCR_BaseGameMode");
1860 {
1861 string elapsedTime = string.Format("Elapsed time: %1 s", GetElapsedTime());
1862 DbgUI.Text(elapsedTime);
1863
1864 string running = string.Format("IsRunning: %1", IsRunning());
1865 DbgUI.Text(running);
1866
1867 string state = string.Format("State: %1", SCR_Enum.GetEnumName(SCR_EGameModeState, GetState()));
1868 DbgUI.Text(state);
1869
1870 string flags = string.Format("TestFlags: %1 (%2)", SCR_Enum.FlagsToString(EGameFlags, m_eGameState), m_eGameState);
1871 DbgUI.Text(flags);
1872
1873 if (IsMaster())
1874 {
1875 DbgUI.Spacer(16);
1876 DbgUI.Text("Authority details:");
1877
1878 float autoReloadDelay = GetAutoReloadDelay();
1879 if (autoReloadDelay > 0)
1880 {
1881 DbgUI.Text(string.Format("Auto reload: %1 seconds.", autoReloadDelay));
1882 }
1883 else
1884 {
1885 DbgUI.Text("Auto reload: Disabled.");
1886 }
1887
1888 DbgUI.Spacer(16);
1889
1890 if (DbgUI.Button("End Session"))
1891 EndGameMode(SCR_GameModeEndData.CreateSimple(EGameOverTypes.FACTION_DRAW));
1892
1893 if (DbgUI.Button("Restart Session"))
1895 }
1896 }
1897 DbgUI.End();
1898 }
1899 #endif
1900
1901 //------------------------------------------------------------------------------------------------
1904
1905 //------------------------------------------------------------------------------------------------
1908 protected void SetLocalControls(bool enabled)
1909 {
1910 PlayerController playerController = GetGame().GetPlayerController();
1911 if (playerController)
1912 {
1913 IEntity controlledEntity = playerController.GetControlledEntity();
1914 if (controlledEntity)
1915 {
1916 ChimeraCharacter character = ChimeraCharacter.Cast(controlledEntity);
1917 if (character)
1918 {
1919 CharacterControllerComponent controller = character.GetCharacterController();
1920 if (controller)
1921 {
1922 bool doDisable = !enabled;
1923 controller.SetDisableWeaponControls(doDisable);
1924 controller.SetDisableMovementControls(doDisable);
1925 }
1926 }
1927 }
1928 }
1929 }
1930
1931 #ifdef GM_AI_STATS
1932 private float m_fLastRecordTime;
1933
1934 private float m_fLastFlushTime;
1935 private float m_fFlushRecordInterval = 10000; // 10s
1936
1937 //------------------------------------------------------------------------------------------------
1938 private void UpdateStatistics(IEntity owner)
1939 {
1940 // Create new records only ever so often
1941 float timeStamp = owner.GetWorld().GetWorldTime();
1942 if (timeStamp > m_fLastRecordTime + SCR_GameModeStatistics.RECORD_INTERVAL_MS)
1943 {
1944 m_fLastRecordTime = timeStamp;
1945
1946 #ifdef GMSTATS
1947 PlayerManager pm = GetGame().GetPlayerManager();
1948 array<int> players = {};
1949 pm.GetPlayers(players);
1950
1951 foreach (int pid : players)
1952 {
1953 IEntity ctrlEnt = pm.GetPlayerControlledEntity(pid);
1954 if (!ctrlEnt)
1955 continue;
1956
1957 SCR_GameModeStatistics.RecordMovement(ctrlEnt, pid);
1958 }
1959 #endif
1960 #ifdef AISTATS
1961 auto aiWorld = SCR_AIWorld.Cast(GetGame().GetAIWorld());
1962 if (aiWorld)
1963 {
1964 array<AIAgent> aiAgents = {};
1965 aiWorld.GetAIAgents(aiAgents);
1966 foreach (AIAgent agent : aiAgents)
1967 {
1968 SCR_GameModeStatistics.RecordAILOD(agent);
1969 }
1970 }
1971 #endif
1972 }
1973
1974 // Flush data if recording in smaller intervals
1975 if (timeStamp > m_fLastFlushTime + m_fFlushRecordInterval)
1976 {
1977 m_fLastFlushTime = timeStamp;
1978 if (IsGameModeStatisticsEnabled())
1979 {
1980 if (SCR_GameModeStatistics.IsRecording())
1981 SCR_GameModeStatistics.Flush();
1982 }
1983 }
1984 }
1985 #endif
1986
1987 //------------------------------------------------------------------------------------------------
1991 {
1992 return m_bAllowControls;
1993 }
1994
1995 //------------------------------------------------------------------------------------------------
1998 protected bool GetAllowControlsTarget()
1999 {
2000 SCR_BaseGameModeStateComponent stateComponent = GetStateComponent(GetState());
2001 if (stateComponent)
2002 return stateComponent.GetAllowControls();
2003
2004 return true;
2005 }
2006
2007 //------------------------------------------------------------------------------------------------
2009 SCR_BaseGameModeStateComponent GetStateComponent(SCR_EGameModeState state)
2010 {
2011 SCR_BaseGameModeStateComponent stateComponent;
2012 if (m_mStateComponents.Find(state, stateComponent))
2013 return stateComponent;
2014
2015 return null;
2016 }
2017
2018 //------------------------------------------------------------------------------------------------
2026 {
2027 if (!CanBePaused())
2028 return false;
2029
2030#ifdef ENABLE_DIAG
2031 if (DiagMenu.GetBool(SCR_DebugMenuID.DEBUGUI_SINGLEPLAYER_DISABLE_TIME_PAUSE))
2032 pause = false;
2033#endif
2034
2035 if (pause)
2037 else
2038 m_ePauseReasons = m_ePauseReasons &~ reason;
2039
2040 ChimeraWorld world = GetGame().GetWorld();
2041 world.PauseGameTime(m_ePauseReasons != 0);
2042
2043 // Audio
2044 if (m_ePauseReasons != 0)
2045 {
2046 if (SCR_Enum.HasFlag(m_ePauseReasons, SCR_EPauseReason.MUSIC))
2047 {
2048 AudioSystem.Pause((1 << AudioSystem.SFX) | (1 << AudioSystem.Dialog) | (1 << AudioSystem.Music));
2049 }
2050 else
2051 {
2052 AudioSystem.Pause((1 << AudioSystem.SFX) | (1 << AudioSystem.Dialog));
2053 AudioSystem.Resume(1 << AudioSystem.Music);
2054 }
2055 }
2056 else
2057 {
2058 AudioSystem.Resume((1 << AudioSystem.SFX) | (1 << AudioSystem.Dialog) | (1 << AudioSystem.Music));
2059 }
2060
2061 return !pause && m_ePauseReasons != 0 || pause;
2062 }
2063
2064 //------------------------------------------------------------------------------------------------
2067 {
2068 return !Replication.IsRunning();
2069 }
2070
2071 //------------------------------------------------------------------------------------------------
2072 // TODO@AS: Small thing, but get rid of m_fTimeElapsed and use
2073 // some sort of m_fStartTime and Replication.Time() instead!
2074 override void EOnFrame(IEntity owner, float timeSlice)
2075 {
2076 #ifdef GM_AI_STATS
2077 if (IsGameModeStatisticsEnabled())
2078 UpdateStatistics(owner);
2079 #endif
2080
2082 HandleSpawnPreload(timeSlice);
2083
2084 // Allow to accumulate time in pre-game too.
2085 SCR_BaseGameModeStateComponent pregameComponent = GetStateComponent(SCR_EGameModeState.PREGAME);
2086 bool isPregame = GetState() == SCR_EGameModeState.PREGAME;
2087 // Increment elapsed time on every machine
2088 bool isRunning = IsRunning();
2089 if (isRunning || isPregame)
2090 {
2091 bool canAdvanceTime = true;
2092
2093 // Check if any players are present; if using "advance time requires players"
2094 // we will only advance the time if at least one player is present;
2095 // this is fairly easy to do, because dedicated (headless) server does not
2096 // count as a player in the PlayerManager
2098 {
2099 int playerCount = GetGame().GetPlayerManager().GetPlayerCount();
2100 if (playerCount == 0)
2101 canAdvanceTime = false;
2102 }
2103
2104 if (canAdvanceTime)
2105 m_fTimeElapsed += timeSlice;
2106 }
2107
2108 // As the authority make corrections as needed
2109 if (IsMaster())
2110 {
2112 {
2113 Replication.BumpMe();
2115 }
2116
2117 // Transition from pre-game to game if possible
2118 // Either fully automatic transition (no component)
2119 // or state-driven transition based on component logic
2120 if (isPregame && (!pregameComponent || pregameComponent.CanAdvanceState(SCR_EGameModeState.GAME)))
2121 {
2122 if (CanStartGameMode())
2123 StartGameMode();
2124 }
2125 else
2126 {
2127 // Time limit end game transition to post-game if possible
2128 if (IsRunning())
2129 {
2130 SCR_BaseGameModeStateComponent gameState = GetStateComponent(SCR_EGameModeState.GAME);
2131 if (gameState && gameState.CanAdvanceState(SCR_EGameModeState.POSTGAME))
2132 {
2133 // Clamp time to maximum
2134 m_fTimeElapsed = Math.Clamp(m_fTimeElapsed, 0, gameState.GetDuration());
2135
2136 // Terminate session
2137 SCR_GameModeEndData data = SCR_GameModeEndData.CreateSimple(EGameOverTypes.EDITOR_FACTION_DRAW); // TODO: Once FACTION_DRAW or TIME_LIMIT works..
2139 }
2140 }
2141 }
2142
2143 // Update controls state
2144 bool shouldAllowControls = GetAllowControlsTarget();
2145 if (shouldAllowControls != m_bAllowControls)
2146 {
2147 m_bAllowControls = shouldAllowControls;
2148 Replication.BumpMe();
2149 }
2150 }
2151
2152
2153 // Should we disable local player controls?
2154 bool allowControls = GetAllowControls();
2155 SetLocalControls(allowControls);
2156
2157 #ifdef GAME_MODE_DEBUG
2158 if (DiagMenu.GetBool(SCR_DebugMenuID.DEBUGUI_GAME_MODE))
2159 {
2160 Diag_DrawGameModeWindow();
2161 Diag_DrawComponentsWindow();
2162 Diag_DrawPlayersWindow();
2163 Diag_DrawControlledEntitiesWindow();
2164 }
2165 #endif
2166 }
2167
2168 //------------------------------------------------------------------------------------------------
2169 override void EOnInit(IEntity owner)
2170 {
2171 // Set Test Game Flags
2172 #ifdef WORKBENCH
2173 if (GetGame().GetWorldEntity() && !GetGame().AreGameFlagsObtained())
2174 {
2175 GetGame().SetGameFlags(m_eTestGameFlags, false);
2176 }
2177 #endif
2178
2179 //~ Remove any duplicate entries
2180 if (m_aDisabledResourceTypes.IsEmpty())
2181 {
2182 //~ TODO: Make this cleaner
2183
2184 set<EResourceType> duplicateRemoveSet = new set<EResourceType>();
2185
2186 foreach (EResourceType resourceType : m_aDisabledResourceTypes)
2187 {
2188 duplicateRemoveSet.Insert(resourceType);
2189 }
2190
2192 foreach (EResourceType resourceType : duplicateRemoveSet)
2193 {
2194 m_aDisabledResourceTypes.Insert(resourceType);
2195 }
2196 }
2197
2198 // Find required components
2199 m_RplComponent = RplComponent.Cast(owner.FindComponent(RplComponent));
2200 m_pRespawnSystemComponent = SCR_RespawnSystemComponent.Cast(owner.FindComponent(SCR_RespawnSystemComponent));
2203 m_pGameModeHealthSettings = SCR_GameModeHealthSettings.Cast(owner.FindComponent(SCR_GameModeHealthSettings));
2204
2205 if (!m_RplComponent)
2206 Print("SCR_BaseGameMode is missing RplComponent!", LogLevel.ERROR);
2208 Print("SCR_BaseGameMode is missing SCR_RespawnSystemComponent!", LogLevel.WARNING);
2209
2211 m_aAdditionalGamemodeComponents = new array<SCR_BaseGameModeComponent>();
2212
2213 array<Managed> additionalComponents = new array<Managed>();
2214 int count = owner.FindComponents(SCR_BaseGameModeComponent, additionalComponents);
2215
2217 for (int i = 0; i < count; i++)
2218 {
2219 SCR_BaseGameModeComponent comp = SCR_BaseGameModeComponent.Cast(additionalComponents[i]);
2221 }
2222
2223 // Find and sort state components
2224 array<Managed> stateComponents = new array<Managed>();
2225 int stateCount = owner.FindComponents(SCR_BaseGameModeStateComponent, stateComponents);
2226 for (int i = 0; i < stateCount; i++)
2227 {
2228 SCR_BaseGameModeStateComponent stateComponent = SCR_BaseGameModeStateComponent.Cast(stateComponents[i]);
2229 SCR_EGameModeState state = stateComponent.GetAffiliatedState();
2230 // Invalid state
2231 if (state < 0)
2232 {
2233 Print("Skipping one of SCR_BaseGameStateComponent(s), invalid affiliated state!", LogLevel.ERROR);
2234 continue;
2235 }
2236
2237 if (m_mStateComponents.Contains(state))
2238 {
2239 string stateName = SCR_Enum.GetEnumName(SCR_EGameModeState, state);
2240 Print("Skipping one of SCR_BaseGameStateComponent(s), duplicate component for state: " + stateName + "!", LogLevel.ERROR);
2241 continue;
2242 }
2243
2244 m_mStateComponents.Insert(state, stateComponent);
2245 }
2246 }
2247
2248 /*
2249 Preload handling
2250 */
2251
2252 //------------------------------------------------------------------------------------------------
2260
2261 //------------------------------------------------------------------------------------------------
2265 {
2266 m_SpawnPreload = SCR_SpawnPreload.PreloadSpawnPosition(position);
2268 m_OnPreloadFinished.Invoke();
2269
2270 }
2271
2272 //------------------------------------------------------------------------------------------------
2274 {
2275 return m_bAutotuneEnabled;
2276 }
2277
2278 //------------------------------------------------------------------------------------------------
2279 protected void HandleSpawnPreload(float timeSlice)
2280 {
2281 bool finished = m_SpawnPreload.Update(timeSlice);
2282 if (finished)
2283 {
2284 m_SpawnPreload = null;
2286 m_OnPreloadFinished.Invoke();
2287 }
2288 }
2289
2290 //------------------------------------------------------------------------------------------------
2291 // constructor
2293 {
2294 #ifdef GAME_MODE_DEBUG
2295 if (!s_DebugRegistered)
2296 {
2297 DiagMenu.RegisterMenu(SCR_DebugMenuID.DEBUGUI_GAME_MODE_MENU, "GameMode", "");
2298 DiagMenu.RegisterBool(SCR_DebugMenuID.DEBUGUI_GAME_MODE, "", "Game Mode", "GameMode");
2299 DiagMenu.RegisterBool(SCR_DebugMenuID.DEBUGUI_CHARACTER_KILLED_LOG, "", "Show kill log", "GameMode");
2300 s_DebugRegistered = true;
2301 }
2302 #endif
2303
2304 Activate();
2305 SetEventMask(EntityEvent.INIT | EntityEvent.FRAME);
2306 }
2307
2308 //------------------------------------------------------------------------------------------------
2309 // destructor
2311 {
2312 #ifdef GAME_MODE_DEBUG
2313 DiagMenu.Unregister(SCR_DebugMenuID.DEBUGUI_GAME_MODE);
2314 s_DebugRegistered = false;
2315 #endif
2316
2317 #ifdef GM_AI_STATS
2318 if (SCR_GameModeStatistics.IsRecording())
2319 SCR_GameModeStatistics.StopRecording();
2320 #endif
2321 }
2322
2323 #ifdef GM_AI_STATS
2324 //------------------------------------------------------------------------------------------------
2326 private bool IsGameModeStatisticsEnabled()
2327 {
2328 // not authority
2329 if (m_RplComponent && !m_RplComponent.IsMaster())
2330 return false;
2331
2332 return GetGame().InPlayMode();
2333 }
2334 #endif
2335}
SCR_EAIThreatSectorFlags flags
SCR_DebugMenuID
This enum contains all IDs for DiagMenu entries added in script.
Definition DebugMenuID.c:4
EGameOverTypes
ENotification
SCR_EPauseReason
Definition EPauseReason.c:2
EPlayerRole
Definition EPlayerRole.c:8
ArmaReforgerScripted GetGame()
Definition game.c:1398
bool AreGameFlagsObtained()
Definition game.c:508
EGameFlags
GameMode Game Flags represented by bit mask.
Definition game.c:17
RplMode
Mode of replication.
Definition RplMode.c:9
func SCR_BaseGameMode_OnPlayerRoleChanged
func SCR_BaseGameMode_OnPlayerDisconnected
ScriptInvokerBase< OnPreloadFinished > OnPreloadFinishedInvoker
func OnPreloadFinished
func SCR_BaseGameMode_PlayerId
func SCR_BaseGameMode_PlayerIdAndEntity
func SCR_BaseGameMode_OnControllableDestroyed
func SCR_BaseGameMode_OnResourceEnabledChanged
void SCR_BaseGameModeComponent(IEntityComponentSource src, IEntity ent, IEntity parent)
void Activate()
SCR_CacheNoteComponentClass ScriptComponentClass RplProp()] protected ref array< string > m_aLines
RplComponent m_RplComponent
vector position
SCR_DestructionSynchronizationComponentClass ScriptComponentClass int index
SCR_ECharacterControlType
What kind of controller the character or (in some cases vehicle) has, eg: AI, Player,...
SCR_EGameModeState
SCR_ESpawnResult
Get all prefabs that have the spawner data
Get all prefabs that have the spawner the given labels and are valid in the editor mode param catalogType Type to catalog to get prefabs from param editorMode Editor mode to get valid entries from param faction Faction(Optional)
void SCR_FactionManager(IEntitySource src, IEntity parent)
void SCR_LoadoutManager(IEntitySource src, IEntity parent)
UI Textures DeployMenu Briefing conflict_HintBanner_1_UI desc
void SCR_RespawnTimerComponent(IEntityComponentSource src, IEntity ent, IEntity parent)
enum EVehicleType IEntity
Backend Api instance.
Definition BackendApi.c:14
Diagnostic and developer menu system.
Definition DiagMenu.c:18
proto external Managed FindComponent(typename typeName)
proto external BaseWorld GetWorld()
proto external int FindComponents(typename typeName, notnull array< Managed > outComponents)
proto external IEntity GetParent()
Definition Math.c:13
Main replication API.
Definition Replication.c:14
Replication connection identity.
Definition RplIdentity.c:14
ref ScriptInvokerBase< SCR_BaseGameMode_OnPlayerDisconnected > m_OnPlayerDisconnected
ref array< EResourceType > m_aDisabledResourceTypes
bool m_bAutotuneEnabled
is the autotune radio feature enabled
ScriptInvoker GetOnGameEnd()
bool CanBePaused()
Return true if client is offline.
ScriptInvokerBase< SCR_BaseGameMode_PlayerId > GetOnPlayerAuditRevived()
void SetLocalControls(bool enabled)
SCR_GameModeHealthSettings m_pGameModeHealthSettings
ScriptInvokerBase< SCR_BaseGameMode_OnPlayerDisconnected > GetOnPostCompPlayerDisconnected()
override void OnPlayerConnected(int playerId)
ref ScriptInvoker m_OnControllableSpawned
SCR_EGameModeState GetState()
ref ScriptInvokerBase< SCR_BaseGameMode_PlayerIdAndEntity > m_OnPlayerSpawned
WorldTimestamp GetGameEndTimeStamp()
EGameFlags m_eTestGameFlags
bool PauseGame(bool pause, SCR_EPauseReason reason=SCR_EPauseReason.SYSTEM)
ScriptInvoker GetOnControllableSpawned()
sealed bool IsMaster()
void OnGameModeEnd(SCR_GameModeEndData endData)
ref ScriptInvokerBase< SCR_BaseGameMode_OnResourceEnabledChanged > m_OnResourceTypeEnabledChanged
ref SCR_SpawnPreload m_SpawnPreload
override void OnControllableDeleted(IEntity entity)
override void OnPlayerRoleChange(int playerId, EPlayerRole roleFlags)
override void OnPlayerAuditSuccess(int iPlayerID)
ref ScriptInvokerBase< SCR_BaseGameMode_OnControllableDestroyed > m_OnPlayerKilled
override void OnControllableDestroyed(IEntity entity, IEntity killerEntity, notnull Instigator instigator)
int GetPlayerRemainingRespawnTime(int playerID)
Returns remaining respawn time in seconds for given player.
ScriptInvoker GetOnControllableDeleted()
ref ScriptInvokerBase< SCR_BaseGameMode_PlayerId > m_OnPlayerAuditFail
RplComponent m_RplComponent
ScriptInvokerBase< SCR_BaseGameMode_OnPlayerDisconnected > GetOnPlayerDisconnected()
ScriptInvoker GetOnWorldPostProcess()
override bool RplSave(ScriptBitWriter writer)
bool IsResourceTypeEnabled(EResourceType resourceType=EResourceType.SUPPLIES)
ScriptInvokerBase< SCR_BaseGameMode_PlayerId > GetOnPlayerAuditSuccess()
bool CanPlayerSpawn_S(SCR_SpawnRequestComponent requestComponent, SCR_SpawnHandlerComponent handlerComponent, SCR_SpawnData data, out SCR_ESpawnResult result=SCR_ESpawnResult.SPAWN_NOT_ALLOWED)
ref ScriptInvokerBase< SCR_BaseGameMode_PlayerId > m_OnPlayerRegistered
ref ScriptInvoker m_OnGameEnd
SCR_RespawnTimerComponent m_RespawnTimerComponent
override void OnPlayerRegistered(int playerId)
ref ScriptInvokerBase< SCR_BaseGameMode_PlayerId > m_OnPlayerAuditSuccess
SCR_BaseScoringSystemComponent m_ScoringSystemComponent
void EndGameMode(SCR_GameModeEndData endData)
ref ScriptInvoker m_OnControllableDeleted
ScriptInvokerBase< SCR_BaseGameMode_PlayerIdAndEntity > GetOnPlayerSpawned()
ref ScriptInvokerBase< SCR_BaseGameMode_PlayerIdAndEntity > m_OnPlayerDeleted
ref ScriptInvokerBase< SCR_BaseGameMode_OnPlayerRoleChanged > m_OnPlayerRoleChange
void OnPlayerLoadoutSet_S(SCR_PlayerLoadoutComponent loadoutComponent, SCR_BasePlayerLoadout loadout)
ref ScriptInvokerBase< SCR_BaseGameMode_PlayerId > m_OnPlayerConnected
ref ScriptInvoker m_OnGameModeEnd
void ConsumeMilitarySupplyAllocationOnPlayerSpawn_S(SCR_SpawnRequestComponent requestComponent, IEntity spawnPoint, SCR_PlayerLoadoutComponent loadoutComponent)
ref ScriptInvokerBase< SCR_BaseGameMode_PlayerId > m_OnPlayerAuditRevived
override void EOnInit(IEntity owner)
void LogKillEvent(notnull SCR_InstigatorContextData instigatorContext)
SCR_EPauseReason m_ePauseReasons
void ConsumeSuppliesOnPlayerSpawn_S(int playerID, IEntity spawnPoint, SCR_PlayerLoadoutComponent loadoutComp)
ScriptInvokerBase< SCR_BaseGameMode_PlayerIdAndEntity > GetOnPlayerDeleted()
void SCR_BaseGameMode(IEntitySource src, IEntity parent)
void OnPlayerEntityChanged_S(int playerId, IEntity previousEntity, IEntity newEntity)
void OnPlayerKilledHandled(int playerId, IEntity playerEntity, IEntity killerEntity, notnull Instigator killer)
Called after player kill behaviour is handled by a component overriding the generic logic.
ScriptInvokerBase< SCR_BaseGameMode_PlayerId > GetOnPlayerConnected()
ref ScriptInvokerBase< SCR_BaseGameMode_OnPlayerDisconnected > m_OnPostCompPlayerDisconnected
void HandleSpawnPreload(float timeSlice)
ref map< int, vector > m_mPlayerSpawnPosition
Used on server to respawn player on their original position after reconnecting.
ScriptInvokerBase< SCR_BaseGameMode_PlayerId > GetOnPlayerAuditFail()
override bool RplLoad(ScriptBitReader reader)
override void OnPlayerAuditFail(int iPlayerID)
override void OnPlayerAuditRevived(int iPlayerID)
bool m_bUseSpawnPreload
Spawn location preload.
override event void OnWorldPostProcess(World world)
void HandleOnTasksInitialized()
Called once tasks are initialized.
ScriptInvokerBase< SCR_BaseGameMode_OnControllableDestroyed > GetOnControllableDestroyed()
ScriptInvoker GetOnGameStart()
bool m_bAllowControls
If false, controls are disable for the time being.
SCR_GameModeHealthSettings GetGameModeHealthSettings()
SCR_RespawnSystemComponent m_pRespawnSystemComponent
WorldTimestamp m_GameEndTimeStamp
void OnSpawnPlayerEntityFailure_S(SCR_SpawnRequestComponent requestComponent, SCR_SpawnHandlerComponent handlerComponent, IEntity entity, SCR_SpawnData data, SCR_ESpawnResult reason)
override void OnPlayerAuditTimeouted(int iPlayerID)
ScriptInvokerBase< SCR_BaseGameMode_OnControllableDestroyed > GetOnPlayerKilled()
OnPreloadFinishedInvoker GetOnPreloadFinished()
SCR_BaseScoringSystemComponent GetScoringSystemComponent()
void OnControllableDestroyedEx(notnull SCR_InstigatorContextData instigatorContextData)
ScriptInvokerBase< SCR_BaseGameMode_PlayerId > GetOnPlayerRegistered()
ScriptInvoker GetOnGameModeEnd()
float m_fLastTimeCorrection
Last timestamp of sent time correction for the server.
void StartSpawnPreload(vector position)
bool PreparePlayerEntity_S(SCR_SpawnRequestComponent requestComponent, SCR_SpawnHandlerComponent handlerComponent, SCR_SpawnData data, IEntity entity)
override void OnGameStart()
ref ScriptInvokerBase< SCR_BaseGameMode_OnControllableDestroyed > m_OnControllableDestroyed
SCR_BaseGameModeStateComponent GetStateComponent(SCR_EGameModeState state)
ref ScriptInvokerBase< SCR_BaseGameMode_PlayerId > m_OnPlayerAuditTimeouted
int GetDisabledResourceTypes(inout notnull array< EResourceType > disabledResourceTypes)
override bool HandlePlayerKilled(int playerId, IEntity playerEntity, IEntity killerEntity, notnull Instigator killer)
override void OnPlayerDisconnected(int playerId, KickCauseCode cause, int timeout)
void OnPlayerSpawnOnPoint_S(SCR_SpawnRequestComponent requestComponent, SCR_SpawnHandlerComponent handlerComponent, IEntity entity, SCR_SpawnPointSpawnData spawnPointData)
bool FilterCallback(IEntity e)
ScriptInvokerBase< SCR_BaseGameMode_OnPlayerRoleChanged > GetOnPlayerRoleChange()
ref OnPreloadFinishedInvoker m_OnPreloadFinished
SCR_GameModeEndData GetEndGameData()
ref array< SCR_BaseGameModeComponent > m_aAdditionalGamemodeComponents
void HandleOnGameModeEndSaveData()
void CachePlayerSpawnPosition(int playerID, vector position)
void OnPlayerFactionSet_S(SCR_PlayerFactionAffiliationComponent factionComponent, Faction faction)
ref ScriptInvoker m_OnWorldPostProcess
override void EOnFrame(IEntity owner, float timeSlice)
void OnPlayerKilledEx(notnull SCR_InstigatorContextData instigatorContextData)
sealed bool TryShutdownServer()
override void OnPlayerKilled(int playerId, IEntity playerEntity, IEntity killerEntity, notnull Instigator killer)
Default player kill behaviour. Called when a player is killed (and HandlePlayerKilled returns true).
void OnPlayerSpawnFinalize_S(SCR_SpawnRequestComponent requestComponent, SCR_SpawnHandlerComponent handlerComponent, SCR_SpawnData data, IEntity entity)
ref ScriptInvoker Event_OnGameStart
ref map< SCR_EGameModeState, SCR_BaseGameModeStateComponent > m_mStateComponents
Map of components per state.
ScriptInvokerBase< SCR_BaseGameMode_OnResourceEnabledChanged > GetOnResourceTypeEnabledChanged()
void SetResourceTypeEnabled(bool enable, EResourceType resourceType=EResourceType.SUPPLIES, int playerID=-1)
ScriptInvokerBase< SCR_BaseGameMode_PlayerId > GetOnPlayerAuditTimeouted()
SCR_RespawnSystemComponent GetRespawnSystemComponent()
override void OnControllableSpawned(IEntity entity)
bool m_bIsHosted
Is the session hosted by a player?
float m_fTimeCorrectionInterval
Interval of time synchronization in seconds.
void OnResourceTypeEnabledChanged()
\Called when Global Supplies is set to enabled or disabled (Server and client)
void OnPlayerDeleted(int playerId, IEntity player)
SCR_ECharacterDeathStatusRelations GetVictimKillerRelation()
SCR_ECharacterDisguiseType GetVictimDisguiseType()
SCR_ECharacterControlType GetKillerCharacterControlType()
SCR_ECharacterDisguiseType GetKillerDisguiseType()
SCR_ECharacterControlType GetVictimCharacterControlType()
bool HandlePlayerDisconnect(int playerId, KickCauseCode cause)
Returns true if the players entity has been taken over for reconnection.
static SCR_ReconnectComponent GetInstance()
Definition World.c:16
Definition Types.c:486
EPhysicsLayerPresets
Enum is filled by C++ by data in project config PhysicsSettings.LayerPresets.
Definition gameLib.c:19
proto void Print(void var, LogLevel level=LogLevel.NORMAL)
Prints content of variable to console/log.
LogLevel
Enum with severity of the logging message.
Definition LogLevel.c:14
proto void PrintFormat(string fmt, void param1=NULL, void param2=NULL, void param3=NULL, void param4=NULL, void param5=NULL, void param6=NULL, void param7=NULL, void param8=NULL, void param9=NULL, LogLevel level=LogLevel.NORMAL)
SCR_FieldOfViewSettings Attribute
EntityEvent
Various entity events.
Definition EntityEvent.c:14
EDamageType
Definition EDamageType.c:13
RplCondition
Conditional replication rule. Fine grained selection of receivers.
TraceFlags
Definition TraceFlags.c:13
ScriptInvokerBase< func > ScriptInvoker
Definition tools.c:134