r-type  0.0.0
R-Type main
Loading...
Searching...
No Matches
WaveManager.cpp
Go to the documentation of this file.
1/// @file WaveManager.cpp
2/// @brief Implementation of wave-based enemy spawning system
3/// @namespace gme
4///
5
7#include <algorithm>
8#include <iostream>
9
10namespace gme
11{
12
14 : m_entityManager(entityManager), m_currentWaveIndex(-1), m_waveTimer(0.0f), m_spawnTimer(0.0f),
15 m_active(false), m_completed(false), m_waveInProgress(false), m_initialDelayPassed(false),
16 m_initialDelayTimer(0.0f)
17 {
18 }
19
20 void WaveManager::update(ecs::Registry &registry, const float dt, const int screenWidth)
21 {
22 if (!m_active || m_completed)
23 {
24 return;
25 }
26
27 // Handle initial delay (sync with client stage spawn)
29 {
32 {
34 std::cout << "[WaveManager] Initial delay complete, waves starting!" << std::endl;
35 }
36 else
37 {
38 return; // Wait for initial delay
39 }
40 }
41
42 // Check if we need to start the first wave
43 if (m_currentWaveIndex < 0)
44 {
46 return;
47 }
48
49 // Update wave timer
50 m_waveTimer += dt;
51 m_spawnTimer += dt;
52
53 // Process enemy spawns for current wave
54 if (m_waveInProgress && m_currentWaveIndex < static_cast<int>(m_waves.size()))
55 {
56 processSpawns(dt, screenWidth);
57
58 const Wave &currentWave = m_waves[m_currentWaveIndex];
59
60 // Check if we should advance to next wave
61 bool shouldAdvance = false;
62
63 if (currentWave.waitForClear)
64 {
65 // Wait for all enemies to be cleared
66 if (isWaveCleared())
67 {
68 shouldAdvance = true;
69 }
70 }
71 else
72 {
73 // Advance after duration expires
74 if (m_waveTimer >= currentWave.duration)
75 {
76 shouldAdvance = true;
77 }
78 }
79
80 if (shouldAdvance)
81 {
82 // Call completion callback if exists
83 if (currentWave.onComplete)
84 {
85 currentWave.onComplete();
86 }
87
88 std::cout << "[WaveManager] Wave " << (m_currentWaveIndex + 1) << " completed!" << std::endl;
89
90 // Move to next wave
91 if (m_currentWaveIndex + 1 < static_cast<int>(m_waves.size()))
92 {
94 }
95 else
96 {
97 // All waves completed
98 m_completed = true;
99 m_waveInProgress = false;
100 std::cout << "[WaveManager] All waves completed!" << std::endl;
101 }
102 }
103 }
104 }
105
107 {
108 m_active = true;
110 m_waveTimer = 0.0f;
111 m_spawnTimer = 0.0f;
112 m_completed = false;
113 m_waveInProgress = false;
114 m_initialDelayPassed = false;
115 m_initialDelayTimer = 0.0f;
116 std::cout << "[WaveManager] Wave system started with " << m_waves.size()
117 << " waves (initial delay: " << INITIAL_DELAY << "s)" << std::endl;
118 }
119
121 {
122 m_active = false;
124 m_waveTimer = 0.0f;
125 m_spawnTimer = 0.0f;
126 m_completed = false;
127 m_waveInProgress = false;
128 m_initialDelayPassed = false;
129 m_initialDelayTimer = 0.0f;
130 m_enemiesSpawned.clear();
131 std::cout << "[WaveManager] Wave system reset" << std::endl;
132 }
133
134 void WaveManager::addWave(const Wave &wave) { m_waves.push_back(wave); }
135
137 {
138 m_waves.clear();
139 reset();
140 }
141
143 {
144 clearWaves();
145 createWave1();
146 createWave2();
147 createWave3();
148 createWave4();
150 std::cout << "[WaveManager] Default waves created (" << m_waves.size() << " waves)" << std::endl;
151 }
152
154 {
156
157 if (m_currentWaveIndex >= static_cast<int>(m_waves.size()))
158 {
159 m_completed = true;
160 m_waveInProgress = false;
161 return;
162 }
163
164 const Wave &wave = m_waves[m_currentWaveIndex];
165
166 m_waveTimer = 0.0f;
167 m_spawnTimer = 0.0f;
168 m_waveInProgress = true;
169 m_enemiesSpawned.clear();
170 m_enemiesSpawned.resize(wave.enemies.size(), false);
171
172 std::cout << "[WaveManager] Starting wave " << (m_currentWaveIndex + 1) << " with " << wave.enemies.size()
173 << " enemies" << std::endl;
174 }
175
177 {
178 // Check if all spawned enemies are dead
179 const auto &enemies = m_entityManager.getEnemies();
180 return enemies.empty();
181 }
182
183 void WaveManager::processSpawns(const float dt, const int screenWidth)
184 {
185 if (m_currentWaveIndex < 0 || m_currentWaveIndex >= static_cast<int>(m_waves.size()))
186 {
187 return;
188 }
189
190 const Wave &currentWave = m_waves[m_currentWaveIndex];
191
192 // Check each enemy spawn in the current wave
193 for (size_t i = 0; i < currentWave.enemies.size(); ++i)
194 {
195 if (m_enemiesSpawned[i])
196 {
197 continue; // Already spawned
198 }
199
200 const EnemySpawn &spawn = currentWave.enemies[i];
201
202 // Check if it's time to spawn this enemy
203 if (m_spawnTimer >= spawn.spawnDelay)
204 {
205 // Calculate spawn position (right side of screen + offset)
206 float spawnX = static_cast<float>(screenWidth) + spawn.x;
207 float spawnY = spawn.y;
208
209 // Create the enemy based on type
210 switch (spawn.type)
211 {
213 m_entityManager.createBasicEnemy(spawnX, spawnY, spawn.health);
214 std::cout << "[WaveManager] Spawned Basic Enemy at (" << spawnX << ", " << spawnY << ")"
215 << std::endl;
216 break;
217
219 m_entityManager.createAdvancedEnemy(spawnX, spawnY, spawn.health);
220 std::cout << "[WaveManager] Spawned Advanced Enemy at (" << spawnX << ", " << spawnY << ")"
221 << std::endl;
222 break;
223
225 m_entityManager.createBoss(spawnX, spawnY, spawn.health);
226 std::cout << "[WaveManager] Spawned Boss at (" << spawnX << ", " << spawnY << ")" << std::endl;
227 break;
228
229 default:
230 std::cerr << "[WaveManager] Unknown enemy type in spawn!" << std::endl;
231 break;
232 }
233
234 m_enemiesSpawned[i] = true;
235 }
236 }
237 }
238
239 // ==================== Default Wave Creation ====================
240
242 {
243 Wave wave;
244 wave.waveNumber = 1;
245 wave.duration = 30.0f;
246 wave.waitForClear = false; // Continue after 30s regardless
247
248 // First group - 3 Basic enemies in vertical line
249 wave.enemies.push_back({ServerEntityType::ENEMY_BASIC, 50.0f, 150.0f, 50.0f, 2.0f});
250 wave.enemies.push_back({ServerEntityType::ENEMY_BASIC, 50.0f, 300.0f, 50.0f, 4.0f});
251 wave.enemies.push_back({ServerEntityType::ENEMY_BASIC, 50.0f, 450.0f, 50.0f, 6.0f});
252
253 // Second group - 2 Basic enemies from sides
254 wave.enemies.push_back({ServerEntityType::ENEMY_BASIC, 80.0f, 200.0f, 50.0f, 12.0f});
255 wave.enemies.push_back({ServerEntityType::ENEMY_BASIC, 80.0f, 400.0f, 50.0f, 12.0f});
256
257 // Third group - 3 Basic enemies in formation
258 wave.enemies.push_back({ServerEntityType::ENEMY_BASIC, 100.0f, 250.0f, 50.0f, 20.0f});
259 wave.enemies.push_back({ServerEntityType::ENEMY_BASIC, 60.0f, 300.0f, 50.0f, 21.0f});
260 wave.enemies.push_back({ServerEntityType::ENEMY_BASIC, 100.0f, 350.0f, 50.0f, 22.0f});
261
262 m_waves.push_back(wave);
263 }
264
266 {
267 Wave wave;
268 wave.waveNumber = 2;
269 wave.duration = 30.0f;
270 wave.waitForClear = false; // Continue after 30s regardless
271
272 // First wave - Advanced enemy with Basic support
273 wave.enemies.push_back({ServerEntityType::ENEMY_ADVANCED, 80.0f, 300.0f, 100.0f, 2.0f});
274 wave.enemies.push_back({ServerEntityType::ENEMY_BASIC, 40.0f, 200.0f, 60.0f, 3.0f});
275 wave.enemies.push_back({ServerEntityType::ENEMY_BASIC, 40.0f, 400.0f, 60.0f, 3.0f});
276
277 // Second wave - V formation of Basic enemies
278 wave.enemies.push_back({ServerEntityType::ENEMY_BASIC, 100.0f, 200.0f, 60.0f, 10.0f});
279 wave.enemies.push_back({ServerEntityType::ENEMY_BASIC, 60.0f, 250.0f, 60.0f, 10.5f});
280 wave.enemies.push_back({ServerEntityType::ENEMY_BASIC, 20.0f, 300.0f, 60.0f, 11.0f});
281 wave.enemies.push_back({ServerEntityType::ENEMY_BASIC, 60.0f, 350.0f, 60.0f, 11.5f});
282 wave.enemies.push_back({ServerEntityType::ENEMY_BASIC, 100.0f, 400.0f, 60.0f, 12.0f});
283
284 // Third wave - Two Advanced enemies
285 wave.enemies.push_back({ServerEntityType::ENEMY_ADVANCED, 90.0f, 250.0f, 100.0f, 20.0f});
286 wave.enemies.push_back({ServerEntityType::ENEMY_ADVANCED, 90.0f, 350.0f, 100.0f, 22.0f});
287
288 m_waves.push_back(wave);
289 }
290
292 {
293 Wave wave;
294 wave.waveNumber = 3;
295 wave.duration = 30.0f;
296 wave.waitForClear = false; // Continue after 30s
297
298 // First assault - Multiple Basic enemies
299 wave.enemies.push_back({ServerEntityType::ENEMY_BASIC, 50.0f, 150.0f, 70.0f, 1.0f});
300 wave.enemies.push_back({ServerEntityType::ENEMY_BASIC, 50.0f, 300.0f, 70.0f, 1.0f});
301 wave.enemies.push_back({ServerEntityType::ENEMY_BASIC, 50.0f, 450.0f, 70.0f, 1.0f});
302
303 // Second assault - Advanced enemies with escort
304 wave.enemies.push_back({ServerEntityType::ENEMY_ADVANCED, 100.0f, 200.0f, 120.0f, 8.0f});
305 wave.enemies.push_back({ServerEntityType::ENEMY_BASIC, 50.0f, 180.0f, 60.0f, 9.0f});
306 wave.enemies.push_back({ServerEntityType::ENEMY_ADVANCED, 100.0f, 400.0f, 120.0f, 10.0f});
307 wave.enemies.push_back({ServerEntityType::ENEMY_BASIC, 50.0f, 420.0f, 60.0f, 11.0f});
308
309 // Final assault - Mini-boss wave
310 wave.enemies.push_back({ServerEntityType::ENEMY_ADVANCED, 120.0f, 250.0f, 150.0f, 18.0f});
311 wave.enemies.push_back({ServerEntityType::ENEMY_ADVANCED, 120.0f, 350.0f, 150.0f, 18.0f});
312 wave.enemies.push_back({ServerEntityType::ENEMY_BASIC, 70.0f, 200.0f, 70.0f, 20.0f});
313 wave.enemies.push_back({ServerEntityType::ENEMY_BASIC, 70.0f, 300.0f, 70.0f, 20.0f});
314 wave.enemies.push_back({ServerEntityType::ENEMY_BASIC, 70.0f, 400.0f, 70.0f, 20.0f});
315
316 m_waves.push_back(wave);
317 }
318
320 {
321 Wave wave;
322 wave.waveNumber = 4;
323 wave.duration = 30.0f;
324 wave.waitForClear = false; // Continue after 30s
325
326 // Intense pre-boss wave with multiple formations
327 // First formation - Top and bottom pincer
328 wave.enemies.push_back({ServerEntityType::ENEMY_ADVANCED, 100.0f, 150.0f, 130.0f, 1.0f});
329 wave.enemies.push_back({ServerEntityType::ENEMY_BASIC, 50.0f, 180.0f, 70.0f, 2.0f});
330 wave.enemies.push_back({ServerEntityType::ENEMY_BASIC, 50.0f, 120.0f, 70.0f, 2.0f});
331
332 wave.enemies.push_back({ServerEntityType::ENEMY_ADVANCED, 100.0f, 450.0f, 130.0f, 1.0f});
333 wave.enemies.push_back({ServerEntityType::ENEMY_BASIC, 50.0f, 420.0f, 70.0f, 2.0f});
334 wave.enemies.push_back({ServerEntityType::ENEMY_BASIC, 50.0f, 480.0f, 70.0f, 2.0f});
335
336 // Second formation - Center reinforcements
337 wave.enemies.push_back({ServerEntityType::ENEMY_BASIC, 80.0f, 250.0f, 70.0f, 10.0f});
338 wave.enemies.push_back({ServerEntityType::ENEMY_BASIC, 80.0f, 300.0f, 70.0f, 10.5f});
339 wave.enemies.push_back({ServerEntityType::ENEMY_BASIC, 80.0f, 350.0f, 70.0f, 11.0f});
340 wave.enemies.push_back({ServerEntityType::ENEMY_ADVANCED, 120.0f, 300.0f, 150.0f, 12.0f});
341
342 // Third formation - Final assault before boss
343 wave.enemies.push_back({ServerEntityType::ENEMY_ADVANCED, 130.0f, 200.0f, 150.0f, 20.0f});
344 wave.enemies.push_back({ServerEntityType::ENEMY_ADVANCED, 130.0f, 400.0f, 150.0f, 20.0f});
345 wave.enemies.push_back({ServerEntityType::ENEMY_BASIC, 70.0f, 250.0f, 70.0f, 22.0f});
346 wave.enemies.push_back({ServerEntityType::ENEMY_BASIC, 70.0f, 300.0f, 70.0f, 22.5f});
347 wave.enemies.push_back({ServerEntityType::ENEMY_BASIC, 70.0f, 350.0f, 70.0f, 23.0f});
348
349 m_waves.push_back(wave);
350 }
351
353 {
354 Wave wave;
355 wave.waveNumber = 5;
356 wave.duration = 60.0f;
357 wave.waitForClear = true; // Must kill boss to win
358
359 // Boss spawn with initial escort
360 wave.enemies.push_back({ServerEntityType::BOSS, 150.0f, 300.0f, 1000.0f, 3.0f});
361
362 // Initial escort
363 wave.enemies.push_back({ServerEntityType::ENEMY_BASIC, 80.0f, 200.0f, 60.0f, 4.0f});
364 wave.enemies.push_back({ServerEntityType::ENEMY_BASIC, 80.0f, 400.0f, 60.0f, 4.0f});
365
366 // First wave of reinforcements (T+15s)
367 wave.enemies.push_back({ServerEntityType::ENEMY_ADVANCED, 100.0f, 250.0f, 100.0f, 15.0f});
368 wave.enemies.push_back({ServerEntityType::ENEMY_ADVANCED, 100.0f, 350.0f, 100.0f, 15.0f});
369
370 // Second wave of reinforcements (T+25s)
371 wave.enemies.push_back({ServerEntityType::ENEMY_BASIC, 60.0f, 180.0f, 60.0f, 25.0f});
372 wave.enemies.push_back({ServerEntityType::ENEMY_BASIC, 60.0f, 250.0f, 60.0f, 25.5f});
373 wave.enemies.push_back({ServerEntityType::ENEMY_BASIC, 60.0f, 350.0f, 60.0f, 26.0f});
374 wave.enemies.push_back({ServerEntityType::ENEMY_BASIC, 60.0f, 420.0f, 60.0f, 26.5f});
375
376 // Third wave of reinforcements (T+40s) - desperation attack
377 wave.enemies.push_back({ServerEntityType::ENEMY_ADVANCED, 120.0f, 200.0f, 120.0f, 40.0f});
378 wave.enemies.push_back({ServerEntityType::ENEMY_ADVANCED, 120.0f, 300.0f, 120.0f, 40.0f});
379 wave.enemies.push_back({ServerEntityType::ENEMY_ADVANCED, 120.0f, 400.0f, 120.0f, 40.0f});
380
381 // Final reinforcements (T+50s) - last stand
382 wave.enemies.push_back({ServerEntityType::ENEMY_BASIC, 50.0f, 150.0f, 70.0f, 50.0f});
383 wave.enemies.push_back({ServerEntityType::ENEMY_BASIC, 50.0f, 225.0f, 70.0f, 50.5f});
384 wave.enemies.push_back({ServerEntityType::ENEMY_BASIC, 50.0f, 300.0f, 70.0f, 51.0f});
385 wave.enemies.push_back({ServerEntityType::ENEMY_BASIC, 50.0f, 375.0f, 70.0f, 51.5f});
386 wave.enemies.push_back({ServerEntityType::ENEMY_BASIC, 50.0f, 450.0f, 70.0f, 52.0f});
387
388 m_waves.push_back(wave);
389 }
390
391} // namespace gme
Wave-based enemy spawning management system for R-Type server.
Class for managing entities and their components.
Definition Registry.hpp:25
Central entity lifecycle manager for the R-Type game server.
const std::unordered_map< std::uint32_t, ecs::Entity > & getEnemies() const
Get all enemy entities.
ecs::Entity createBoss(float x, float y, float health=1000.0f)
Create a boss enemy entity.
ecs::Entity createAdvancedEnemy(float x, float y, float health=100.0f)
Create an advanced enemy entity.
ecs::Entity createBasicEnemy(float x, float y, float health=50.0f)
Create a basic enemy entity.
bool m_waveInProgress
Whether a wave is currently spawning.
bool m_completed
Whether all waves are completed.
void startNextWave()
Start the next wave in sequence.
std::vector< Wave > m_waves
List of all configured waves.
void clearWaves()
Clear all configured waves.
float m_spawnTimer
Timer for processing enemy spawns.
void createWave3()
Create wave 3 configuration (increased difficulty)
void update(ecs::Registry &registry, float dt, int screenWidth)
Update wave manager logic (called each frame)
void start()
Start the wave system.
static constexpr float INITIAL_DELAY
Initial delay in seconds (synced with client)
bool m_initialDelayPassed
Whether initial delay has elapsed.
float m_initialDelayTimer
Timer for initial delay countdown.
WaveManager(EntityManager &entityManager)
Constructor.
void reset()
Stop and reset the wave system to initial state.
void createWave1()
Create wave 1 configuration (basic introduction wave)
bool isWaveCleared() const
Check if current wave is cleared of all enemies.
int m_currentWaveIndex
Index of current/next wave (0-based)
std::vector< bool > m_enemiesSpawned
Track which enemies in current wave have spawned.
void processSpawns(float dt, int screenWidth)
Process enemy spawns for current wave.
EntityManager & m_entityManager
Reference to entity manager for spawning.
void createBossWave()
Create boss wave configuration (final challenge)
void setupDefaultWaves()
Initialize with default wave patterns.
void createWave4()
Create wave 4 configuration (final standard wave)
float m_waveTimer
Timer for current wave duration.
void addWave(const Wave &wave)
Add a custom wave to the wave list.
void createWave2()
Create wave 2 configuration (mixed enemies)
bool m_active
Whether wave system is active.
@ ENEMY_ADVANCED
Advanced enemy type (complex behavior)
@ ENEMY_BASIC
Basic enemy type (simple behavior)
@ BOSS
Boss enemy (high health, special patterns)
Definition of a single enemy spawn within a wave.
float spawnDelay
Delay in seconds from wave start before spawning this enemy.
float health
Initial health points for this enemy.
float y
Spawn Y position (vertical placement on screen)
float x
Spawn X position (typically off-screen right)
ServerEntityType type
Type of enemy to spawn (ENEMY_BASIC, ENEMY_ADVANCED, or BOSS)
Complete wave configuration containing multiple enemy spawns.
std::vector< EnemySpawn > enemies
List of all enemies to spawn in this wave.
int waveNumber
Wave sequence number (1-based)
std::function< void()> onComplete
Optional callback function invoked when wave completes.
bool waitForClear
If true, next wave starts only when all enemies are destroyed.
float duration
Maximum duration in seconds before next wave (if not waitForClear)