|
|
|
@@ -14,6 +14,7 @@
|
|
|
|
|
#include "core/defaults.hpp"
|
|
|
|
|
#include "core/graphics/shape_loader.hpp"
|
|
|
|
|
#include "core/input/input.hpp"
|
|
|
|
|
#include "core/math/easing.hpp"
|
|
|
|
|
#include "core/rendering/shape_renderer.hpp"
|
|
|
|
|
#include "core/system/scene_context.hpp"
|
|
|
|
|
#include "project.h"
|
|
|
|
@@ -68,13 +69,9 @@ TitleScene::TitleScene(SDLManager& sdl, SceneContext& context)
|
|
|
|
|
|
|
|
|
|
ship_animator_ = std::make_unique<Title::ShipAnimator>(sdl_.getRenderer(), camera_.get());
|
|
|
|
|
ship_animator_->init();
|
|
|
|
|
|
|
|
|
|
if (estat_actual_ == TitleState::MAIN) {
|
|
|
|
|
ship_animator_->setVisible(true);
|
|
|
|
|
ship_animator_->startEntryAnimation();
|
|
|
|
|
} else {
|
|
|
|
|
ship_animator_->setVisible(false);
|
|
|
|
|
}
|
|
|
|
|
// Les naus comencen invisibles; updateMainState() les dispara al moment
|
|
|
|
|
// correcte de la intro coreografiada (també quan venim de JUMP_TO_TITLE_MAIN).
|
|
|
|
|
ship_animator_->setVisible(false);
|
|
|
|
|
|
|
|
|
|
initTitle();
|
|
|
|
|
inicialitzarJailgames();
|
|
|
|
@@ -251,8 +248,16 @@ void TitleScene::inicialitzarJailgames() {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TitleScene::dibuixarPeuTitol(float spacing) const {
|
|
|
|
|
namespace S = Defaults::Title::Sequence;
|
|
|
|
|
|
|
|
|
|
const float JAILGAMES_Y_INTRO = (1.0F - Easing::easeOutQuad(intro_jailgames_progress_)) *
|
|
|
|
|
S::FOOTER_OFFSCREEN_OFFSET_Y;
|
|
|
|
|
const float COPYRIGHT_Y_INTRO = (1.0F - Easing::easeOutQuad(intro_copyright_progress_)) *
|
|
|
|
|
S::FOOTER_OFFSCREEN_OFFSET_Y;
|
|
|
|
|
|
|
|
|
|
for (const auto& lletra : lletres_jailgames_) {
|
|
|
|
|
Rendering::renderShape(sdl_.getRenderer(), lletra.shape, lletra.position, 0.0F, Defaults::Title::Layout::JAILGAMES_SCALE, 1.0F, 1.0F, Defaults::Title::Colors::JAILGAMES_LOGO);
|
|
|
|
|
const Vec2 POS{.x = lletra.position.x, .y = lletra.position.y + JAILGAMES_Y_INTRO};
|
|
|
|
|
Rendering::renderShape(sdl_.getRenderer(), lletra.shape, POS, 0.0F, Defaults::Title::Layout::JAILGAMES_SCALE, 1.0F, 1.0F, Defaults::Title::Colors::JAILGAMES_LOGO);
|
|
|
|
|
}
|
|
|
|
|
std::string copyright = Project::COPYRIGHT;
|
|
|
|
|
for (char& c : copyright) {
|
|
|
|
@@ -260,7 +265,7 @@ void TitleScene::dibuixarPeuTitol(float spacing) const {
|
|
|
|
|
c = static_cast<char>(c - 32);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
const float Y_COPY = Defaults::Game::HEIGHT * Defaults::Title::Layout::COPYRIGHT1_POS;
|
|
|
|
|
const float Y_COPY = (Defaults::Game::HEIGHT * Defaults::Title::Layout::COPYRIGHT1_POS) + COPYRIGHT_Y_INTRO;
|
|
|
|
|
const float CENTRE_X = Defaults::Game::WIDTH / 2.0F;
|
|
|
|
|
text_.renderCentered(copyright, {.x = CENTRE_X, .y = Y_COPY}, Defaults::Title::Layout::COPYRIGHT_SCALE, spacing, 1.0F, Defaults::Title::Colors::COPYRIGHT);
|
|
|
|
|
}
|
|
|
|
@@ -326,18 +331,48 @@ void TitleScene::updateStarfieldState(float delta_time) {
|
|
|
|
|
|
|
|
|
|
void TitleScene::updateMainState(float delta_time) {
|
|
|
|
|
temps_estat_main_ += delta_time;
|
|
|
|
|
if (temps_estat_main_ >= Defaults::Title::Ships::ENTRANCE_DELAY &&
|
|
|
|
|
ship_animator_ && !ship_animator_->isVisible()) {
|
|
|
|
|
|
|
|
|
|
namespace S = Defaults::Title::Sequence;
|
|
|
|
|
namespace Sh = Defaults::Title::Ships;
|
|
|
|
|
|
|
|
|
|
// Thresholds derivats de la coreografia (vegeu Defaults::Title::Sequence).
|
|
|
|
|
constexpr float T_FOOTER_START = S::LOGO_ENTRY_DURATION;
|
|
|
|
|
constexpr float T_COPY_START = T_FOOTER_START + S::COPYRIGHT_STAGGER;
|
|
|
|
|
constexpr float T_JAILGAMES_END = T_FOOTER_START + S::JAILGAMES_ENTRY_DURATION;
|
|
|
|
|
constexpr float T_COPYRIGHT_END = T_COPY_START + S::COPYRIGHT_ENTRY_DURATION;
|
|
|
|
|
constexpr float T_FOOTER_END = std::max(T_JAILGAMES_END, T_COPYRIGHT_END);
|
|
|
|
|
constexpr float T_SHIPS_START = T_FOOTER_END + S::SHIPS_DELAY_AFTER_FOOTER;
|
|
|
|
|
constexpr float T_SHIPS_LANDED = T_SHIPS_START + Sh::ENTRY_DURATION + Sh::P2_ENTRY_DELAY;
|
|
|
|
|
constexpr float T_PRESS_START_VISIBLE = T_SHIPS_LANDED + S::PRESS_START_DELAY_AFTER_SHIPS;
|
|
|
|
|
|
|
|
|
|
intro_logo_progress_ = std::clamp(temps_estat_main_ / S::LOGO_ENTRY_DURATION, 0.0F, 1.0F);
|
|
|
|
|
intro_jailgames_progress_ = std::clamp(
|
|
|
|
|
(temps_estat_main_ - T_FOOTER_START) / S::JAILGAMES_ENTRY_DURATION,
|
|
|
|
|
0.0F,
|
|
|
|
|
1.0F);
|
|
|
|
|
intro_copyright_progress_ = std::clamp(
|
|
|
|
|
(temps_estat_main_ - T_COPY_START) / S::COPYRIGHT_ENTRY_DURATION,
|
|
|
|
|
0.0F,
|
|
|
|
|
1.0F);
|
|
|
|
|
|
|
|
|
|
if (!ships_intro_launched_ && temps_estat_main_ >= T_SHIPS_START &&
|
|
|
|
|
ship_animator_ != nullptr) {
|
|
|
|
|
ship_animator_->setVisible(true);
|
|
|
|
|
ship_animator_->startEntryAnimation();
|
|
|
|
|
ships_intro_launched_ = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (temps_estat_main_ < DELAY_INICI_ANIMACIO) {
|
|
|
|
|
if (!press_start_visible_ && temps_estat_main_ >= T_PRESS_START_VISIBLE) {
|
|
|
|
|
press_start_visible_ = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// L'oscil·lació suau del logo arrenca quan el logo ha aterrat. Així
|
|
|
|
|
// l'amplitud creix gradualment (lerp) durant la resta de la intro.
|
|
|
|
|
if (temps_estat_main_ < S::LOGO_ENTRY_DURATION) {
|
|
|
|
|
factor_lerp_ = 0.0F;
|
|
|
|
|
animacio_activa_ = false;
|
|
|
|
|
} else if (temps_estat_main_ < DELAY_INICI_ANIMACIO + DURACIO_LERP) {
|
|
|
|
|
const float TEMPS_LERP = temps_estat_main_ - DELAY_INICI_ANIMACIO;
|
|
|
|
|
factor_lerp_ = TEMPS_LERP / DURACIO_LERP;
|
|
|
|
|
} else if (temps_estat_main_ < S::LOGO_ENTRY_DURATION + DURACIO_LERP) {
|
|
|
|
|
factor_lerp_ = (temps_estat_main_ - S::LOGO_ENTRY_DURATION) / DURACIO_LERP;
|
|
|
|
|
animacio_activa_ = true;
|
|
|
|
|
} else {
|
|
|
|
|
factor_lerp_ = 1.0F;
|
|
|
|
@@ -382,13 +417,36 @@ void TitleScene::handleSkipInput() {
|
|
|
|
|
}
|
|
|
|
|
estat_actual_ = TitleState::MAIN;
|
|
|
|
|
starfield_->setBrightness(BRIGHTNESS_STARFIELD);
|
|
|
|
|
temps_estat_main_ = 0.0F;
|
|
|
|
|
|
|
|
|
|
// Saltar la intro coreografiada: deixar tots els elements ja in-place.
|
|
|
|
|
namespace S = Defaults::Title::Sequence;
|
|
|
|
|
namespace Sh = Defaults::Title::Ships;
|
|
|
|
|
constexpr float T_FOOTER_END = std::max(
|
|
|
|
|
S::LOGO_ENTRY_DURATION + S::JAILGAMES_ENTRY_DURATION,
|
|
|
|
|
S::LOGO_ENTRY_DURATION + S::COPYRIGHT_STAGGER + S::COPYRIGHT_ENTRY_DURATION);
|
|
|
|
|
constexpr float T_PRESS_START_VISIBLE = T_FOOTER_END + S::SHIPS_DELAY_AFTER_FOOTER +
|
|
|
|
|
Sh::ENTRY_DURATION + Sh::P2_ENTRY_DELAY + S::PRESS_START_DELAY_AFTER_SHIPS;
|
|
|
|
|
temps_estat_main_ = T_PRESS_START_VISIBLE;
|
|
|
|
|
intro_logo_progress_ = 1.0F;
|
|
|
|
|
intro_jailgames_progress_ = 1.0F;
|
|
|
|
|
intro_copyright_progress_ = 1.0F;
|
|
|
|
|
press_start_visible_ = true;
|
|
|
|
|
ships_intro_launched_ = true;
|
|
|
|
|
if (ship_animator_ != nullptr) {
|
|
|
|
|
ship_animator_->setVisible(true);
|
|
|
|
|
ship_animator_->startEntryAnimation();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TitleScene::handleStartInput() {
|
|
|
|
|
if (estat_actual_ != TitleState::MAIN) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// No acceptar START fins que la intro coreografiada haja conclòs i el
|
|
|
|
|
// text "PRESS START TO PLAY" siga visible.
|
|
|
|
|
if (!press_start_visible_) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const bool P1_ABANS = match_config_.jugador1_actiu;
|
|
|
|
|
const bool P2_ABANS = match_config_.jugador2_actiu;
|
|
|
|
|
|
|
|
|
@@ -466,6 +524,12 @@ void TitleScene::draw() {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Offset additiu de la intro del logo (cau des de dalt). Quan progress=1,
|
|
|
|
|
// el logo està al seu lloc i aquest offset val 0 — l'oscil·lació suau
|
|
|
|
|
// (updateLogoAnimation) continua treballant sobre la posició base.
|
|
|
|
|
const float LOGO_Y_INTRO = (1.0F - Easing::easeOutQuad(intro_logo_progress_)) *
|
|
|
|
|
Defaults::Title::Sequence::LOGO_OFFSCREEN_OFFSET_Y;
|
|
|
|
|
|
|
|
|
|
if (animacio_activa_) {
|
|
|
|
|
float temps_shadow = std::max(0.0F, temps_animacio_ - SHADOW_DELAY);
|
|
|
|
|
const float TWO_PI = 2.0F * Defaults::Math::PI;
|
|
|
|
@@ -475,39 +539,43 @@ void TitleScene::draw() {
|
|
|
|
|
for (std::size_t i = 0; i < lletres_orni_.size(); ++i) {
|
|
|
|
|
const Vec2 POS_SHADOW{
|
|
|
|
|
.x = posicions_originals_orni_[i].x + std::round(SHADOW_OX),
|
|
|
|
|
.y = posicions_originals_orni_[i].y + std::round(SHADOW_OY),
|
|
|
|
|
.y = posicions_originals_orni_[i].y + std::round(SHADOW_OY) + LOGO_Y_INTRO,
|
|
|
|
|
};
|
|
|
|
|
Rendering::renderShape(sdl_.getRenderer(), lletres_orni_[i].shape, POS_SHADOW, 0.0F, Defaults::Title::Layout::LOGO_SCALE, 1.0F, SHADOW_BRIGHTNESS, Defaults::Title::Colors::LOGO_SHADOW);
|
|
|
|
|
}
|
|
|
|
|
for (std::size_t i = 0; i < lletres_attack_.size(); ++i) {
|
|
|
|
|
const Vec2 POS_SHADOW{
|
|
|
|
|
.x = posicions_originals_attack_[i].x + std::round(SHADOW_OX),
|
|
|
|
|
.y = posicions_originals_attack_[i].y + std::round(SHADOW_OY),
|
|
|
|
|
.y = posicions_originals_attack_[i].y + std::round(SHADOW_OY) + LOGO_Y_INTRO,
|
|
|
|
|
};
|
|
|
|
|
Rendering::renderShape(sdl_.getRenderer(), lletres_attack_[i].shape, POS_SHADOW, 0.0F, Defaults::Title::Layout::LOGO_SCALE, 1.0F, SHADOW_BRIGHTNESS, Defaults::Title::Colors::LOGO_SHADOW);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (const auto& lletra : lletres_orni_) {
|
|
|
|
|
Rendering::renderShape(sdl_.getRenderer(), lletra.shape, lletra.position, 0.0F, Defaults::Title::Layout::LOGO_SCALE, 1.0F, 1.0F, Defaults::Title::Colors::LOGO_MAIN);
|
|
|
|
|
const Vec2 POS{.x = lletra.position.x, .y = lletra.position.y + LOGO_Y_INTRO};
|
|
|
|
|
Rendering::renderShape(sdl_.getRenderer(), lletra.shape, POS, 0.0F, Defaults::Title::Layout::LOGO_SCALE, 1.0F, 1.0F, Defaults::Title::Colors::LOGO_MAIN);
|
|
|
|
|
}
|
|
|
|
|
for (const auto& lletra : lletres_attack_) {
|
|
|
|
|
Rendering::renderShape(sdl_.getRenderer(), lletra.shape, lletra.position, 0.0F, Defaults::Title::Layout::LOGO_SCALE, 1.0F, 1.0F, Defaults::Title::Colors::LOGO_MAIN);
|
|
|
|
|
const Vec2 POS{.x = lletra.position.x, .y = lletra.position.y + LOGO_Y_INTRO};
|
|
|
|
|
Rendering::renderShape(sdl_.getRenderer(), lletra.shape, POS, 0.0F, Defaults::Title::Layout::LOGO_SCALE, 1.0F, 1.0F, Defaults::Title::Colors::LOGO_MAIN);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const float SPACING = Defaults::Title::Layout::TEXT_SPACING;
|
|
|
|
|
|
|
|
|
|
bool mostrar_text = true;
|
|
|
|
|
if (estat_actual_ == TitleState::PLAYER_JOIN_PHASE) {
|
|
|
|
|
const float FASE = temps_acumulat_ * BLINK_FREQUENCY * 2.0F * std::numbers::pi_v<float>;
|
|
|
|
|
mostrar_text = (std::sin(FASE) > 0.0F);
|
|
|
|
|
}
|
|
|
|
|
if (mostrar_text) {
|
|
|
|
|
const std::string MAIN_TEXT = "PRESS START TO PLAY";
|
|
|
|
|
const float MAIN_SCALE = Defaults::Title::Layout::PRESS_START_SCALE;
|
|
|
|
|
const float CENTRE_X = Defaults::Game::WIDTH / 2.0F;
|
|
|
|
|
const float CENTRE_Y = Defaults::Game::HEIGHT * Defaults::Title::Layout::PRESS_START_POS;
|
|
|
|
|
text_.renderCentered(MAIN_TEXT, {.x = CENTRE_X, .y = CENTRE_Y}, MAIN_SCALE, SPACING, 1.0F, Defaults::Title::Colors::PRESS_START);
|
|
|
|
|
if (press_start_visible_) {
|
|
|
|
|
bool mostrar_text = true;
|
|
|
|
|
if (estat_actual_ == TitleState::PLAYER_JOIN_PHASE) {
|
|
|
|
|
const float FASE = temps_acumulat_ * BLINK_FREQUENCY * 2.0F * std::numbers::pi_v<float>;
|
|
|
|
|
mostrar_text = (std::sin(FASE) > 0.0F);
|
|
|
|
|
}
|
|
|
|
|
if (mostrar_text) {
|
|
|
|
|
const std::string MAIN_TEXT = "PRESS START TO PLAY";
|
|
|
|
|
const float MAIN_SCALE = Defaults::Title::Layout::PRESS_START_SCALE;
|
|
|
|
|
const float CENTRE_X = Defaults::Game::WIDTH / 2.0F;
|
|
|
|
|
const float CENTRE_Y = Defaults::Game::HEIGHT * Defaults::Title::Layout::PRESS_START_POS;
|
|
|
|
|
text_.renderCentered(MAIN_TEXT, {.x = CENTRE_X, .y = CENTRE_Y}, MAIN_SCALE, SPACING, 1.0F, Defaults::Title::Colors::PRESS_START);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dibuixarPeuTitol(SPACING);
|
|
|
|
|