feat(demo): demos a 1 i 2 jugadors, esquiva de bales enemigues i vides infinites

This commit is contained in:
2026-05-28 13:14:19 +02:00
parent 92f76d091d
commit 455b7a6893
6 changed files with 158 additions and 55 deletions
+37 -21
View File
@@ -32,11 +32,9 @@ using SceneType = SceneContext::SceneType;
using Option = SceneContext::Option;
namespace {
// Attract mode: duració màxima de la demo i marge perquè es vegi l'explosió
// de la nau abans de saltar al logo (menor que DEATH_DURATION=3s per evitar
// la seqüència de respawn/continue).
// Attract mode: durada fixa de la demo. Amb vides infinites, sempre dura
// això (les morts respawnegen); només input o aquest timeout la tallen.
constexpr float DEMO_DURATION = 35.0F;
constexpr float DEMO_DEATH_LINGER = 2.0F;
// Qualsevol d'aquestes accions trenca la demo i torna al títol.
constexpr std::array<InputAction, 5> DEMO_EXIT_ACTIONS = {
@@ -160,10 +158,11 @@ GameScene::GameScene(SDLManager& sdl, SceneContext& context)
if (match_config_.mode == GameConfig::Mode::DEMO) {
// Attract mode: arrencar directament en PLAYING a l'escenari curat
// actual (partida "ja començada") i avançar l'índex perquè la pròxima
// demo mostri un escenari diferent.
const uint8_t DEMO_STAGE = Systems::Demo::scenarioStage(context_.demoScenarioIndex());
// demo mostri un escenari diferent. El nombre de jugadors ja l'ha fixat
// TitleScene al match_config llegint el mateix escenari.
const Systems::Demo::Scenario SC = Systems::Demo::scenario(context_.demoScenarioIndex());
context_.advanceDemoScenario();
stage_manager_->initDemo(DEMO_STAGE);
stage_manager_->initDemo(SC.stage);
demo_timer_ = DEMO_DURATION;
} else {
stage_manager_->init();
@@ -339,10 +338,10 @@ void GameScene::updateShipsControl(float delta_time) {
if (!ACTIU || hit_timer_per_player_[i] != 0.0F) {
continue;
}
// En demo, la P1 es mou amb el pilot IA (control calculat a stepDemo);
// la resta de casos llegeixen Input com sempre.
if (DEMO && i == 0) {
ships_[0].applyMovement(demo_ctrl_.left, demo_ctrl_.right, demo_ctrl_.thrust, delta_time);
// En demo, cada nau activa es mou amb el seu pilot IA (control calculat
// a stepDemo); la resta de casos llegeixen Input com sempre.
if (DEMO) {
ships_[i].applyMovement(demo_ctrls_[i].left, demo_ctrls_[i].right, demo_ctrls_[i].thrust, delta_time);
} else {
ships_[i].processInput(delta_time, i);
}
@@ -357,22 +356,30 @@ auto GameScene::stepDemo(float delta_time) -> bool {
return true;
}
// En morir la nau, escurçar el timer perquè es vegi l'explosió i després
// saltar al logo (sense passar per CONTINUE/GAME_OVER ni parar la música).
if (hit_timer_per_player_[0] > 0.0F) {
demo_timer_ = std::min(demo_timer_, DEMO_DEATH_LINGER);
}
// Vides infinites: la demo dura sempre el temps fix; en morir, stepDeathSequence
// respawneja (no acaba ni passa per CONTINUE/GAME_OVER).
demo_timer_ -= delta_time;
if (demo_timer_ <= 0.0F) {
endDemo();
return true;
}
// Control del pilot per al frame; el disparo el dispara GameScene.
demo_ctrl_ = demo_pilot_.compute(ships_[0], enemies_, Defaults::Zones::PLAYAREA, delta_time);
if (demo_ctrl_.shoot) {
fireBullet(0);
// Control de cada nau activa per al frame; el disparo el dispara GameScene.
for (uint8_t i = 0; i < 2; i++) {
const bool ACTIU = (i == 0) ? match_config_.player1_active : match_config_.player2_active;
if (!ACTIU || hit_timer_per_player_[i] != 0.0F) {
demo_ctrls_[i] = {}; // nau inactiva/morta: sense control
continue;
}
demo_ctrls_[i] = demo_pilots_[i].compute(
ships_[i],
enemies_,
bullets_,
Defaults::Zones::PLAYAREA,
delta_time);
if (demo_ctrls_[i].shoot) {
fireBullet(i);
}
}
return false;
}
@@ -491,6 +498,15 @@ void GameScene::stepDeathSequence(float delta_time) {
}
// *** PHASE 3: RESPAWN OR GAME OVER ***
// Mode demo: vides infinites. Respawn sempre, sense decrementar vides ni
// passar mai per CONTINUE/GAME_OVER — la demo dura el seu temps fix.
if (match_config_.mode == GameConfig::Mode::DEMO) {
Vec2 spawn_pos = getSpawnPoint(i);
ships_[i].init(&spawn_pos, /*activar_invulnerabilitat=*/true);
hit_timer_per_player_[i] = 0.0F;
continue;
}
lives_per_player_[i]--;
if (lives_per_player_[i] > 0) {
Vec2 spawn_pos = getSpawnPoint(i);
+5 -5
View File
@@ -102,10 +102,10 @@ class GameScene final : public Scene {
// Control de sons de animación INIT_HUD
bool init_hud_rect_sound_played_{false}; // Flag para evitar repetir sonido del rectángulo
// Attract mode (mode DEMO): un pilot IA controla la nau P1.
Systems::Demo::DemoPilot demo_pilot_;
Systems::Demo::Control demo_ctrl_{}; // Control del pilot per al frame actual
float demo_timer_{0.0F}; // Temps restant de la demo (→ LOGO en esgotar-se)
// Attract mode (mode DEMO): un pilot IA per nau activa (1 o 2 jugadors).
std::array<Systems::Demo::DemoPilot, 2> demo_pilots_;
std::array<Systems::Demo::Control, 2> demo_ctrls_{}; // Control per nau al frame actual
float demo_timer_{0.0F}; // Temps restant de la demo (→ LOGO)
// Funciones privades
// bullet_velocity: velocitat de la bala que ha causat la mort (Vec2{} si no
@@ -144,7 +144,7 @@ class GameScene final : public Scene {
void stepPhysics(float delta_time);
void stepShootingInput();
void stepMidGameJoin();
// Mueve las naves activas: en mode DEMO la P1 usa el pilot IA (demo_ctrl_),
// Mueve las naves activas: en mode DEMO cada nau usa su pilot IA (demo_ctrls_),
// el resto usa processInput (Input). Compartido por los 3 estados jugables.
void updateShipsControl(float delta_time);
// Mode DEMO: gestiona salida (input→título, timeout/muerte→logo) y calcula
+5 -1
View File
@@ -20,6 +20,7 @@
#include "core/rendering/shape_renderer.hpp"
#include "core/system/scene_context.hpp"
#include "core/system/service_menu.hpp"
#include "game/systems/demo_pilot.hpp"
#include "project.h"
using SceneManager::SceneContext;
@@ -359,9 +360,12 @@ void TitleScene::update(float delta_time) {
idle_timer_ += delta_time;
}
if (idle_timer_ >= TITLE_DEMO_TIMEOUT) {
// L'escenari curat (mateix índex que llegirà GameScene) decideix
// quants jugadors IA hi ha. No avancem l'índex ací: ho fa GameScene.
const Systems::Demo::Scenario SC = Systems::Demo::scenario(context_.demoScenarioIndex());
GameConfig::MatchConfig demo_cfg;
demo_cfg.player1_active = true;
demo_cfg.player2_active = false;
demo_cfg.player2_active = (SC.players >= 2);
demo_cfg.mode = GameConfig::Mode::DEMO;
context_.setMatchConfig(demo_cfg);
context_.setNextScene(SceneType::GAME);