55using LabExtended . Events . Round ;
66
77using LabExtended . Core ;
8+ using LabExtended . Extensions ;
89using LabExtended . Utilities . Update ;
910
1011using System . Diagnostics ;
1112using System . ComponentModel ;
1213
1314using YamlDotNet . Serialization ;
15+ using System . Text ;
1416
1517namespace LabExtended . API . Custom . Gamemodes
1618{
@@ -22,132 +24,143 @@ public abstract class CustomGamemode : CustomObject<CustomGamemode>
2224 private static PlayerUpdateComponent updateComponent = PlayerUpdateComponent . Create ( ) ;
2325
2426 /// <summary>
25- /// Gets a queue of gamemodes to be activated next .
27+ /// Gets the curently enabled gamemode .
2628 /// </summary>
27- public static Queue < CustomGamemode > Queue { get ; } = new ( ) ;
29+ public static List < CustomGamemode > Active { get ; } = new ( ) ;
30+
31+ private Stopwatch runTimeWatch = new ( ) ;
2832
2933 /// <summary>
30- /// Gets the curently enabled gamemode.
34+ /// Whether or not the gamemode is currently active.
35+ /// </summary>
36+ [ YamlIgnore ]
37+ public bool IsActive => runTimeWatch . IsRunning ;
38+
39+ /// <summary>
40+ /// Gets or sets a value indicating whether the game mode can be activated after a round has already started.
3141 /// </summary>
32- public static CustomGamemode ? Current { get ; private set ; }
42+ [ Description ( "Whether or not the gamemode can be started in the middle of the round." ) ]
43+ public virtual bool CanActivateMidRound { get ; set ; } = true ;
3344
3445 /// <summary>
35- /// Enables the specified gamemode as the current active gamemode.
46+ /// Gets or sets a value indicating whether the gamemode is automatically disabled when the round ends .
3647 /// </summary>
37- /// <remarks>
38- /// If the specified gamemode is already active or cannot be activated mid-round, the method returns without enabling the gamemode.
39- /// If an override is not allowed and a gamemode is already active, the method does not enable the new gamemode.
40- /// </remarks>
41- /// <param name="gamemode">The gamemode instance to enable.</param>
42- /// <param name="overrideCurrent">Indicates whether to allow overriding the currently active gamemode if one is active.</param>
43- /// <returns>
44- /// true if the gamemode is successfully enabled; otherwise, false.
45- /// </returns>
46- /// <exception cref="ArgumentNullException">Thrown when the provided gamemode instance is null.</exception>
47- public static bool Enable ( CustomGamemode gamemode , bool overrideCurrent = false )
48- {
49- if ( gamemode is null )
50- throw new ArgumentNullException ( nameof ( gamemode ) ) ;
51-
52- if ( Current != null && Current == gamemode )
48+ [ Description ( "Whether or not the gamemode should be automatically disabled when the round ends." ) ]
49+ public virtual bool ShouldDisableOnRoundEnd { get ; set ; } = true ;
50+
51+ /// <summary>
52+ /// Gets or sets a value indicating whether the gamemode prevents the default wave spawns from occurring.
53+ /// </summary>
54+ [ Description ( "Whether or not the gamemode prevents the default wave spawns from occurring." ) ]
55+ public virtual bool PreventWaveSpawns { get ; set ; }
56+
57+ /// <summary>
58+ /// Gets or sets the list of gamemode identifiers that are incompatible with this gamemode.
59+ /// </summary>
60+ [ Description ( "A list of gamemode IDs that are incompatible with this gamemode." ) ]
61+ public virtual string [ ] IncompatibleGamemodes { get ; set ; } = [ ] ;
62+
63+ /// <summary>
64+ /// Gets the amount of time that has elapsed since the gamemode has started.
65+ /// </summary>
66+ [ YamlIgnore ]
67+ public TimeSpan RunTime => runTimeWatch . Elapsed ;
68+
69+ /// <summary>
70+ /// Gets the date and time at which the current run started.
71+ /// </summary>
72+ [ YamlIgnore ]
73+ public DateTime StartTime => DateTime . Now - runTimeWatch . Elapsed ;
74+
75+ /// <summary>
76+ /// Enables the current instance if it is not already active and can be enabled.
77+ /// </summary>
78+ /// <returns>true if the instance was successfully enabled; otherwise, false.</returns>
79+ public bool Enable ( )
80+ {
81+ if ( IsActive )
5382 return false ;
5483
55- if ( ! gamemode . CanActivateMidRound && ! ExRound . IsWaitingForPlayers )
56- {
57- var list = Queue . ToList ( ) ;
58-
59- list . Insert ( 0 , gamemode ) ;
60-
61- Queue . Clear ( ) ;
62-
63- list . ForEach ( Queue . Enqueue ) ;
64- list . Clear ( ) ;
65-
84+ if ( ! CanBeEnabled ( Active ) )
6685 return false ;
67- }
6886
69- if ( Current != null )
87+ if ( ! Active . AddUnique ( this ) )
7088 {
71- if ( ! overrideCurrent )
72- return false ;
73-
74- if ( ! Disable ( ) )
75- return false ;
89+ runTimeWatch . Restart ( ) ;
90+ return false ;
7691 }
7792
78- Current = gamemode ;
79- Current . OnEnabled ( ) ;
93+ runTimeWatch . Restart ( ) ;
8094
81- Current . IsActive = true ;
95+ ApiLog . Info ( "Custom Gamemodes" , $ "Enabled custom gamemode &3{ Id } &r!") ;
96+
97+ OnEnabled ( ) ;
8298 return true ;
8399 }
84100
85101 /// <summary>
86- /// Disables the current gamemode if one is active.
102+ /// Disables the current instance if it is active.
87103 /// </summary>
88- /// <remarks>If no current gamemode is active, the method performs no action and returns false.
89- /// After disabling, the current gamemode is set to null and will no longer be considered active.</remarks>
90- /// <returns>true if an active gamemode was disabled; otherwise, false.</returns>
91- public static bool Disable ( )
104+ /// <returns>true if the instance was active and is now disabled; otherwise, false.</returns>
105+ public bool Disable ( )
92106 {
93- if ( Current == null )
107+ if ( ! IsActive )
94108 return false ;
95109
96- var current = Current ;
110+ Active . Remove ( this ) ;
97111
98- Current = null ;
112+ runTimeWatch . Stop ( ) ;
113+ runTimeWatch . Reset ( ) ;
99114
100- current . IsActive = false ;
101- current . OnDisabled ( ) ;
115+ ApiLog . Info ( "Custom Gamemodes" , $ "Disabled custom gamemode &3{ Id } &r!") ;
102116
117+ OnDisabled ( ) ;
103118 return true ;
104119 }
105120
106- private Stopwatch runTimeWatch = new ( ) ;
107-
108121 /// <summary>
109- /// Whether or not the gamemode is currently active.
122+ /// Determines whether or not the gamemode can be enabled given the other currently active gamemodes .
110123 /// </summary>
111- [ YamlIgnore ]
112- public bool IsActive { get ; private set ; }
124+ /// <param name="otherModes">List of other active modes.</param>
125+ /// <returns><c>true</c> if the gamemode can be enabled; otherwise, <c>false</c>.</returns>
126+ public virtual bool CanBeEnabled ( List < CustomGamemode > otherModes )
127+ {
128+ if ( IsActive )
129+ return false ;
113130
114- /// <summary>
115- /// Gets or sets a value indicating whether the game mode can be activated after a round has already started.
116- /// </summary>
117- [ Description ( "Whether or not the gamemode can be started in the middle of the round." ) ]
118- public virtual bool CanActivateMidRound { get ; set ; } = true ;
131+ if ( ! CanActivateMidRound && ! ExRound . IsWaitingForPlayers )
132+ return false ;
119133
120- /// <summary>
121- /// Gets or sets a value indicating whether the gamemode prevents the default wave spawns from occurring.
122- /// </summary>
123- [ Description ( "Whether or not the gamemode prevents the default wave spawns from occurring." ) ]
124- public virtual bool PreventWaveSpawns { get ; set ; } = true ;
134+ if ( IncompatibleGamemodes ? . Length > 0 && otherModes . Any ( x => IncompatibleGamemodes . Contains ( x . Id ) ) )
135+ return false ;
125136
126- /// <summary>
127- /// Gets the amount of time that has elapsed since the gamemode has started.
128- /// </summary>
129- [ YamlIgnore ]
130- public TimeSpan RunTime => runTimeWatch . Elapsed ;
137+ return true ;
138+ }
131139
132140 /// <summary>
133141 /// Gets called when the gamemode is enabled.
134142 /// </summary>
135143 public virtual void OnEnabled ( )
136144 {
137- runTimeWatch . Restart ( ) ;
138-
139- ApiLog . Info ( "Custom Gamemodes" , $ "Enabled custom gamemode &3{ Id } &r!") ;
145+
140146 }
141147
142148 /// <summary>
143149 /// Gets called when the gamemode is disabled.
144150 /// </summary>
145151 public virtual void OnDisabled ( )
146152 {
147- runTimeWatch . Stop ( ) ;
148- runTimeWatch . Reset ( ) ;
149-
150- ApiLog . Info ( "Custom Gamemodes" , $ "Disabled custom gamemode &3{ Id } &r!") ;
153+
154+ }
155+
156+ /// <summary>
157+ /// Appends a textual representation of the current gamemode's state to the specified <see cref="StringBuilder"/>
158+ /// instance.
159+ /// </summary>
160+ /// <param name="builder">The <see cref="StringBuilder"/> instance to which the gamemode's state will be appended. Cannot be null.</param>
161+ public virtual void PrintState ( StringBuilder builder )
162+ {
163+
151164 }
152165
153166 /// <inheritdoc/>
@@ -163,7 +176,7 @@ public override void OnUnregistered()
163176 {
164177 base . OnUnregistered ( ) ;
165178
166- if ( Current != null && Current == this )
179+ if ( IsActive )
167180 Disable ( ) ;
168181
169182 ApiLog . Info ( "Custom Gamemodes" , $ "&1Unregistered&r custom gamemode &3{ Id } &r") ;
@@ -210,7 +223,10 @@ public virtual void OnRoundEnded(RoundEndedEventArgs args)
210223 /// </summary>
211224 public virtual void OnRoundRestarting ( )
212225 {
213-
226+ if ( ShouldDisableOnRoundEnd && IsActive )
227+ {
228+ Disable ( ) ;
229+ }
214230 }
215231
216232 /// <summary>
@@ -322,99 +338,87 @@ public virtual void OnWaveSpawned(WaveRespawnedEventArgs args)
322338
323339 private static void _WaitingForPlayers ( )
324340 {
325- while ( Current == null && Queue . TryDequeue ( out var gamemode ) )
326- {
327- if ( ! Enable ( gamemode , true ) )
328- {
329- ApiLog . Warn ( "Custom Gamemodes" , $ "Queued gamemode &1{ gamemode . Id } &r could not be enabled!") ;
330- }
331- else
332- {
333- break ;
334- }
335- }
336-
337- Current ? . OnWaitingForPlayers ( ) ;
341+ Active . ForEach ( x => x . OnWaitingForPlayers ( ) ) ;
338342 }
339343
340344 private static void _Update ( )
341345 {
342- Current ? . OnUpdate ( ) ;
346+ Active . ForEach ( x => x . OnUpdate ( ) ) ;
343347 }
344348
345349 private static void _RoundCheckingEndConditions ( RoundEndingConditionsCheckEventArgs args )
346350 {
347- Current ? . OnRoundCheckingEndConditions ( args ) ;
351+ Active . ForEach ( x => x . OnRoundCheckingEndConditions ( args ) ) ;
348352 }
349353
350354 private static void _RoundEnding ( RoundEndingEventArgs args )
351355 {
352- Current ? . OnRoundEnding ( args ) ;
356+ Active . ForEach ( x => x . OnRoundEnding ( args ) ) ;
353357 }
354358
355359 private static void _RoundEnded ( RoundEndedEventArgs args )
356360 {
357- Current ? . OnRoundEnded ( args ) ;
361+ Active . ForEach ( x => x . OnRoundEnded ( args ) ) ;
358362 }
359363
360364 private static void _RoundRestarting ( )
361365 {
362- Current ? . OnRoundRestarting ( ) ;
366+ Active . ForEach ( x => x . OnRoundRestarting ( ) ) ;
363367 }
364368
365369 private static void _RoundStarted ( )
366370 {
367- Current ? . OnRoundStarted ( ) ;
371+ Active . ForEach ( x => x . OnRoundStarted ( ) ) ;
368372 }
369373
370374 private static void _AssigningRoles ( AssigningRolesEventArgs args )
371375 {
372- Current ? . OnAssigningRoles ( args ) ;
376+ Active . ForEach ( x => x . OnAssigningRoles ( args ) ) ;
373377 }
374378
375379 private static void _AssignedRoles ( AssignedRolesEventArgs args )
376380 {
377- Current ? . OnAssignedRoles ( args ) ;
381+ Active . ForEach ( x => x . OnAssignedRoles ( args ) ) ;
378382 }
379383
380384 private static void _LateJoinSettingRole ( LateJoinSettingRoleEventArgs args )
381385 {
382- Current ? . OnLateJoinSettingRole ( args ) ;
386+ Active . ForEach ( x => x . OnLateJoinSettingRole ( args ) ) ;
383387 }
384388
385389 private static void _LateJoinSetRole ( LateJoinSetRoleEventArgs args )
386390 {
387- Current ? . OnLateJoinSetRole ( args ) ;
391+ Active . ForEach ( x => x . OnLateJoinSetRole ( args ) ) ;
388392 }
389393
390394 private static void _PlayerJoined ( ExPlayer player )
391395 {
392- Current ? . OnPlayerJoined ( player ) ;
396+ Active . ForEach ( x => x . OnPlayerJoined ( player ) ) ;
393397 }
394398
395399 private static void _PlayerLeft ( ExPlayer player )
396400 {
397- Current ? . OnPlayerLeft ( player ) ;
401+ Active . ForEach ( x => x . OnPlayerLeft ( player ) ) ;
398402 }
399403
400404 private static void _WaveSelecting ( WaveTeamSelectingEventArgs args )
401405 {
402- Current ? . OnWaveSelecting ( args ) ;
406+ Active . ForEach ( x => x . OnWaveSelecting ( args ) ) ;
403407 }
404408
405409 private static void _WaveSelected ( WaveTeamSelectedEventArgs args )
406- {
407- Current ? . OnWaveSelected ( args ) ;
410+ {
411+ Active . ForEach ( x => x . OnWaveSelected ( args ) ) ;
408412 }
409413
410414 private static void _WaveSpawning ( WaveRespawningEventArgs args )
411415 {
412- Current ? . OnWaveSpawning ( args ) ;
416+ Active . ForEach ( x => x . OnWaveSpawning ( args ) ) ;
413417 }
414418
415419 private static void _WaveSpawned ( WaveRespawnedEventArgs args )
416420 {
417- Current ? . OnWaveSpawned ( args ) ;
421+ Active . ForEach ( x => x . OnWaveSpawned ( args ) ) ;
418422 }
419423
420424 internal static void _Init ( )
0 commit comments