diff --git a/data/entities/big_pentagon/big_pentagon.yaml b/data/entities/big_pentagon/big_pentagon.yaml index 41cfa22..fe90c61 100644 --- a/data/entities/big_pentagon/big_pentagon.yaml +++ b/data/entities/big_pentagon/big_pentagon.yaml @@ -1,15 +1,17 @@ name: big_pentagon ai_type: big_pentagon # Validat contra el directori; mapeja a EnemyType::BIG_PENTAGON. -# Reusa la shape de pentagon però a escala 2.0 — primer enemic gegant del joc. +# Shape circular pròpia (anell exterior + anell interior + 6 radis + nucli), +# pensada per llegir-se com a "reactor / orb" amb més detall que els enemics +# petits. shape: - path: enemy_pentagon.shp - scale: 2.0 + path: enemy_big_orb.shp + scale: 1.5 collision_factor: 1.0 physics: - mass: 20.0 # Massa gran: la bala el frena poc, els trossos volen amb la força de la bala. - speed: 30.0 # Avanç lent, com a "tanc carregant". + mass: 50.0 # Molt pesat: una bala el frena un poc però no el "envia a passejar". + speed: 50.0 # Avança decidit cap al ship (no és lent passiu, és amenaça constant). rotation_delta_min: 0.3 rotation_delta_max: 1.5 restitution: 1.0 @@ -17,11 +19,12 @@ physics: angular_damping: 0.0 ai: - # Persecució lenta del ship més proper: amb chase_strength baix la inèrcia - # és molt notable, fa el comportament del "boss lent" característic. + # Persecució contínua del ship més proper. chase_strength alt (1.0 = ~1s + # per realinear-se) perquè, encara que una bala l'empentja lateralment, + # ràpidament torna a posar la seua proa cap al jugador. movement: type: chase - chase_strength: 0.3 + chase_strength: 1.0 animation: pulse: @@ -49,31 +52,31 @@ spawn: invulnerability_brightness_end: 0.7 invulnerability_scale_start: 0.0 invulnerability_scale_end: 1.0 - safety_distance: 72.0 # Doble del normal — és el doble de gran. + safety_distance: 54.0 # 1.5× del normal (alineat amb scale 1.5). colors: - normal: [0, 180, 255] # Blau elèctric per distingir-lo del pentagon cyan. + normal: [66, 195, 208] # #42C3D0 — turquesa-cyan distintiu per al boss. wounded: [255, 220, 60] score: 500 # 5x un enemic normal: aguanta 10x més. # Estrenant el sistema HP: 10 unitats. Cada bala fa decrease_health + flash -# + create_debris_partial (xip a 0.3x). En el 10è hit, on_no_health -# encadena set_hurt (entra wounded). Wound expira → destroy → on_destroy -# fa l'explosió completa. +# + create_debris_partial (xip a 0.3x) + create_fireworks_small (espurna). +# Al 10è hit (HP=0), on_no_health encadena destroy directe — sense passar +# per wounded (com Star). 10 HP ja és prou dificultat sense afegir un hit +# extra. health: 10 events: on_hit: - - action: decrease_health # primer: si arriba a 0 dispara on_no_health - - action: flash # feedback visual de damage parcial - - action: create_debris_partial # xip a 0.3x mida (sense ser letal) - - action: apply_impulse # empenta el cos (sense will_die és segur en hit no-letal) + - action: decrease_health # primer: si arriba a 0 dispara on_no_health + - action: flash # feedback visual de damage parcial + - action: create_debris_partial # xip a 0.3x mida (sense ser letal) + - action: create_fireworks_small # espurna a cada hit (12 punts, lent) + - action: apply_impulse # empenta el cos (skip si will_die) on_no_health: - - action: set_hurt # entra wounded; segon hit durant wounded matarà - on_hurt_end: - - action: destroy + - action: destroy # mort directa, sense wounded on_destroy: - action: add_score - - action: create_debris # explosió completa + - action: create_debris # explosió completa - action: create_fireworks diff --git a/data/shapes/enemy_big_orb.shp b/data/shapes/enemy_big_orb.shp new file mode 100644 index 0000000..39c3c87 --- /dev/null +++ b/data/shapes/enemy_big_orb.shp @@ -0,0 +1,32 @@ +# enemy_big_orb.shp - ORNI enemic gegant (orb circular, doble anell amb radis) +# © 2026 JailDesigner +# +# Forma "reactor / boss circular" — més detall que els enemics petits perquè +# es renderitza a escala 1.5x i ha de llegir-se com a amenaça gran. +# - Anell exterior: dodecàgon (12 vèrtexs) — aparença circular suau, radi 20. +# - Anell interior: hexàgon (6 vèrtexs, rotat 30°) — radi 10. +# - 6 radis curts que connecten l'anell interior amb l'exterior. +# - Petit "+" central com a nucli. +# Bounding radius natiu = 20 (alineat amb la resta d'enemics). + +name: enemy_big_orb +scale: 1.0 +center: 0, 0 + +# Anell exterior (dodecàgon, vèrtex apuntant amunt) +polyline: 0,-20 10,-17.32 17.32,-10 20,0 17.32,10 10,17.32 0,20 -10,17.32 -17.32,10 -20,0 -17.32,-10 -10,-17.32 0,-20 + +# Anell interior (hexàgon, vèrtex apuntant a la dreta — rotat 30° respecte l'exterior) +polyline: 5,-8.66 10,0 5,8.66 -5,8.66 -10,0 -5,-8.66 5,-8.66 + +# 6 radis: del vèrtex de l'hexàgon interior al vèrtex corresponent del dodecàgon exterior +line: 5,-8.66 10,-17.32 +line: 10,0 20,0 +line: 5,8.66 10,17.32 +line: -5,8.66 -10,17.32 +line: -10,0 -20,0 +line: -5,-8.66 -10,-17.32 + +# Nucli central: petit "+" (2 segments creuats, radi 3) +line: -3,0 3,0 +line: 0,-3 0,3 diff --git a/data/stages/stages.yaml b/data/stages/stages.yaml index 848a106..9a9b885 100644 --- a/data/stages/stages.yaml +++ b/data/stages/stages.yaml @@ -7,7 +7,7 @@ metadata: description: "Progressive difficulty curve from novice to expert" stages: - # STAGE 1: Tutorial - Mix de tots 5 tipus per mostrar-los junts (inclou big_pentagon) + # STAGE 1: Tutorial - 4 tipus (sense star: les bales fan injugable el test). - stage_id: 1 total_enemies: 50 spawn_config: @@ -15,10 +15,9 @@ stages: initial_delay: 0.3 spawn_interval: 0.4 enemy_distribution: - pentagon: 20 - cuadrado: 20 - molinillo: 20 - star: 20 + pentagon: 30 + cuadrado: 25 + molinillo: 25 big_pentagon: 20 difficulty_multipliers: speed_multiplier: 0.7 diff --git a/source/core/defaults/enemies.hpp b/source/core/defaults/enemies.hpp index a0af7d3..5ffe89d 100644 --- a/source/core/defaults/enemies.hpp +++ b/source/core/defaults/enemies.hpp @@ -33,3 +33,13 @@ namespace Defaults::Enemies::Debris { constexpr float PARTIAL_PIECE_SCALE = 0.3F; } // namespace Defaults::Enemies::Debris + +namespace Defaults::Enemies::Fireworks { + + // Paràmetres del firework "petit" per a l'acció CREATE_FIREWORKS_SMALL + // (feedback per impacte parcial en enemics HP>1). Pocs punts i baixa + // velocitat: una espurna breu, no una explosió. + constexpr int SMALL_N_POINTS = 20; + constexpr float SMALL_SPEED = 250.0F; + +} // namespace Defaults::Enemies::Fireworks diff --git a/source/game/entities/enemy_config.cpp b/source/game/entities/enemy_config.cpp index 25903d8..19af094 100644 --- a/source/game/entities/enemy_config.cpp +++ b/source/game/entities/enemy_config.cpp @@ -193,6 +193,7 @@ namespace { if (s == "create_debris") { return EnemyActionType::CREATE_DEBRIS; } if (s == "create_debris_partial") { return EnemyActionType::CREATE_DEBRIS_PARTIAL; } if (s == "create_fireworks") { return EnemyActionType::CREATE_FIREWORKS; } + if (s == "create_fireworks_small") { return EnemyActionType::CREATE_FIREWORKS_SMALL; } if (s == "apply_impulse") { return EnemyActionType::APPLY_IMPULSE; } if (s == "decrease_health") { return EnemyActionType::DECREASE_HEALTH; } if (s == "flash") { return EnemyActionType::FLASH; } diff --git a/source/game/entities/enemy_event.hpp b/source/game/entities/enemy_event.hpp index 03ffc79..4c6e58b 100644 --- a/source/game/entities/enemy_event.hpp +++ b/source/game/entities/enemy_event.hpp @@ -18,15 +18,16 @@ enum class EnemyEventType : uint8_t { }; enum class EnemyActionType : uint8_t { - SET_HURT, // Entra estat wounded (o destrueix si ja era wounded) - DESTROY, // Dispara on_destroy + desactiva físicament - ADD_SCORE, // Suma config.score al shooter + floating score - CREATE_DEBRIS, // Explosió de debris amb herència de velocitat - CREATE_DEBRIS_PARTIAL, // Debris de xip parcial (trossos a escala 0.3, per hits HP>1) - CREATE_FIREWORKS, // Burst radial de firework - APPLY_IMPULSE, // Aplica l'impuls de la bala impactant - DECREASE_HEALTH, // Decrementa health_; si <=0, dispatcha ON_NO_HEALTH - FLASH, // Flash visual breu (feedback per impacte parcial) + SET_HURT, // Entra estat wounded (o destrueix si ja era wounded) + DESTROY, // Dispara on_destroy + desactiva físicament + ADD_SCORE, // Suma config.score al shooter + floating score + CREATE_DEBRIS, // Explosió de debris amb herència de velocitat + CREATE_DEBRIS_PARTIAL, // Debris de xip parcial (trossos a escala 0.3, per hits HP>1) + CREATE_FIREWORKS, // Burst radial de firework + CREATE_FIREWORKS_SMALL, // Burst petit (pocs punts, poca velocitat) — feedback per hit + APPLY_IMPULSE, // Aplica l'impuls de la bala impactant + DECREASE_HEALTH, // Decrementa health_; si <=0, dispatcha ON_NO_HEALTH + FLASH, // Flash visual breu (feedback per impacte parcial) }; struct EnemyAction { diff --git a/source/game/systems/collision_system.cpp b/source/game/systems/collision_system.cpp index e7a32e5..fa67f14 100644 --- a/source/game/systems/collision_system.cpp +++ b/source/game/systems/collision_system.cpp @@ -83,36 +83,6 @@ namespace Systems::Collision { } } - void detectWoundedChain(Context& ctx) { - const std::size_t N = ctx.enemies.size(); - for (std::size_t i = 0; i < N; i++) { - Enemy& a = ctx.enemies[i]; - if (!a.isCollidable()) { - continue; - } - for (std::size_t j = i + 1; j < N; j++) { - Enemy& b = ctx.enemies[j]; - if (!b.isCollidable()) { - continue; - } - const bool A_WOUNDED = a.isWounded(); - const bool B_WOUNDED = b.isWounded(); - if (A_WOUNDED == B_WOUNDED) { - continue; // ambos sanos o ambos heridos: nada que propagar - } - if (!Physics::checkCollision(a, b, Defaults::Game::COLLISION_WOUNDED_CHAIN_AMPLIFIER)) { - continue; - } - // El sano queda herido, propagando el shooter original. - if (A_WOUNDED) { - b.hurt(a.getLastHitBy()); - } else { - a.hurt(b.getLastHitBy()); - } - } - } - } - void detectShipEnemy(Context& ctx) { constexpr float AMPLIFIER = Defaults::Game::COLLISION_SHIP_ENEMY_AMPLIFIER; @@ -263,7 +233,9 @@ namespace Systems::Collision { void detectAll(Context& ctx) { processWoundedDeaths(ctx); // expiran ANTES de ser tocadas por bala este frame detectBulletEnemy(ctx); - detectWoundedChain(ctx); // un herit pot ferir a un sa al fregar-lo + // Wounded chain desactivat: era massa fàcil que un enemic ferit topés + // amb el big_pentagon (10 HP) i el matés instantàniament. La regla + // "ferit-toca-sa → ferit" queda permanentment fora. detectShipEnemy(ctx); detectBulletPlayer(ctx); detectEnemyBulletShip(ctx); diff --git a/source/game/systems/collision_system.hpp b/source/game/systems/collision_system.hpp index 9e28de6..46619ba 100644 --- a/source/game/systems/collision_system.hpp +++ b/source/game/systems/collision_system.hpp @@ -56,12 +56,6 @@ namespace Systems::Collision { // al `last_hit_by_` del enemy (si está set). void processWoundedDeaths(Context& ctx); - // Si un enemy herido colisiona con uno sano (ni herido ni invulnerable), - // el sano también queda herido (efecto cadena). Propaga `last_hit_by_` para - // que el shooter original siga acreditándose la muerte en cascada. El rebote - // físico ya lo resuelve PhysicsWorld; aquí solo propagamos el estado. - void detectWoundedChain(Context& ctx); - // Detecta colisiones ship → enemy. Si hit, llama on_player_hit(player_id). void detectShipEnemy(Context& ctx); diff --git a/source/game/systems/enemy_event_dispatcher.cpp b/source/game/systems/enemy_event_dispatcher.cpp index c4a6d9a..b28d8e7 100644 --- a/source/game/systems/enemy_event_dispatcher.cpp +++ b/source/game/systems/enemy_event_dispatcher.cpp @@ -51,14 +51,17 @@ namespace Systems::EnemyEvents { piece_scale); } - void doCreateFireworks(Systems::Collision::Context& ctx, const Enemy& enemy) { + // Helper compartit per CREATE_FIREWORKS i CREATE_FIREWORKS_SMALL: + // mateixa crida a spawn(), només canvien n_points, initial_speed i + // glow_color segons el "tamany" del burst (mort vs feedback per hit). + void spawnFireworksForEnemy(Systems::Collision::Context& ctx, const Enemy& enemy, int n_points, float initial_speed, SDL_Color glow_color) { ctx.firework_manager.spawn(enemy.getCenter(), Defaults::FX::Firework::DEFAULT_COLOR, - Defaults::FX::Firework::SPEED, - Defaults::FX::Firework::N_POINTS, + initial_speed, + n_points, Defaults::FX::Firework::INITIAL_BRIGHTNESS, /*glow=*/true, - enemy.getConfig().colors.wounded); + glow_color); } void doApplyImpulse(Enemy& enemy, const Bullet* bullet) { @@ -123,7 +126,14 @@ namespace Systems::EnemyEvents { spawnDebrisForEnemy(ctx, enemy, bullet, Defaults::Enemies::Debris::PARTIAL_PIECE_SCALE); break; case EnemyActionType::CREATE_FIREWORKS: - doCreateFireworks(ctx, enemy); + // Burst de mort: glow amb el color wounded (daurat) per + // marcar la mort com a esdeveniment "calent". + spawnFireworksForEnemy(ctx, enemy, Defaults::FX::Firework::N_POINTS, Defaults::FX::Firework::SPEED, enemy.getConfig().colors.wounded); + break; + case EnemyActionType::CREATE_FIREWORKS_SMALL: + // Burst d'impacte: glow amb el color de l'enemic, perquè + // l'espurna llegisca com a "tros del propi cos saltant". + spawnFireworksForEnemy(ctx, enemy, Defaults::Enemies::Fireworks::SMALL_N_POINTS, Defaults::Enemies::Fireworks::SMALL_SPEED, enemy.getConfig().colors.normal); break; case EnemyActionType::APPLY_IMPULSE: if (!will_die) {