Merge branch 'tweak/misc-adjustments': retocs varis (paleta, glow, audio, física, destell del títol)
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
# title_flash.shp - Sparkle 4-puntes amb costats còncaus (Atari-style)
|
||||
# 4 puntes als cardinals (radi 30) i valls còncaus als 45° (corba Bezier
|
||||
# quadràtica amb control point ±8). 5 punts per arc subdividint la corba.
|
||||
|
||||
name: title_flash
|
||||
scale: 1.0
|
||||
center: 0, 0
|
||||
|
||||
polyline: 0,-30 3.76,-21.76 8.64,-14.64 14.64,-8.64 21.76,-3.76 30,0 21.76,3.76 14.64,8.64 8.64,14.64 3.76,21.76 0,30 -3.76,21.76 -8.64,14.64 -14.64,8.64 -21.76,3.76 -30,0 -21.76,-3.76 -14.64,-8.64 -8.64,-14.64 -3.76,-21.76 0,-30
|
||||
Binary file not shown.
@@ -39,6 +39,7 @@ namespace Defaults::Sound {
|
||||
constexpr const char* EXPLOSION2 = "effects/explosion2.wav"; // Explosión alternativa
|
||||
constexpr const char* FRIENDLY_FIRE_HIT = "effects/friendly_fire.wav"; // Friendly fire hit
|
||||
constexpr const char* HIT = "effects/hit.wav"; // Enemic ferit (primer impacte → HURT)
|
||||
constexpr const char* HURT = "effects/hurt.wav"; // Nau pròpia entra a HURT
|
||||
constexpr const char* INIT_HUD = "effects/init_hud.wav"; // Para la animación del HUD
|
||||
constexpr const char* LASER = "effects/laser_shoot.wav"; // Disparo
|
||||
constexpr const char* LOGO = "effects/logo.wav"; // Logo
|
||||
|
||||
@@ -22,6 +22,9 @@ namespace Defaults::Game {
|
||||
// (1.05F) per tolerar floating-point i petites separacions post-impuls.
|
||||
constexpr float COLLISION_SHIP_ENEMY_AMPLIFIER = 1.05F;
|
||||
constexpr float COLLISION_BULLET_ENEMY_AMPLIFIER = 1.15F; // 115% hitbox (generous)
|
||||
// Wounded chain: el rebot físic separa els cossos abans que arribi
|
||||
// la detecció gameplay; amplier generós perquè el toc compti.
|
||||
constexpr float COLLISION_WOUNDED_CHAIN_AMPLIFIER = 1.25F;
|
||||
|
||||
// Friendly fire system
|
||||
constexpr bool FRIENDLY_FIRE_ENABLED = true; // Activar friendly fire
|
||||
|
||||
@@ -14,11 +14,11 @@ namespace Defaults::Palette {
|
||||
// brillantor perceptual sota el bloom (sense alterar la identitat de color).
|
||||
// El canal dominant es manté a 255 a cada color per maximitzar la saturació
|
||||
// visible quan el halo s'expandeix.
|
||||
constexpr SDL_Color SHIP = {.r = 255, .g = 255, .b = 255, .a = 255}; // Blanco neutro
|
||||
constexpr SDL_Color BULLET = {.r = 155, .g = 255, .b = 175, .a = 255}; // Verde laser
|
||||
constexpr SDL_Color PENTAGON = {.r = 155, .g = 195, .b = 255, .a = 255}; // Azul "esquivador"
|
||||
constexpr SDL_Color QUADRAT = {.r = 255, .g = 140, .b = 140, .a = 255}; // Rojo "tank"
|
||||
constexpr SDL_Color MOLINILLO = {.r = 255, .g = 160, .b = 255, .a = 255}; // Magenta agresivo
|
||||
constexpr SDL_Color WOUNDED = {.r = 255, .g = 220, .b = 60, .a = 255}; // Dorado: enemigo herido
|
||||
constexpr SDL_Color SHIP = {.r = 255, .g = 255, .b = 255, .a = 255}; // Blanco neutro
|
||||
constexpr SDL_Color BULLET = {.r = 155, .g = 255, .b = 175, .a = 255}; // Verde laser
|
||||
constexpr SDL_Color PENTAGON = {.r = 0, .g = 255, .b = 255, .a = 255}; // Cyan pur "esquivador"
|
||||
constexpr SDL_Color QUADRAT = {.r = 255, .g = 0, .b = 0, .a = 255}; // Roig pur "tank"
|
||||
constexpr SDL_Color MOLINILLO = {.r = 255, .g = 0, .b = 255, .a = 255}; // Magenta pur "agressiu"
|
||||
constexpr SDL_Color WOUNDED = {.r = 255, .g = 220, .b = 60, .a = 255}; // Dorado: enemigo herido
|
||||
|
||||
} // namespace Defaults::Palette
|
||||
|
||||
@@ -18,6 +18,14 @@ namespace Defaults::Physics {
|
||||
constexpr float IMPACT_MOMENTUM_FACTOR = 3.0F; // Factor de transferència de moment bala→enemic
|
||||
} // namespace Bullet
|
||||
|
||||
// Ship → enemy: impuls explícit aplicat a l'enemic en el moment exacte
|
||||
// que la nau mor per col·lisió amb ell (afegit per damunt del rebot
|
||||
// natural de PhysicsWorld, que ja és present però subtil amb la
|
||||
// damping de la nau).
|
||||
namespace Ship {
|
||||
constexpr float DEATH_IMPACT_MOMENTUM_FACTOR = 0.3F;
|
||||
} // namespace Ship
|
||||
|
||||
// Explosions (debris physics)
|
||||
namespace Debris {
|
||||
constexpr float VELOCITAT_BASE = 80.0F; // Velocidad inicial (px/s)
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
namespace Defaults::Playfield {
|
||||
|
||||
// Estructura de la graella (cel·les omplen tota la PLAYAREA)
|
||||
@@ -11,8 +13,11 @@ namespace Defaults::Playfield {
|
||||
constexpr int SUBDIVISIONS = 4; // cada cel·la principal es divideix en N subcel·les
|
||||
|
||||
// Brillo respecte al color global (border = 1.0)
|
||||
constexpr float GRID_BRIGHTNESS = 0.15F;
|
||||
constexpr float SUBGRID_BRIGHTNESS = 0.05F;
|
||||
constexpr float GRID_BRIGHTNESS = 0.20F;
|
||||
constexpr float SUBGRID_BRIGHTNESS = 0.10F;
|
||||
|
||||
// Color de la rejilla (lila/violeta synthwave). Es modula amb brillantor.
|
||||
constexpr SDL_Color GRID_COLOR = {.r = 160, .g = 80, .b = 255, .a = 255};
|
||||
|
||||
// Animació de creació amb timer intern del Playfield.
|
||||
// L'animació total cobreix tot l'INIT_HUD (3 s). Cada línia es pinta en
|
||||
|
||||
@@ -196,13 +196,39 @@ namespace Graphics {
|
||||
lines_.clear();
|
||||
lines_.reserve(verticals.size() + horizontals.size());
|
||||
|
||||
// El spawn_time_s s'assigna per índex espacial perquè la diagonal de
|
||||
// l'ona de creixement avanci uniformement. L'ordre dins lines_, en
|
||||
// canvi, ha de garantir que el grid principal (més brillant) es
|
||||
// dibuixi DESPRÉS del subgrid: així a les interseccions guanya el
|
||||
// principal i no queden tallades pel subgrid.
|
||||
for (int i = 0; i < NUM_V; i++) {
|
||||
verticals[i].spawn_time_s = static_cast<float>(i) * INTERVAL_V;
|
||||
lines_.push_back(verticals[i]);
|
||||
}
|
||||
for (int i = 0; i < NUM_H; i++) {
|
||||
horizontals[i].spawn_time_s = static_cast<float>(i) * INTERVAL_H;
|
||||
lines_.push_back(horizontals[i]);
|
||||
}
|
||||
|
||||
// Passada 1: subgrid (verticals + horitzontals).
|
||||
for (const auto& v : verticals) {
|
||||
if (v.brightness < Defaults::Playfield::GRID_BRIGHTNESS) {
|
||||
lines_.push_back(v);
|
||||
}
|
||||
}
|
||||
for (const auto& h : horizontals) {
|
||||
if (h.brightness < Defaults::Playfield::GRID_BRIGHTNESS) {
|
||||
lines_.push_back(h);
|
||||
}
|
||||
}
|
||||
// Passada 2: grid principal (verticals + horitzontals).
|
||||
for (const auto& v : verticals) {
|
||||
if (v.brightness >= Defaults::Playfield::GRID_BRIGHTNESS) {
|
||||
lines_.push_back(v);
|
||||
}
|
||||
}
|
||||
for (const auto& h : horizontals) {
|
||||
if (h.brightness >= Defaults::Playfield::GRID_BRIGHTNESS) {
|
||||
lines_.push_back(h);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -264,7 +290,9 @@ namespace Graphics {
|
||||
static_cast<int>(START_Y),
|
||||
static_cast<int>(END_X),
|
||||
static_cast<int>(END_Y),
|
||||
line.brightness);
|
||||
line.brightness,
|
||||
0.0F,
|
||||
Defaults::Playfield::GRID_COLOR);
|
||||
// Cap brillant mentre creix.
|
||||
if (P < 1.0F) {
|
||||
const float LENGTH = std::sqrt((DX * DX) + (DY * DY));
|
||||
@@ -276,7 +304,9 @@ namespace Graphics {
|
||||
static_cast<int>(START_Y + (DY * HEAD_T)),
|
||||
static_cast<int>(END_X),
|
||||
static_cast<int>(END_Y),
|
||||
Defaults::Playfield::HEAD_BRIGHTNESS);
|
||||
Defaults::Playfield::HEAD_BRIGHTNESS,
|
||||
0.0F,
|
||||
Defaults::Playfield::GRID_COLOR);
|
||||
}
|
||||
}
|
||||
return;
|
||||
@@ -303,7 +333,9 @@ namespace Graphics {
|
||||
static_cast<int>(prev_y),
|
||||
static_cast<int>(NX),
|
||||
static_cast<int>(NY),
|
||||
line.brightness);
|
||||
line.brightness,
|
||||
0.0F,
|
||||
Defaults::Playfield::GRID_COLOR);
|
||||
prev_x = NX;
|
||||
prev_y = NY;
|
||||
}
|
||||
|
||||
@@ -31,20 +31,15 @@ namespace Graphics {
|
||||
const float MIN_Y = zona.y;
|
||||
const float MAX_Y = zona.y + zona.h;
|
||||
|
||||
// Tint aleatori entre blanc (255,255,255) i cyan (0,255,255) per estrella.
|
||||
// T ∈ [0,1]: 0 → blanc; 1 → cyan. R = 255·(1-T), G=B=255.
|
||||
// Color únic per a totes les estrelles: el mateix blanc-blau gel
|
||||
// del starfield del títol (Defaults::Title::Colors::STARFIELD).
|
||||
const auto FILL_LAYER = [&](int layer, int count, int& idx) {
|
||||
for (int i = 0; i < count; i++) {
|
||||
const float T = randUniform(0.0F, 1.0F);
|
||||
stars_[idx++] = Star{
|
||||
.x = randUniform(MIN_X, MAX_X),
|
||||
.y = randUniform(MIN_Y, MAX_Y),
|
||||
.layer = layer,
|
||||
.color = SDL_Color{
|
||||
.r = static_cast<Uint8>(255.0F * (1.0F - T)),
|
||||
.g = 255,
|
||||
.b = 255,
|
||||
.a = 255}};
|
||||
.color = Defaults::Title::Colors::STARFIELD};
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -182,5 +182,5 @@ void Ship::draw() const {
|
||||
|
||||
void Ship::herir() {
|
||||
hurt_timer_ = Defaults::Ship::Hurt::DURATION;
|
||||
Audio::get()->playSound(Defaults::Sound::HIT, Audio::Group::GAME);
|
||||
Audio::get()->playSound(Defaults::Sound::HURT, Audio::Group::GAME);
|
||||
}
|
||||
|
||||
@@ -723,30 +723,36 @@ void GameScene::tocado(uint8_t player_id) {
|
||||
if (hit_timer_per_player_[player_id] == 0.0F) {
|
||||
// *** PHASE 1: TRIGGER DEATH ***
|
||||
|
||||
// Capturar velocitat ABANS del markHit (que la reseteja a zero).
|
||||
// Sense això, els debris no hereten cap inèrcia de la nau.
|
||||
const Vec2 SHIP_VEL_PRE_DEATH = ships_[player_id].getVelocityVector();
|
||||
const Vec2 SHIP_POS = ships_[player_id].getCenter();
|
||||
const float SHIP_ANGLE = ships_[player_id].getAngle();
|
||||
const float SHIP_BRIGHT = ships_[player_id].getBrightness();
|
||||
|
||||
// Mark ship as dead (stops rendering and input)
|
||||
ships_[player_id].markHit();
|
||||
|
||||
// Create ship explosion
|
||||
const Vec2& ship_pos = ships_[player_id].getCenter();
|
||||
float ship_angle = ships_[player_id].getAngle();
|
||||
Vec2 vel_nau = ships_[player_id].getVelocityVector();
|
||||
// Reduir la velocity heretada per la ship segons defaults (més realista)
|
||||
constexpr float INHERIT = Defaults::Physics::Debris::SHIP_VELOCITY_INHERITANCE;
|
||||
Vec2 vel_nau_80 = {.x = vel_nau.x * INHERIT, .y = vel_nau.y * INHERIT};
|
||||
const Vec2 INHERITED_VEL = SHIP_VEL_PRE_DEATH *
|
||||
Defaults::Physics::Debris::SHIP_VELOCITY_INHERITANCE;
|
||||
|
||||
// Mateixa dispersió i efecte que els debris d'enemic (lifetime,
|
||||
// friction, segment_multiplier alineats); només canvien sound i color.
|
||||
debris_manager_.explode(
|
||||
ships_[player_id].getShape(), // Ship shape (3 lines)
|
||||
ship_pos, // Center position
|
||||
ship_angle, // Ship orientation
|
||||
1.0F, // Normal scale
|
||||
Defaults::Physics::Debris::VELOCITAT_BASE, // 80 px/s
|
||||
ships_[player_id].getBrightness(), // Heredar brightness
|
||||
vel_nau_80, // Heredar 80% velocity
|
||||
0.0F, // Nave: trayectorias rectas (sin drotacio)
|
||||
0.0F, // Sin herencia visual (rotación aleatoria)
|
||||
Defaults::Sound::EXPLOSION2, // Sonido alternativo para la explosión
|
||||
Defaults::Palette::SHIP // Debris hereda color de la nave
|
||||
);
|
||||
ships_[player_id].getShape(),
|
||||
SHIP_POS,
|
||||
SHIP_ANGLE,
|
||||
1.0F,
|
||||
Defaults::Physics::Debris::VELOCITAT_BASE,
|
||||
SHIP_BRIGHT,
|
||||
INHERITED_VEL,
|
||||
0.0F, // sense herència angular
|
||||
0.0F, // sin herencia visual
|
||||
Defaults::Sound::EXPLOSION2,
|
||||
Defaults::Palette::SHIP,
|
||||
Defaults::Physics::Debris::ENEMY_LIFETIME,
|
||||
Defaults::Physics::Debris::ENEMY_FRICTION,
|
||||
Defaults::Physics::Debris::ENEMY_SEGMENT_MULTIPLIER);
|
||||
|
||||
// Start death timer (non-zero to avoid re-triggering)
|
||||
hit_timer_per_player_[player_id] = Defaults::Game::HIT_TIMER_TRIGGER_DEATH;
|
||||
@@ -873,9 +879,10 @@ void GameScene::drawStageMessage(const std::string& message) {
|
||||
float x = play_area.x + ((play_area.w - full_text_width) / 2.0F);
|
||||
float y = play_area.y + (play_area.h * Defaults::Game::STAGE_MESSAGE_Y_RATIO) - (text_height / 2.0F);
|
||||
|
||||
// Render only the partial message (typewriter effect)
|
||||
// Render only the partial message (typewriter effect) amb el color
|
||||
// ambre neon del "PRESS START" del títol — unifica el feel dels missatges.
|
||||
Vec2 pos = {.x = x, .y = y};
|
||||
text_.render(partial_message, pos, scale, SPACING);
|
||||
text_.render(partial_message, pos, scale, SPACING, 1.0F, Defaults::Title::Colors::PRESS_START);
|
||||
}
|
||||
|
||||
// ========================================
|
||||
|
||||
@@ -73,6 +73,15 @@ TitleScene::TitleScene(SDLManager& sdl, SceneContext& context)
|
||||
// correcte de la intro coreografiada (també quan venim de JUMP_TO_TITLE_MAIN).
|
||||
ship_animator_->setVisible(false);
|
||||
|
||||
// Flash que tapa el "pop" final de la nau al VP. Es spawneja al centre
|
||||
// de pantalla (= projecció del VP) quan ship_animator avisa.
|
||||
flash_shape_ = Graphics::ShapeLoader::load("title_flash.shp");
|
||||
ship_animator_->setOnShipDisappear([this](int /*player_id*/) {
|
||||
triggerFlash(Vec2{
|
||||
.x = static_cast<float>(Defaults::Window::WIDTH) / 2.0F,
|
||||
.y = static_cast<float>(Defaults::Window::HEIGHT) / 2.0F});
|
||||
});
|
||||
|
||||
initTitle();
|
||||
inicialitzarJailgames();
|
||||
|
||||
@@ -294,6 +303,7 @@ void TitleScene::update(float delta_time) {
|
||||
estat_actual_ == TitleState::PLAYER_JOIN_PHASE)) {
|
||||
ship_animator_->update(delta_time);
|
||||
}
|
||||
updateFlashes(delta_time);
|
||||
|
||||
switch (estat_actual_) {
|
||||
case TitleState::STARFIELD_FADE_IN:
|
||||
@@ -524,6 +534,7 @@ void TitleScene::draw() {
|
||||
estat_actual_ == TitleState::PLAYER_JOIN_PHASE)) {
|
||||
ship_animator_->draw();
|
||||
}
|
||||
drawFlashes();
|
||||
|
||||
if (estat_actual_ == TitleState::STARFIELD_FADE_IN || estat_actual_ == TitleState::STARFIELD) {
|
||||
return;
|
||||
@@ -629,3 +640,47 @@ auto TitleScene::checkStartGameButtonPressed() -> bool {
|
||||
void TitleScene::handleEvent(const SDL_Event& event) {
|
||||
(void)event;
|
||||
}
|
||||
|
||||
namespace {
|
||||
constexpr float FLASH_DURATION = 0.40F;
|
||||
constexpr float FLASH_MAX_SCALE = 2.5F;
|
||||
constexpr SDL_Color FLASH_COLOR = {.r = 255, .g = 255, .b = 255, .a = 255};
|
||||
} // namespace
|
||||
|
||||
void TitleScene::triggerFlash(Vec2 pos) {
|
||||
for (auto& f : flashes_) {
|
||||
if (!f.active) {
|
||||
f.active = true;
|
||||
f.position = pos;
|
||||
f.timer = 0.0F;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TitleScene::updateFlashes(float delta_time) {
|
||||
for (auto& f : flashes_) {
|
||||
if (!f.active) {
|
||||
continue;
|
||||
}
|
||||
f.timer += delta_time;
|
||||
if (f.timer >= FLASH_DURATION) {
|
||||
f.active = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TitleScene::drawFlashes() {
|
||||
if (!flash_shape_) {
|
||||
return;
|
||||
}
|
||||
for (const auto& f : flashes_) {
|
||||
if (!f.active) {
|
||||
continue;
|
||||
}
|
||||
// Escala 0 → max al midpoint → 0. Sinus simètric.
|
||||
const float T_NORM = f.timer / FLASH_DURATION;
|
||||
const float SCALE = FLASH_MAX_SCALE * std::sin(T_NORM * Defaults::Math::PI);
|
||||
Rendering::renderShape(sdl_.getRenderer(), flash_shape_, f.position, 0.0F, SCALE, 1.0F, 1.0F, FLASH_COLOR);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,6 +63,20 @@ class TitleScene final : public Scene {
|
||||
std::unique_ptr<Graphics::Camera3D> camera_;
|
||||
std::unique_ptr<Graphics::Starfield> starfield_;
|
||||
std::unique_ptr<Title::ShipAnimator> ship_animator_;
|
||||
|
||||
// Destell que tapa el "pop" final de cada nau quan arriba al VP.
|
||||
// Pool fix de 2 (una per nau). Anima escala 0→max→0.
|
||||
struct Flash {
|
||||
Vec2 position{};
|
||||
float timer{0.0F};
|
||||
bool active{false};
|
||||
};
|
||||
std::array<Flash, 2> flashes_{};
|
||||
std::shared_ptr<Graphics::Shape> flash_shape_;
|
||||
|
||||
void triggerFlash(Vec2 pos);
|
||||
void updateFlashes(float delta_time);
|
||||
void drawFlashes();
|
||||
TitleState estat_actual_{TitleState::STARFIELD_FADE_IN};
|
||||
float temps_acumulat_{0.0F};
|
||||
|
||||
|
||||
@@ -177,7 +177,7 @@ namespace Systems::Collision {
|
||||
if (A_WOUNDED == B_WOUNDED) {
|
||||
continue; // ambos sanos o ambos heridos: nada que propagar
|
||||
}
|
||||
if (!Physics::checkCollision(a, b, 1.0F)) {
|
||||
if (!Physics::checkCollision(a, b, Defaults::Game::COLLISION_WOUNDED_CHAIN_AMPLIFIER)) {
|
||||
continue;
|
||||
}
|
||||
// El sano queda herido, propagando el shooter original.
|
||||
@@ -203,24 +203,29 @@ namespace Systems::Collision {
|
||||
}
|
||||
|
||||
// Comprovem si la nau toca QUALSEVOL enemic vulnerable aquest frame.
|
||||
bool touching_now = false;
|
||||
for (const auto& enemy : ctx.enemies) {
|
||||
Enemy* touched_enemy = nullptr;
|
||||
for (auto& enemy : ctx.enemies) {
|
||||
if (enemy.isInvulnerable()) {
|
||||
continue;
|
||||
}
|
||||
if (Physics::checkCollision(ctx.ships[i], enemy, AMPLIFIER)) {
|
||||
touching_now = true;
|
||||
touched_enemy = &enemy;
|
||||
break;
|
||||
}
|
||||
}
|
||||
const bool TOUCHING_NOW = touched_enemy != nullptr;
|
||||
|
||||
// Edge-trigger: només compta com a impacte la transició no-tocant → tocant.
|
||||
// Així el contacte continu durant el rebot frame-a-frame no dispara HURT i mort
|
||||
// en frames consecutius.
|
||||
const bool RISING_EDGE = touching_now && !ctx.ships[i].wasTouchingEnemyPrevFrame();
|
||||
const bool RISING_EDGE = TOUCHING_NOW && !ctx.ships[i].wasTouchingEnemyPrevFrame();
|
||||
if (RISING_EDGE) {
|
||||
if (ctx.ships[i].isHurt()) {
|
||||
// Segon impacte durant HURT → mort definitiva (mateix flux que abans).
|
||||
// Segon impacte durant HURT → mort. Aplica un impuls afegit
|
||||
// perquè l'enemic surti disparat (feedback visible).
|
||||
const Vec2 SHIP_VEL = ctx.ships[i].getVelocityVector();
|
||||
const Vec2 IMPULSE = SHIP_VEL * (Defaults::Ship::MASS * Defaults::Physics::Ship::DEATH_IMPACT_MOMENTUM_FACTOR);
|
||||
touched_enemy->applyImpulse(IMPULSE);
|
||||
ctx.on_player_hit(i);
|
||||
} else {
|
||||
// Primer impacte → estat HURT (rebot físic ja resolt per PhysicsWorld;
|
||||
@@ -228,7 +233,7 @@ namespace Systems::Collision {
|
||||
ctx.ships[i].herir();
|
||||
}
|
||||
}
|
||||
ctx.ships[i].setTouchingEnemyPrevFrame(touching_now);
|
||||
ctx.ships[i].setTouchingEnemyPrevFrame(TOUCHING_NOW);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -268,7 +273,12 @@ namespace Systems::Collision {
|
||||
}
|
||||
|
||||
// *** TEAMMATE HIT (friendly fire) ***
|
||||
// Víctima perd 1 vida, atacant en guanya 1.
|
||||
// Víctima perd 1 vida, atacant en guanya 1. Apliquem l'impuls
|
||||
// de la bala a la nau ABANS de on_player_hit perquè tocado()
|
||||
// captura la velocitat per als debris (si no, queden quiets).
|
||||
const Vec2 BULLET_IMPULSE = bullet.getBody().velocity *
|
||||
(bullet.getBody().mass * Defaults::Physics::Bullet::IMPACT_MOMENTUM_FACTOR);
|
||||
ctx.ships[player_id].getBody().applyImpulse(BULLET_IMPULSE);
|
||||
ctx.on_player_hit(player_id);
|
||||
ctx.lives_per_player[BULLET_OWNER]++;
|
||||
Audio::get()->playSound(Defaults::Sound::FRIENDLY_FIRE_HIT, Audio::Group::GAME);
|
||||
|
||||
@@ -142,9 +142,14 @@ namespace Title {
|
||||
case ShipState::FLOATING:
|
||||
updateFloating(ship, delta_time);
|
||||
break;
|
||||
case ShipState::EXITING:
|
||||
case ShipState::EXITING: {
|
||||
updateExiting(ship, delta_time);
|
||||
// Transició a invisible: la nau acaba d'arribar al VP.
|
||||
if (!ship.visible && on_ship_disappear_) {
|
||||
on_ship_disappear_(ship.player_id);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
|
||||
#include "core/graphics/camera3d.hpp"
|
||||
#include "core/graphics/wireframe3d.hpp"
|
||||
@@ -70,10 +71,16 @@ namespace Title {
|
||||
[[nodiscard]] auto isAnimationComplete() const -> bool;
|
||||
[[nodiscard]] auto isVisible() const -> bool;
|
||||
|
||||
// Callback disparat quan una nau acaba l'EXITING (es torna invisible
|
||||
// al VP). Útil per a un destell que tapi el pop final.
|
||||
using ShipDisappearCallback = std::function<void(int player_id)>;
|
||||
void setOnShipDisappear(ShipDisappearCallback cb) { on_ship_disappear_ = std::move(cb); }
|
||||
|
||||
private:
|
||||
Rendering::Renderer* renderer_;
|
||||
const Graphics::Camera3D* camera_;
|
||||
std::array<TitleShip, 2> ships_;
|
||||
ShipDisappearCallback on_ship_disappear_;
|
||||
|
||||
static void updateEntering(TitleShip& ship, float delta_time);
|
||||
static void updateFloating(TitleShip& ship, float delta_time);
|
||||
|
||||
Reference in New Issue
Block a user