@@ -19,9 +19,17 @@ public partial class EnemySpawner : Node
1919 private readonly RandomNumberGenerator _rng = new ( ) ;
2020 private readonly Dictionary < PackedScene , Stack < Enemy > > _enemyPoolByScene = new ( ) ;
2121 private readonly Dictionary < Enemy , PackedScene > _sceneByEnemy = new ( ) ;
22+ private readonly HashSet < Enemy > _activeEnemies = new ( ) ;
2223 private int _enemyNameCounter ;
2324 private Timer _spawnTimer ;
2425 private bool _isSpawningEnabled = true ;
26+ private float _baseSpawnIntervalMin ;
27+ private float _baseSpawnIntervalMax ;
28+ private float _spawnRateMultiplier = 1f ;
29+ private float _spawnCountMultiplier = 1f ;
30+ private float _enemySpeedMultiplier = 1f ;
31+ private float _enemyHealthMultiplier = 1f ;
32+ private float _enemyScoreMultiplier = 1f ;
2533
2634 public override void _Ready ( )
2735 {
@@ -40,6 +48,9 @@ public override void _Ready()
4048 Autostart = false
4149 } ;
4250
51+ _baseSpawnIntervalMin = Mathf . Max ( 0.05f , _spawnIntervalMin ) ;
52+ _baseSpawnIntervalMax = Mathf . Max ( _baseSpawnIntervalMin , _spawnIntervalMax ) ;
53+
4354 AddChild ( _spawnTimer ) ;
4455 _spawnTimer . Timeout += OnSpawnTimerTimeout ;
4556
@@ -64,7 +75,12 @@ private void OnSpawnTimerTimeout()
6475 return ;
6576 }
6677
67- SpawnEnemy ( ) ;
78+ int spawnCount = CalculateSpawnCount ( ) ;
79+ for ( int i = 0 ; i < spawnCount ; i ++ )
80+ {
81+ SpawnEnemy ( ) ;
82+ }
83+
6884 ScheduleNextSpawn ( ) ;
6985 }
7086
@@ -75,13 +91,40 @@ private void ScheduleNextSpawn()
7591 return ;
7692 }
7793
78- float minInterval = Mathf . Max ( 0.05f , Math . Min ( _spawnIntervalMin , _spawnIntervalMax ) ) ;
79- float maxInterval = Mathf . Max ( minInterval , Math . Max ( _spawnIntervalMin , _spawnIntervalMax ) ) ;
94+ float baseMinInterval = Mathf . Max ( 0.05f , Math . Min ( _baseSpawnIntervalMin , _baseSpawnIntervalMax ) ) ;
95+ float baseMaxInterval = Mathf . Max ( baseMinInterval , Math . Max ( _baseSpawnIntervalMin , _baseSpawnIntervalMax ) ) ;
96+ float safeRateMultiplier = Mathf . Max ( 0.1f , _spawnRateMultiplier ) ;
97+ float minInterval = Mathf . Max ( 0.05f , baseMinInterval / safeRateMultiplier ) ;
98+ float maxInterval = Mathf . Max ( minInterval , baseMaxInterval / safeRateMultiplier ) ;
8099
81100 _spawnTimer . WaitTime = _rng . RandfRange ( minInterval , maxInterval ) ;
82101 _spawnTimer . Start ( ) ;
83102 }
84103
104+ public void SetDifficultyMultipliers (
105+ float spawnRateMultiplier ,
106+ float spawnCountMultiplier ,
107+ float enemySpeedMultiplier ,
108+ float enemyHealthMultiplier ,
109+ float enemyScoreMultiplier )
110+ {
111+ _spawnRateMultiplier = Mathf . Clamp ( spawnRateMultiplier , 0.4f , 4.0f ) ;
112+ _spawnCountMultiplier = Mathf . Clamp ( spawnCountMultiplier , 1.0f , 4.0f ) ;
113+ _enemySpeedMultiplier = Mathf . Clamp ( enemySpeedMultiplier , 0.5f , 4.0f ) ;
114+ _enemyHealthMultiplier = Mathf . Clamp ( enemyHealthMultiplier , 0.5f , 5.0f ) ;
115+ _enemyScoreMultiplier = Mathf . Clamp ( enemyScoreMultiplier , 0.5f , 5.0f ) ;
116+
117+ foreach ( Enemy activeEnemy in _activeEnemies )
118+ {
119+ if ( ! GodotObject . IsInstanceValid ( activeEnemy ) )
120+ {
121+ continue ;
122+ }
123+
124+ activeEnemy . ApplyDifficulty ( _enemySpeedMultiplier , _enemyHealthMultiplier , _enemyScoreMultiplier ) ;
125+ }
126+ }
127+
85128 public void SetSpawningEnabled ( bool isEnabled )
86129 {
87130 if ( _isSpawningEnabled == isEnabled )
@@ -184,6 +227,9 @@ private Enemy AcquireEnemy(PackedScene enemyScene, Node targetParent)
184227 }
185228 }
186229
230+ enemy . ApplyDifficulty ( _enemySpeedMultiplier , _enemyHealthMultiplier , _enemyScoreMultiplier ) ;
231+ _activeEnemies . Add ( enemy ) ;
232+
187233 return enemy ;
188234 }
189235
@@ -212,6 +258,7 @@ private void ReturnEnemyToPool(Enemy enemy)
212258
213259 if ( ! _sceneByEnemy . TryGetValue ( enemy , out PackedScene enemyScene ) || enemyScene == null )
214260 {
261+ _activeEnemies . Remove ( enemy ) ;
215262 enemy . QueueFree ( ) ;
216263 return ;
217264 }
@@ -222,6 +269,7 @@ private void ReturnEnemyToPool(Enemy enemy)
222269 _enemyPoolByScene [ enemyScene ] = scenePool ;
223270 }
224271
272+ _activeEnemies . Remove ( enemy ) ;
225273 enemy . DeactivateForPool ( ) ;
226274 scenePool . Push ( enemy ) ;
227275 }
@@ -299,4 +347,18 @@ private EnemyTargetLane PickTargetLane(EnemySpawnOrigin spawnOrigin)
299347 return ( EnemyTargetLane ) _rng . RandiRange ( 0 , 2 ) ;
300348 }
301349 }
350+
351+ private int CalculateSpawnCount ( )
352+ {
353+ float safeMultiplier = Mathf . Max ( 1f , _spawnCountMultiplier ) ;
354+ int guaranteedCount = Math . Max ( 1 , Mathf . FloorToInt ( safeMultiplier ) ) ;
355+ float extraSpawnChance = Mathf . Clamp ( safeMultiplier - guaranteedCount , 0f , 1f ) ;
356+
357+ if ( _rng . Randf ( ) < extraSpawnChance )
358+ {
359+ guaranteedCount ++ ;
360+ }
361+
362+ return guaranteedCount ;
363+ }
302364}
0 commit comments