17 const std::shared_ptr<eng::IAudio> &audio,
const float skinIndex,
bool &showDebug,
18 const uint32_t sessionId)
19 : AScene(assignedId), m_audio(audio), m_renderer(renderer), m_skinIndex(skinIndex), m_showDebug(showDebug),
20 m_sessionId(sessionId)
22 auto ®istry = AScene::getRegistry();
24 registry.onComponentAdded(
25 [
this, &renderer, &audio, ®istry](
const ecs::Entity e,
const std::type_info &type)
27 const auto *audioComp = registry.getComponent<
ecs::Audio>(e);
28 const auto *colorComp = registry.getComponent<
ecs::Color>(e);
29 const auto *fontComp = registry.getComponent<
ecs::Font>(e);
30 const auto *rectComp = registry.getComponent<
ecs::Rect>(e);
31 const auto *scaleComp = registry.getComponent<
ecs::Scale>(e);
32 const auto *textComp = registry.getComponent<
ecs::Text>(e);
33 const auto *textureComp = registry.getComponent<
ecs::Texture>(e);
35 const auto *hitBox = registry.getComponent<
ecs::Hitbox>(e);
37 if (hitBox && transform)
39 renderer->createCircleShape({.name =
"hitbox_" + std::to_string(e),
40 .radius = hitBox->radius,
41 .color = {.r = 255, .g = 0, .b = 0, .a = 100},
42 .
x = transform->x + hitBox->offsetX,
43 .
y = transform->y + hitBox->offsetY,
44 .outline_thickness = 1.0f,
45 .outline_color = {.r = 255, .g = 0, .b = 0, .a = 200}});
49 if (textComp && transform && fontComp)
54 renderer->createFont(fontComp->
id, fontComp->path);
58 {.font_name = fontComp->
id,
59 .color = {.r = colorComp->r, .g = colorComp->g, .b = colorComp->b, .a = colorComp->a},
60 .content = textComp->content,
61 .size = textComp->font_size,
64 .name = textComp->
id});
69 const float scale_x = scaleComp ? scaleComp->x : 1.F;
70 const float scale_y = scaleComp ? scaleComp->y : 1.F;
75 renderer->createTexture(textureComp->
id, textureComp->path);
79 if (transform && textureComp)
83 renderer->createSprite(textureComp->
id + std::to_string(e), textureComp->
id, transform->x,
84 transform->y, scale_x, scale_y,
static_cast<int>(rectComp->pos_x),
85 static_cast<int>(rectComp->pos_y), rectComp->size_x, rectComp->size_y);
89 renderer->createSprite(textureComp->
id + std::to_string(e), textureComp->
id, transform->x,
98 audio->createAudio(audioComp->path, audioComp->volume, audioComp->loop,
99 audioComp->id + std::to_string(e));
104 registry.createEntity().with<
ecs::Score>(
"score", 0).build();
107 m_hudSystem = std::make_unique<HUDSystem>(renderer, registry);
113 uint32_t skinIndex = 0;
119 playerRect->pos_y = skinPosY;
124 uint32_t skinIndex = 0;
130 playerRect->pos_y = skinPosY;
133 auto beginSoundEntity = registry.createEntity()
136 if (
auto *audioComp = registry.getComponent<
ecs::Audio>(beginSoundEntity))
138 audioComp->play =
true;
224 auto ®istry = getRegistry();
227 for (
const auto &entityState : worldState.
entities)
232 for (
auto &score : registry.getAll<
ecs::Score>() | std::views::values)
234 score.value =
static_cast<int>(entityState.score);
241 std::set<uint32_t> currentEnemyIds;
242 std::set<uint32_t> currentProjectileIds;
244 if (m_firstWorldState)
246 uint32_t playerIndex = 0;
247 std::vector<uint32_t> playerIds;
249 playerIds.push_back(m_sessionId);
250 for (
const auto &entityState : worldState.
entities)
254 if (entityState.id != m_sessionId)
256 playerIds.push_back(entityState.id);
261 for (uint32_t playerId : playerIds)
263 m_playerSkinMap[playerId] = playerIndex;
267 m_firstWorldState =
false;
271 for (
const auto &entityState : worldState.
entities)
276 currentEnemyIds.insert(entityState.id);
280 currentProjectileIds.insert(entityState.id);
285 for (
const auto &entityState : worldState.
entities)
287 if (entityState.id == m_sessionId)
289 if (
auto *transform = registry.getComponent<
ecs::Transform>(m_localPlayerEntity))
291 float deltaX = std::abs(transform->x - entityState.x);
292 float deltaY = std::abs(transform->y - entityState.y);
293 const float CORRECTION_THRESHOLD = 5.0f;
295 if (deltaX > CORRECTION_THRESHOLD || deltaY > CORRECTION_THRESHOLD)
298 transform->x = transform->x + t * (entityState.x - transform->x);
299 transform->y = transform->y + t * (entityState.y - transform->y);
302 if (
auto *velocity = registry.getComponent<
ecs::Velocity>(m_localPlayerEntity))
304 velocity->x = entityState.vx;
305 velocity->y = entityState.vy;
309 if (
auto *health = registry.getComponent<
ecs::Health>(m_localPlayerEntity))
311 if (entityState.healthPercent != 255)
313 float previousHealth = health->current;
314 health->current = (
static_cast<float>(entityState.healthPercent) / 100.0f) * health->max;
317 if (health->current <= 0.0f && previousHealth > 0.0f)
319 utl::Logger::log(
"GameMulti: Local player died - disconnecting and showing game over",
324 std::vector<std::uint8_t> emptyData;
338 if (
auto *beamCharge = registry.getComponent<
ecs::BeamCharge>(m_localPlayerEntity))
341 beamCharge->current_charge =
static_cast<float>(entityState.stateFlags) / 255.0f;
346 if (!m_remotePlayers.contains(entityState.id))
348 uint32_t skinIndex = 0;
349 if (m_playerSkinMap.contains(entityState.id))
351 skinIndex = m_playerSkinMap[entityState.id];
359 registry.createEntity()
360 .with<
ecs::Transform>(
"remote_player_" + std::to_string(entityState.id), entityState.x,
362 .with<ecs::Velocity>(
"remote_velocity_" + std::to_string(entityState.id), entityState.vx,
364 .with<ecs::Rect>(
"remote_rect_" + std::to_string(entityState.id), 0.F, skinPosY,
367 .with<ecs::Scale>(
"remote_scale_" + std::to_string(entityState.id),
369 .with<ecs::Texture>(
"remote_texture_" + std::to_string(entityState.id),
371 .with<ecs::Player>(
"remote_player_comp_" + std::to_string(entityState.id),
false)
373 .with<
ecs::Health>(
"remote_player_health_" + std::to_string(entityState.id), 100.0f, 100.0f)
375 m_remotePlayers[entityState.id] = remotePlayer;
377 m_remotePlayerData[entityState.id] = {.targetX = entityState.x,
378 .targetY = entityState.y,
379 .targetVx = entityState.vx,
380 .targetVy = entityState.vy,
381 .currentX = entityState.x,
382 .currentY = entityState.y,
383 .smoothFactor = REMOTE_PLAYER_SMOOTH_FACTOR,
384 .targetRotation = 0.0f,
385 .currentRotation = 0.0f};
389 m_remotePlayerData[entityState.id].targetX = entityState.x;
390 m_remotePlayerData[entityState.id].targetY = entityState.y;
391 m_remotePlayerData[entityState.id].targetVx = entityState.vx;
392 m_remotePlayerData[entityState.id].targetVy = entityState.vy;
395 if (
auto *health = registry.getComponent<
ecs::Health>(m_remotePlayers[entityState.id]))
397 if (entityState.healthPercent != 255)
399 health->current = (
static_cast<float>(entityState.healthPercent) / 100.0f) * health->max;
407 if (!m_projectileEntities.contains(entityState.id))
410 bool isEnemyProjectile =
412 bool isSupercharged =
413 (entityState.subtype ==
416 std::string texturePath;
417 float width, height, scale;
419 if (isEnemyProjectile)
432 width = isSupercharged ? 29.0f : 20.0f;
433 height = isSupercharged ? 24.0f : 10.0f;
434 scale = isSupercharged ? 1.5f : 1.0f;
438 registry.createEntity()
439 .with<
ecs::Transform>(
"projectile_" + std::to_string(entityState.id), entityState.x,
441 .with<ecs::Velocity>(
"projectile_velocity_" + std::to_string(entityState.id),
442 entityState.vx, entityState.vy)
443 .with<ecs::Rect>(
"projectile_rect_" + std::to_string(entityState.id), 0.F, 0.F,
444 static_cast<int>(width),
static_cast<int>(height))
445 .with<ecs::Scale>(
"projectile_scale_" + std::to_string(entityState.id), scale, scale)
446 .with<ecs::Texture>(
"projectile_texture_" + std::to_string(entityState.id), texturePath);
448 if (isSupercharged && !isEnemyProjectile)
450 entityBuilder.with<
ecs::Animation>(
"projectile_animation_" + std::to_string(entityState.id), 0,
451 4, 0.15f, 0.0f, 29, 24, 4);
455 m_projectileEntities[entityState.id] = projectile;
464 if (
auto *transform = registry.getComponent<
ecs::Transform>(m_projectileEntities[entityState.id]))
466 transform->x = entityState.x;
467 transform->y = entityState.y;
469 if (
auto *velocity = registry.getComponent<
ecs::Velocity>(m_projectileEntities[entityState.id]))
471 velocity->x = entityState.vx;
472 velocity->y = entityState.vy;
478 if (!m_enemyEntities.contains(entityState.id))
481 std::string texturePath;
482 float width, height, scale;
502 registry.createEntity()
503 .with<
ecs::Transform>(
"enemy_" + std::to_string(entityState.id), entityState.x,
505 .with<ecs::Velocity>(
"enemy_velocity_" + std::to_string(entityState.id), entityState.vx,
507 .with<ecs::Rect>(
"enemy_rect_" + std::to_string(entityState.id), 0.F, 0.F,
508 static_cast<int>(width),
static_cast<int>(height))
509 .with<ecs::Scale>(
"enemy_scale_" + std::to_string(entityState.id), scale, scale)
510 .with<ecs::Texture>(
"enemy_texture_" + std::to_string(entityState.id), texturePath)
512 .with<
ecs::Animation>(
"enemy_animation_" + std::to_string(entityState.id), 0, animFrames,
513 0.5f, 0.0f,
static_cast<int>(width),
static_cast<int>(height),
517 m_enemyEntities[entityState.id] = enemy;
519 m_enemyData[entityState.id] = {.targetX = entityState.x,
520 .targetY = entityState.y,
521 .targetVx = entityState.vx,
522 .targetVy = entityState.vy,
523 .currentX = entityState.x,
524 .currentY = entityState.y,
525 .smoothFactor = ENEMY_SMOOTH_FACTOR,
526 .targetRotation = 0.0F,
527 .currentRotation = 0.0F};
531 m_enemyData[entityState.id].targetX = entityState.x;
532 m_enemyData[entityState.id].targetY = entityState.y;
533 m_enemyData[entityState.id].targetVx = entityState.vx;
534 m_enemyData[entityState.id].targetVy = entityState.vy;
539 if (!m_enemyEntities.contains(entityState.id))
544 float height = 64.0f;
548 registry.createEntity()
549 .with<
ecs::Transform>(
"boss_" + std::to_string(entityState.id), entityState.x,
551 .with<ecs::Velocity>(
"boss_velocity_" + std::to_string(entityState.id), entityState.vx,
553 .with<ecs::Rect>(
"boss_rect_" + std::to_string(entityState.id), 0.F, 0.F,
554 static_cast<int>(width),
static_cast<int>(height))
555 .with<ecs::Scale>(
"boss_scale_" + std::to_string(entityState.id), scale, scale)
556 .with<ecs::Texture>(
"boss_texture_" + std::to_string(entityState.id), texturePath)
558 .with<
ecs::Animation>(
"boss_animation_" + std::to_string(entityState.id), 0, 2, 0.3f, 0.0f,
559 static_cast<int>(width),
static_cast<int>(height), 2)
562 m_enemyEntities[entityState.id] = boss;
564 m_enemyData[entityState.id] = {.targetX = entityState.x,
565 .targetY = entityState.y,
566 .targetVx = entityState.vx,
567 .targetVy = entityState.vy,
568 .currentX = entityState.x,
569 .currentY = entityState.y,
570 .smoothFactor = ENEMY_SMOOTH_FACTOR,
571 .targetRotation = 0.0f,
572 .currentRotation = 0.0f};
574 utl::Logger::log(
"GameMulti: Boss spawned with ID " + std::to_string(entityState.id),
579 m_enemyData[entityState.id].targetX = entityState.x;
580 m_enemyData[entityState.id].targetY = entityState.y;
581 m_enemyData[entityState.id].targetVx = entityState.vx;
582 m_enemyData[entityState.id].targetVy = entityState.vy;
589 std::vector<uint32_t> enemiesToRemove;
590 for (
const auto &enemyId : m_enemyEntities | std::views::keys)
592 if (!currentEnemyIds.contains(enemyId))
594 enemiesToRemove.push_back(enemyId);
598 for (uint32_t enemyId : enemiesToRemove)
600 ecs::Entity enemyEntity = m_enemyEntities[enemyId];
603 if (
auto *transform = registry.getComponent<
ecs::Transform>(enemyEntity))
605 registry.createEntity()
606 .with<
ecs::Transform>(
"explosion_transform", transform->x, transform->y, 0.0f)
607 .with<ecs::Rect>(
"explosion_rect", 0.0f, 0.0f,
626 if (registry.hasComponent<
ecs::Rect>(enemyEntity))
627 registry.removeComponent<
ecs::Rect>(enemyEntity);
628 if (registry.hasComponent<
ecs::Scale>(enemyEntity))
629 registry.removeComponent<
ecs::Scale>(enemyEntity);
635 m_enemyEntities.erase(enemyId);
636 m_enemyData.erase(enemyId);
640 std::vector<uint32_t> projectilesToRemove;
641 for (
const auto &projectileId : m_projectileEntities | std::views::keys)
643 if (!currentProjectileIds.contains(projectileId))
645 projectilesToRemove.push_back(projectileId);
649 for (uint32_t projectileId : projectilesToRemove)
651 ecs::Entity projectileEntity = m_projectileEntities[projectileId];
662 if (registry.hasComponent<
ecs::Rect>(projectileEntity))
664 registry.removeComponent<
ecs::Rect>(projectileEntity);
666 if (registry.hasComponent<
ecs::Scale>(projectileEntity))
668 registry.removeComponent<
ecs::Scale>(projectileEntity);
670 if (registry.hasComponent<
ecs::Texture>(projectileEntity))
672 registry.removeComponent<
ecs::Texture>(projectileEntity);
679 m_projectileEntities.erase(projectileId);
682 catch (
const std::exception &e)