Merge branch 'time-based'
This commit is contained in:
@@ -8,88 +8,95 @@
|
||||
|
||||
namespace {
|
||||
|
||||
// Normalitza CRLF: fitxers .ani amb terminadors de Windows fan que
|
||||
// line == "[animation]" no faci match i el parser entri en bucle
|
||||
// infinit / no carregui cap animació.
|
||||
void stripCr(std::string &s) {
|
||||
if (!s.empty() && s.back() == '\r') {
|
||||
s.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
void parseFramesList(const std::string &value, Animation &buffer, int frame_width, int frame_height, int frames_per_row, int max_tiles) {
|
||||
std::stringstream ss(value);
|
||||
std::string tmp;
|
||||
SDL_Rect rect = {0, 0, frame_width, frame_height};
|
||||
while (getline(ss, tmp, ',')) {
|
||||
const int NUM_TILE = std::stoi(tmp) > max_tiles ? 0 : std::stoi(tmp);
|
||||
rect.x = (NUM_TILE % frames_per_row) * frame_width;
|
||||
rect.y = (NUM_TILE / frames_per_row) * frame_height;
|
||||
buffer.frames.push_back(rect);
|
||||
}
|
||||
}
|
||||
|
||||
void parseAnimationField(const std::string &line, int pos, Animation &buffer, int frame_width, int frame_height, int frames_per_row, int max_tiles, const std::string &filename) {
|
||||
const std::string KEY = line.substr(0, pos);
|
||||
const std::string VALUE = line.substr(pos + 1, line.length());
|
||||
if (KEY == "name") {
|
||||
buffer.name = VALUE;
|
||||
} else if (KEY == "speed") {
|
||||
buffer.speed = std::stoi(VALUE);
|
||||
} else if (KEY == "loop") {
|
||||
buffer.loop = std::stoi(VALUE);
|
||||
} else if (KEY == "frames") {
|
||||
parseFramesList(VALUE, buffer, frame_width, frame_height, frames_per_row, max_tiles);
|
||||
} else {
|
||||
std::cout << "Warning: file " << filename.c_str() << "\n, unknown parameter \"" << KEY.c_str() << "\"" << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
auto parseAnimationBlock(std::istream &file, int frame_width, int frame_height, int frames_per_row, int max_tiles, const std::string &filename) -> Animation {
|
||||
Animation buffer;
|
||||
buffer.speed = 0;
|
||||
buffer.loop = -1;
|
||||
buffer.counter = 0;
|
||||
buffer.current_frame = 0;
|
||||
buffer.completed = false;
|
||||
|
||||
std::string line;
|
||||
do {
|
||||
if (!std::getline(file, line)) {
|
||||
break;
|
||||
// Normalitza CRLF: fitxers .ani amb terminadors de Windows fan que
|
||||
// line == "[animation]" no faci match i el parser entri en bucle
|
||||
// infinit / no carregui cap animació.
|
||||
void stripCr(std::string &s) {
|
||||
if (!s.empty() && s.back() == '\r') {
|
||||
s.pop_back();
|
||||
}
|
||||
stripCr(line);
|
||||
int pos = line.find('=');
|
||||
if (pos != (int)std::string::npos) {
|
||||
parseAnimationField(line, pos, buffer, frame_width, frame_height, frames_per_row, max_tiles, filename);
|
||||
}
|
||||
|
||||
void parseFramesList(const std::string &value, Animation &buffer, int frame_width, int frame_height, int frames_per_row, int max_tiles) {
|
||||
std::stringstream ss(value);
|
||||
std::string tmp;
|
||||
SDL_Rect rect = {0, 0, frame_width, frame_height};
|
||||
while (getline(ss, tmp, ',')) {
|
||||
const int NUM_TILE = std::stoi(tmp) > max_tiles ? 0 : std::stoi(tmp);
|
||||
rect.x = (NUM_TILE % frames_per_row) * frame_width;
|
||||
rect.y = (NUM_TILE / frames_per_row) * frame_height;
|
||||
buffer.frames.push_back(rect);
|
||||
}
|
||||
} while (line != "[/animation]");
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void parseGlobalField(const std::string &line, int pos, int &frames_per_row, int &frame_width, int &frame_height, int &max_tiles, const Texture *texture, const std::string &filename) {
|
||||
const std::string KEY = line.substr(0, pos);
|
||||
const std::string VALUE = line.substr(pos + 1, line.length());
|
||||
if (KEY == "framesPerRow") {
|
||||
frames_per_row = std::stoi(VALUE);
|
||||
} else if (KEY == "frameWidth") {
|
||||
frame_width = std::stoi(VALUE);
|
||||
} else if (KEY == "frameHeight") {
|
||||
frame_height = std::stoi(VALUE);
|
||||
} else {
|
||||
std::cout << "Warning: file " << filename.c_str() << "\n, unknown parameter \"" << KEY.c_str() << "\"" << '\n';
|
||||
}
|
||||
|
||||
if (frames_per_row == 0 && frame_width > 0) {
|
||||
frames_per_row = texture->getWidth() / frame_width;
|
||||
void parseAnimationField(const std::string &line, int pos, Animation &buffer, int frame_width, int frame_height, int frames_per_row, int max_tiles, const std::string &filename) {
|
||||
const std::string KEY = line.substr(0, pos);
|
||||
const std::string VALUE = line.substr(pos + 1, line.length());
|
||||
if (KEY == "name") {
|
||||
buffer.name = VALUE;
|
||||
} else if (KEY == "speed") {
|
||||
buffer.speed = std::stoi(VALUE);
|
||||
// Time-based: el valor del .ani s'expressa en "ticks per frame
|
||||
// d'animació" (assumint 60 Hz). El camp `speed` (int) es manté per al
|
||||
// fallback frame-based; el nou `step_duration_s` (float) és el que
|
||||
// gasta animate(dt).
|
||||
buffer.step_duration_s = static_cast<float>(buffer.speed) / 60.0F;
|
||||
} else if (KEY == "loop") {
|
||||
buffer.loop = std::stoi(VALUE);
|
||||
} else if (KEY == "frames") {
|
||||
parseFramesList(VALUE, buffer, frame_width, frame_height, frames_per_row, max_tiles);
|
||||
} else {
|
||||
std::cout << "Warning: file " << filename.c_str() << "\n, unknown parameter \"" << KEY.c_str() << "\"" << '\n';
|
||||
}
|
||||
}
|
||||
if (max_tiles == 0 && frame_width > 0 && frame_height > 0) {
|
||||
const int W = texture->getWidth() / frame_width;
|
||||
const int H = texture->getHeight() / frame_height;
|
||||
max_tiles = W * H;
|
||||
|
||||
auto parseAnimationBlock(std::istream &file, int frame_width, int frame_height, int frames_per_row, int max_tiles, const std::string &filename) -> Animation {
|
||||
Animation buffer;
|
||||
buffer.speed = 0;
|
||||
buffer.step_duration_s = 0.0F;
|
||||
buffer.loop = -1;
|
||||
buffer.counter = 0;
|
||||
buffer.current_frame = 0;
|
||||
buffer.completed = false;
|
||||
buffer.time_accumulator_s = 0.0F;
|
||||
|
||||
std::string line;
|
||||
do {
|
||||
if (!std::getline(file, line)) {
|
||||
break;
|
||||
}
|
||||
stripCr(line);
|
||||
int pos = line.find('=');
|
||||
if (pos != (int)std::string::npos) {
|
||||
parseAnimationField(line, pos, buffer, frame_width, frame_height, frames_per_row, max_tiles, filename);
|
||||
}
|
||||
} while (line != "[/animation]");
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void parseGlobalField(const std::string &line, int pos, int &frames_per_row, int &frame_width, int &frame_height, int &max_tiles, const Texture *texture, const std::string &filename) {
|
||||
const std::string KEY = line.substr(0, pos);
|
||||
const std::string VALUE = line.substr(pos + 1, line.length());
|
||||
if (KEY == "framesPerRow") {
|
||||
frames_per_row = std::stoi(VALUE);
|
||||
} else if (KEY == "frameWidth") {
|
||||
frame_width = std::stoi(VALUE);
|
||||
} else if (KEY == "frameHeight") {
|
||||
frame_height = std::stoi(VALUE);
|
||||
} else {
|
||||
std::cout << "Warning: file " << filename.c_str() << "\n, unknown parameter \"" << KEY.c_str() << "\"" << '\n';
|
||||
}
|
||||
|
||||
if (frames_per_row == 0 && frame_width > 0) {
|
||||
frames_per_row = texture->getWidth() / frame_width;
|
||||
}
|
||||
if (max_tiles == 0 && frame_width > 0 && frame_height > 0) {
|
||||
const int W = texture->getWidth() / frame_width;
|
||||
const int H = texture->getHeight() / frame_height;
|
||||
max_tiles = W * H;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -204,33 +211,26 @@ auto AnimatedSprite::getIndex(const std::string &name) -> int {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Calcula el frame correspondiente a la animación
|
||||
void AnimatedSprite::animate() {
|
||||
if (!enabled_ || animation_[current_animation_].speed == 0) {
|
||||
// Avança l'acumulador i calcula el frame actual a partir de `step_duration_s`.
|
||||
void AnimatedSprite::animate(float dt_s) {
|
||||
Animation &anim = animation_[current_animation_];
|
||||
if (!enabled_ || anim.step_duration_s <= 0.0F) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Calcula el frame actual a partir del contador
|
||||
animation_[current_animation_].current_frame = animation_[current_animation_].counter / animation_[current_animation_].speed;
|
||||
anim.time_accumulator_s += dt_s;
|
||||
anim.current_frame = static_cast<int>(anim.time_accumulator_s / anim.step_duration_s);
|
||||
|
||||
// Si alcanza el final de la animación, reinicia el contador de la animación
|
||||
// en función de la variable loop y coloca el nuevo frame
|
||||
if (animation_[current_animation_].current_frame >= (int)animation_[current_animation_].frames.size()) {
|
||||
if (animation_[current_animation_].loop == -1) { // Si no hay loop, deja el último frame
|
||||
animation_[current_animation_].current_frame = animation_[current_animation_].frames.size();
|
||||
animation_[current_animation_].completed = true;
|
||||
} else { // Si hay loop, vuelve al frame indicado
|
||||
animation_[current_animation_].counter = 0;
|
||||
animation_[current_animation_].current_frame = animation_[current_animation_].loop;
|
||||
if (anim.current_frame >= (int)anim.frames.size()) {
|
||||
if (anim.loop == -1) {
|
||||
anim.current_frame = anim.frames.size();
|
||||
anim.completed = true;
|
||||
} else {
|
||||
anim.time_accumulator_s = 0.0F;
|
||||
anim.current_frame = anim.loop;
|
||||
}
|
||||
}
|
||||
// En caso contrario
|
||||
else {
|
||||
// Escoge el frame correspondiente de la animación
|
||||
setSpriteClip(animation_[current_animation_].frames[animation_[current_animation_].current_frame]);
|
||||
|
||||
// Incrementa el contador de la animacion
|
||||
animation_[current_animation_].counter++;
|
||||
} else {
|
||||
setSpriteClip(anim.frames[anim.current_frame]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -344,10 +344,10 @@ void AnimatedSprite::setCurrentAnimation(int index) {
|
||||
}
|
||||
}
|
||||
|
||||
// Actualiza las variables del objeto
|
||||
void AnimatedSprite::update() {
|
||||
animate();
|
||||
MovingSprite::update();
|
||||
// animate(dt) + MovingSprite::update(dt) (move + rotació)
|
||||
void AnimatedSprite::update(float dt_s) {
|
||||
animate(dt_s);
|
||||
MovingSprite::update(dt_s);
|
||||
}
|
||||
|
||||
// Establece el rectangulo para un frame de una animación
|
||||
|
||||
@@ -12,11 +12,13 @@ class Texture;
|
||||
struct Animation {
|
||||
std::string name; // Nombre de la animacion
|
||||
std::vector<SDL_Rect> frames; // Cada uno de los frames que componen la animación
|
||||
int speed; // Velocidad de la animación
|
||||
int speed; // Velocidad de la animación (frame-based: ticks per frame)
|
||||
float step_duration_s; // Time-based: segons per frame d'animació (derivat de speed al parse: speed/60)
|
||||
int loop; // Indica a que frame vuelve la animación al terminar. -1 para que no vuelva
|
||||
bool completed; // Indica si ha finalizado la animación
|
||||
int current_frame; // Frame actual
|
||||
int counter; // Contador para las animaciones
|
||||
int counter; // Contador per a les animacions (frame-based)
|
||||
float time_accumulator_s; // Acumulador de temps (time-based)
|
||||
};
|
||||
|
||||
struct AnimatedSpriteData {
|
||||
@@ -37,7 +39,7 @@ class AnimatedSprite : public MovingSprite {
|
||||
|
||||
~AnimatedSprite() override; // Destructor
|
||||
|
||||
void animate(); // Calcula el frame correspondiente a la animación actual
|
||||
void animate(float dt_s); // Calcula el frame correspondiente a la animación actual
|
||||
auto getNumFrames() -> int; // Obtiene el numero de frames de la animación actual
|
||||
void setCurrentFrame(int num); // Establece el frame actual de la animación
|
||||
void setAnimationCounter(const std::string &name, int num); // Establece el valor del contador
|
||||
@@ -56,13 +58,13 @@ class AnimatedSprite : public MovingSprite {
|
||||
auto getAnimationClip(const std::string &name = "default", Uint8 index = 0) -> SDL_Rect; // Devuelve el rectangulo de una animación y frame concreto
|
||||
auto getAnimationClip(int index_a = 0, Uint8 index_f = 0) -> SDL_Rect;
|
||||
|
||||
auto getIndex(const std::string &name) -> int; // Obtiene el indice de la animación a partir del nombre
|
||||
auto getIndex(const std::string &name) -> int; // Obtiene el indice de la animación a partir del nombre
|
||||
auto loadFromVector(const std::vector<std::string> *source) -> bool; // Carga la animación desde un vector
|
||||
|
||||
void setCurrentAnimation(const std::string &name = "default"); // Establece la animacion actual
|
||||
void setCurrentAnimation(int index = 0);
|
||||
|
||||
void update() override; // Actualiza las variables del objeto
|
||||
void update(float dt_s) override; // Actualiza las variables del objeto
|
||||
|
||||
void setAnimationFrames(Uint8 index_animation, Uint8 index_frame, int x, int y, int w, int h); // OLD - Establece el rectangulo para un frame de una animación
|
||||
void setAnimationCounter(int value); // OLD - Establece el contador para todas las animaciones
|
||||
|
||||
@@ -31,6 +31,7 @@ void Fade::init(Uint8 r, Uint8 g, Uint8 b) {
|
||||
enabled_ = false;
|
||||
finished_ = false;
|
||||
counter_ = 0;
|
||||
elapsed_s_ = 0.0F;
|
||||
r_ = r;
|
||||
g_ = g;
|
||||
b_ = b;
|
||||
@@ -155,11 +156,15 @@ void Fade::renderFadeRandomSquare() {
|
||||
}
|
||||
}
|
||||
|
||||
// Actualiza las variables internas
|
||||
void Fade::update() {
|
||||
if (enabled_) {
|
||||
counter_++;
|
||||
}
|
||||
// Actualiza les variables internes. `counter_` (Uint16, frames a la cadència
|
||||
// de referència 60Hz) es deriva de `elapsed_s_` perquè els helpers de
|
||||
// `render()` (renderFadeFullscreen / Center / RandomSquare) segueixin
|
||||
// llegint-lo igual que abans.
|
||||
void Fade::update(float dt_s) {
|
||||
if (!enabled_) { return; }
|
||||
elapsed_s_ += dt_s;
|
||||
constexpr float FADE_STEPS_PER_S = 60.0F;
|
||||
counter_ = static_cast<Uint16>(elapsed_s_ * FADE_STEPS_PER_S);
|
||||
}
|
||||
|
||||
// Activa el fade
|
||||
@@ -167,6 +172,7 @@ void Fade::activateFade() {
|
||||
enabled_ = true;
|
||||
finished_ = false;
|
||||
counter_ = 0;
|
||||
elapsed_s_ = 0.0F;
|
||||
squares_drawn_ = 0;
|
||||
last_square_ticks_ = 0;
|
||||
fullscreen_done_ = false;
|
||||
|
||||
@@ -18,7 +18,7 @@ class Fade {
|
||||
|
||||
void init(Uint8 r, Uint8 g, Uint8 b); // Inicializa las variables
|
||||
void render(); // Pinta una transición en pantalla
|
||||
void update(); // Actualiza las variables internas
|
||||
void update(float dt_s); // Actualiza las variables internas
|
||||
void activateFade(); // Activa el fade
|
||||
|
||||
[[nodiscard]] auto hasEnded() const -> bool; // Comprueba si ha terminado la transicion
|
||||
@@ -34,7 +34,8 @@ class Fade {
|
||||
SDL_Renderer *renderer_ = nullptr; // El renderizador de la ventana
|
||||
SDL_Texture *backbuffer_ = nullptr; // Textura para usar como backbuffer
|
||||
Type fade_type_{Type::FULLSCREEN}; // Tipo de fade a realizar
|
||||
Uint16 counter_ = 0; // Contador interno
|
||||
Uint16 counter_ = 0; // Contador intern (frame-based)
|
||||
float elapsed_s_ = 0.0F; // Acumulador de temps (time-based)
|
||||
bool enabled_ = false; // Indica si el fade está activo
|
||||
bool finished_ = false; // Indica si ha terminado la transición
|
||||
Uint8 r_ = 0, g_ = 0, b_ = 0; // Colores para el fade
|
||||
|
||||
@@ -34,22 +34,22 @@ void MovingSprite::clear() {
|
||||
center_ = nullptr;
|
||||
rotate_speed_ = 0;
|
||||
rotate_amount_ = 0.0;
|
||||
counter_ = 0;
|
||||
|
||||
current_flip_ = SDL_FLIP_NONE;
|
||||
}
|
||||
|
||||
// Mueve el sprite
|
||||
void MovingSprite::move() {
|
||||
// Mueve el sprite. vx_/vy_ en px/s, ax_/ay_ en px/s². Integració d'Euler
|
||||
// senzilla — suficient per a moviments sense col·lisions sensibles.
|
||||
void MovingSprite::move(float dt_s) {
|
||||
if (enabled_) {
|
||||
x_prev_ = x_;
|
||||
y_prev_ = y_;
|
||||
|
||||
x_ += vx_;
|
||||
y_ += vy_;
|
||||
x_ += vx_ * dt_s;
|
||||
y_ += vy_ * dt_s;
|
||||
|
||||
vx_ += ax_;
|
||||
vy_ += ay_;
|
||||
vx_ += ax_ * dt_s;
|
||||
vy_ += ay_ * dt_s;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,17 +180,6 @@ auto MovingSprite::getRotateSpeed() const -> Uint16 {
|
||||
return rotate_speed_;
|
||||
}
|
||||
|
||||
// Establece la rotacion
|
||||
void MovingSprite::rotate() {
|
||||
if (enabled_) {
|
||||
if (rotate_enabled_) {
|
||||
if (counter_ % rotate_speed_ == 0) {
|
||||
incAngle(rotate_amount_);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Establece el valor de la variable
|
||||
void MovingSprite::setRotate(bool value) {
|
||||
rotate_enabled_ = value;
|
||||
@@ -216,13 +205,15 @@ void MovingSprite::disableRotate() {
|
||||
angle_ = (double)0;
|
||||
}
|
||||
|
||||
// Actualiza las variables internas del objeto
|
||||
void MovingSprite::update() {
|
||||
move();
|
||||
rotate();
|
||||
|
||||
if (enabled_) {
|
||||
++counter_ %= 60000;
|
||||
// Actualiza les variables internes (move + rotació integrada). La rotació
|
||||
// frame-based original era `incAngle(rotate_amount_)` cada `rotate_speed_`
|
||||
// frames a 60Hz, equivalent a velocitat angular constant
|
||||
// = rotate_amount_ * 60 / rotate_speed_ graus/s.
|
||||
void MovingSprite::update(float dt_s) {
|
||||
move(dt_s);
|
||||
if (enabled_ && rotate_enabled_) {
|
||||
const double ANGULAR_VELOCITY_DEG_PER_S = rotate_amount_ * 60.0 / static_cast<double>(rotate_speed_);
|
||||
incAngle(ANGULAR_VELOCITY_DEG_PER_S * dt_s);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,11 +10,10 @@ class MovingSprite : public Sprite {
|
||||
public:
|
||||
explicit MovingSprite(float x = 0, float y = 0, int w = 0, int h = 0, float velx = 0, float vely = 0, float accelx = 0, float accely = 0, Texture *texture = nullptr, SDL_Renderer *renderer = nullptr); // Constructor
|
||||
|
||||
void move(); // Mueve el sprite
|
||||
void rotate(); // Rota el sprite
|
||||
virtual void update(); // Actualiza las variables internas del objeto
|
||||
void clear(); // Reinicia todas las variables
|
||||
void render() override; // Muestra el sprite por pantalla
|
||||
void move(float dt_s); // Mueve el sprite (vx/vy/ax/ay en px/s i px/s^2)
|
||||
virtual void update(float dt_s); // Actualiza les variables internes (move + rotació integrada)
|
||||
void clear(); // Reinicia todas las variables
|
||||
void render() override; // Muestra el sprite por pantalla
|
||||
|
||||
// cppcheck-suppress duplInheritedMember ; shadow intencional: Sprite::getPosX retorna int (sprites estàtics), MovingSprite::getPosX retorna float (sub-pixel). No s'accedeix via Sprite*: la jerarquia de joc treballa amb el tipus concret
|
||||
[[nodiscard]] auto getPosX() const -> float; // Obten el valor de la variable
|
||||
@@ -83,9 +82,8 @@ class MovingSprite : public Sprite {
|
||||
|
||||
double angle_{0.0}; // Angulo para dibujarlo
|
||||
bool rotate_enabled_{false}; // Indica si ha de rotar
|
||||
int rotate_speed_{0}; // Velocidad de giro
|
||||
double rotate_amount_{0.0}; // Cantidad de grados a girar en cada iteración
|
||||
int counter_{0}; // Contador interno
|
||||
int rotate_speed_{0}; // Velocidad de giro (frames per pas de rotació al ritme de referència 60Hz)
|
||||
double rotate_amount_{0.0}; // Cantidad de grados a girar en cada pas
|
||||
SDL_Point *center_{nullptr}; // Centro de rotación
|
||||
SDL_FlipMode current_flip_{SDL_FLIP_NONE}; // Indica como se voltea el sprite
|
||||
};
|
||||
|
||||
@@ -20,21 +20,18 @@ void SmartSprite::init() {
|
||||
on_destination_ = false;
|
||||
dest_x_ = 0;
|
||||
dest_y_ = 0;
|
||||
counter_ = 0;
|
||||
finished_ = false;
|
||||
}
|
||||
|
||||
// Actualiza la posición y comprueba si ha llegado a su destino
|
||||
void SmartSprite::update() {
|
||||
// La velocitat i acceleració són en px/s i px/s²; el temps de permanència
|
||||
// després d'arribar al destí ve donat per setRemainingTime().
|
||||
void SmartSprite::update(float dt_s) {
|
||||
if (enabled_) {
|
||||
// NOLINTNEXTLINE(bugprone-parent-virtual-call): salt deliberat a l'avi — SmartSprite hereta d'AnimatedSprite només per reutilitzar API, però no usa animació de frames, així que es salta AnimatedSprite::update() (que cridaria animate())
|
||||
MovingSprite::update();
|
||||
MovingSprite::update(dt_s);
|
||||
|
||||
// Comprueba el movimiento
|
||||
checkMove();
|
||||
|
||||
// Comprueba si ha terminado
|
||||
checkFinished();
|
||||
checkFinished(dt_s);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,6 +53,15 @@ void SmartSprite::setEnabledCounter(int value) {
|
||||
enabled_counter_ = value;
|
||||
}
|
||||
|
||||
// Time-based: temps de visibilitat post-arribada
|
||||
void SmartSprite::setRemainingTime(float seconds) {
|
||||
remaining_time_s_ = seconds;
|
||||
}
|
||||
|
||||
auto SmartSprite::getRemainingTime() const -> float {
|
||||
return remaining_time_s_;
|
||||
}
|
||||
|
||||
// Establece el valor de la variable
|
||||
void SmartSprite::setDestX(int x) {
|
||||
dest_x_ = x;
|
||||
@@ -129,16 +135,15 @@ void SmartSprite::checkMove() {
|
||||
}
|
||||
}
|
||||
|
||||
// Comprueba si ha terminado
|
||||
void SmartSprite::checkFinished() {
|
||||
// Comprueba si ha llegado a su destino
|
||||
// Decrementa el temps restant cada crida si està al destí
|
||||
void SmartSprite::checkFinished(float dt_s) {
|
||||
on_destination_ = getPosX() == dest_x_ && getPosY() == dest_y_;
|
||||
|
||||
if (on_destination_) { // Si esta en el destino comprueba su contador
|
||||
if (enabled_counter_ == 0) { // Si ha llegado a cero, deshabilita el objeto y lo marca como finalizado
|
||||
if (on_destination_) {
|
||||
if (remaining_time_s_ <= 0.0F) {
|
||||
finished_ = true;
|
||||
} else { // Si no ha llegado a cero, decrementa el contador
|
||||
enabled_counter_--;
|
||||
} else {
|
||||
remaining_time_s_ -= dt_s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,27 +10,30 @@ class SmartSprite : public AnimatedSprite {
|
||||
public:
|
||||
SmartSprite(Texture *texture, SDL_Renderer *renderer); // Constructor
|
||||
|
||||
void init(); // Inicializa el objeto
|
||||
void update() override; // Actualiza la posición y comprueba si ha llegado a su destino
|
||||
void render() override; // Pinta el objeto en pantalla
|
||||
void init(); // Inicializa el objeto
|
||||
void update(float dt_s) override; // Actualiza la posicion
|
||||
void render() override; // Pinta el objeto en pantalla
|
||||
|
||||
[[nodiscard]] auto getEnabledCounter() const -> int; // Obtiene el valor de la variable
|
||||
void setEnabledCounter(int value); // Establece el valor de la variable
|
||||
void setDestX(int x); // Establece el valor de la variable
|
||||
void setDestY(int y); // Establece el valor de la variable
|
||||
[[nodiscard]] auto getDestX() const -> int; // Obtiene el valor de la variable
|
||||
[[nodiscard]] auto getDestY() const -> int; // Obtiene el valor de la variable
|
||||
[[nodiscard]] auto isOnDestination() const -> bool; // Obtiene el valor de la variable
|
||||
[[nodiscard]] auto hasFinished() const -> bool; // Obtiene el valor de la variable
|
||||
[[nodiscard]] auto getEnabledCounter() const -> int; // Obtiene el valor de la variable
|
||||
void setEnabledCounter(int value); // Establece el valor de la variable
|
||||
void setRemainingTime(float seconds); // Time-based: temps que es queda visible despres d'arribar al desti
|
||||
[[nodiscard]] auto getRemainingTime() const -> float; // Time-based: temps restant
|
||||
void setDestX(int x); // Establece el valor de la variable
|
||||
void setDestY(int y); // Establece el valor de la variable
|
||||
[[nodiscard]] auto getDestX() const -> int; // Obtiene el valor de la variable
|
||||
[[nodiscard]] auto getDestY() const -> int; // Obtiene el valor de la variable
|
||||
[[nodiscard]] auto isOnDestination() const -> bool; // Obtiene el valor de la variable
|
||||
[[nodiscard]] auto hasFinished() const -> bool; // Obtiene el valor de la variable
|
||||
|
||||
private:
|
||||
// Variables
|
||||
bool on_destination_; // Indica si está en el destino
|
||||
int dest_x_; // Posicion de destino en el eje X
|
||||
int dest_y_; // Posicion de destino en el eje Y
|
||||
int enabled_counter_; // Contador para deshabilitarlo
|
||||
bool finished_; // Indica si ya ha terminado
|
||||
bool on_destination_; // Indica si está en el destino
|
||||
int dest_x_; // Posicion de destino en el eje X
|
||||
int dest_y_; // Posicion de destino en el eje Y
|
||||
int enabled_counter_; // Contador (frames, derivat de remaining_time_s_ * 60)
|
||||
float remaining_time_s_{0.0F}; // Temps restant per a deshabilitar-lo
|
||||
bool finished_; // Indica si ya ha terminado
|
||||
|
||||
void checkMove(); // Comprueba el movimiento
|
||||
void checkFinished(); // Comprueba si ha terminado
|
||||
void checkMove(); // Comprueba el movimiento
|
||||
void checkFinished(float dt_s); // Comprueba si ha terminado
|
||||
};
|
||||
|
||||
@@ -7,30 +7,27 @@ Writer::Writer(Text *text)
|
||||
: text_(text) {
|
||||
}
|
||||
|
||||
// Actualiza el objeto
|
||||
void Writer::update() {
|
||||
if (enabled_) {
|
||||
if (!completed_) { // No completado
|
||||
if (writing_counter_ > 0) {
|
||||
writing_counter_--;
|
||||
}
|
||||
// Avança un caracter cada `seconds_per_char_` i un cop completat es queda
|
||||
// visible `remaining_time_s_` segons abans de finalitzar.
|
||||
void Writer::update(float dt_s) {
|
||||
if (!enabled_) { return; }
|
||||
|
||||
else if (writing_counter_ == 0) {
|
||||
index_++;
|
||||
writing_counter_ = speed_;
|
||||
}
|
||||
|
||||
if (index_ == length_) {
|
||||
completed_ = true;
|
||||
}
|
||||
if (!completed_) {
|
||||
char_timer_s_ += dt_s;
|
||||
while (char_timer_s_ >= seconds_per_char_ && index_ < length_) {
|
||||
char_timer_s_ -= seconds_per_char_;
|
||||
++index_;
|
||||
}
|
||||
if (index_ >= length_) {
|
||||
completed_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (completed_) { // Completado
|
||||
if (enabled_counter_ > 0) {
|
||||
enabled_counter_--;
|
||||
} else if (enabled_counter_ == 0) {
|
||||
finished_ = true;
|
||||
}
|
||||
if (completed_) {
|
||||
if (remaining_time_s_ <= 0.0F) {
|
||||
finished_ = true;
|
||||
} else {
|
||||
remaining_time_s_ -= dt_s;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -63,10 +60,10 @@ void Writer::setCaption(const std::string &text) {
|
||||
length_ = text.length();
|
||||
}
|
||||
|
||||
// Establece el valor de la variable
|
||||
void Writer::setSpeed(int value) {
|
||||
speed_ = value;
|
||||
writing_counter_ = value;
|
||||
// Segons per caracter. Quan s'usa, l'update(dt) avança index.
|
||||
void Writer::setSecondsPerChar(float seconds) {
|
||||
seconds_per_char_ = seconds;
|
||||
char_timer_s_ = 0.0F;
|
||||
}
|
||||
|
||||
// Establece el valor de la variable
|
||||
@@ -79,14 +76,13 @@ auto Writer::isEnabled() const -> bool {
|
||||
return enabled_;
|
||||
}
|
||||
|
||||
// Establece el valor de la variable
|
||||
void Writer::setEnabledCounter(int time) {
|
||||
enabled_counter_ = time;
|
||||
// Temps que es mante visible despres de completar el text.
|
||||
void Writer::setRemainingTime(float seconds) {
|
||||
remaining_time_s_ = seconds;
|
||||
}
|
||||
|
||||
// Obtiene el valor de la variable
|
||||
auto Writer::getEnabledCounter() const -> int {
|
||||
return enabled_counter_;
|
||||
auto Writer::getRemainingTime() const -> float {
|
||||
return remaining_time_s_;
|
||||
}
|
||||
|
||||
// Centra la cadena de texto a un punto X
|
||||
|
||||
@@ -8,19 +8,19 @@ class Writer {
|
||||
public:
|
||||
explicit Writer(Text *text); // Constructor
|
||||
|
||||
void update(); // Actualiza el objeto
|
||||
void render(); // Dibuja el objeto en pantalla
|
||||
void update(float dt_s); // Actualiza el objeto
|
||||
void render(); // Dibuja el objeto en pantalla
|
||||
|
||||
void setPosX(int value); // Establece el valor de la variable
|
||||
void setPosY(int value); // Establece el valor de la variable
|
||||
void setKerning(int value); // Establece el valor de la variable
|
||||
void setCaption(const std::string &text); // Establece el valor de la variable
|
||||
void setSpeed(int value); // Establece el valor de la variable
|
||||
void setSecondsPerChar(float seconds); // Segons per caracter
|
||||
void setEnabled(bool value); // Establece el valor de la variable
|
||||
[[nodiscard]] auto isEnabled() const -> bool; // Obtiene el valor de la variable
|
||||
|
||||
void setEnabledCounter(int time); // Establece el valor de la variable
|
||||
[[nodiscard]] auto getEnabledCounter() const -> int; // Obtiene el valor de la variable
|
||||
void setRemainingTime(float seconds); // Temps despres de completar
|
||||
[[nodiscard]] auto getRemainingTime() const -> float; // Temps restant
|
||||
|
||||
void center(int x); // Centra la cadena de texto a un punto X
|
||||
[[nodiscard]] auto hasFinished() const -> bool; // Obtiene el valor de la variable
|
||||
@@ -30,16 +30,16 @@ class Writer {
|
||||
Text *text_; // Objeto encargado de escribir el texto
|
||||
|
||||
// Variables
|
||||
int pos_x_{0}; // Posicion en el eje X donde empezar a escribir el texto
|
||||
int pos_y_{0}; // Posicion en el eje Y donde empezar a escribir el texto
|
||||
int kerning_{0}; // Kerning del texto, es decir, espaciado entre caracteres
|
||||
std::string caption_; // El texto para escribir
|
||||
int speed_{0}; // Velocidad de escritura
|
||||
int writing_counter_{0}; // Temporizador de escritura para cada caracter
|
||||
int index_{0}; // Posición del texto que se está escribiendo
|
||||
int length_{0}; // Longitud de la cadena a escribir
|
||||
bool completed_{false}; // Indica si se ha escrito todo el texto
|
||||
bool enabled_{false}; // Indica si el objeto está habilitado
|
||||
int enabled_counter_{0}; // Temporizador para deshabilitar el objeto
|
||||
bool finished_{false}; // Indica si ya ha terminado
|
||||
int pos_x_{0}; // Posicion en el eje X donde empezar a escribir el texto
|
||||
int pos_y_{0}; // Posicion en el eje Y donde empezar a escribir el texto
|
||||
int kerning_{0}; // Kerning del texto, es decir, espaciado entre caracteres
|
||||
std::string caption_; // El texto para escribir
|
||||
float seconds_per_char_{0.0F}; // Segons per caracter
|
||||
float char_timer_s_{0.0F}; // Acumulador d'avanç de caracter
|
||||
int index_{0}; // Posición del texto que se está escribiendo
|
||||
int length_{0}; // Longitud de la cadena a escribir
|
||||
bool completed_{false}; // Indica si se ha escrito todo el texto
|
||||
bool enabled_{false}; // Indica si el objeto está habilitado
|
||||
float remaining_time_s_{0.0F}; // Temps restant per a deshabilitar
|
||||
bool finished_{false}; // Indica si ya ha terminado
|
||||
};
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
#include "core/system/delta_time.hpp"
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
namespace DeltaTime {
|
||||
|
||||
namespace {
|
||||
Uint64 last_time_ms = 0;
|
||||
}
|
||||
|
||||
void reset() {
|
||||
last_time_ms = SDL_GetTicks();
|
||||
}
|
||||
|
||||
auto tick() -> float {
|
||||
const Uint64 NOW_MS = SDL_GetTicks();
|
||||
const float DELTA_S = static_cast<float>(NOW_MS - last_time_ms) / 1000.0F;
|
||||
last_time_ms = NOW_MS;
|
||||
return DELTA_S;
|
||||
}
|
||||
|
||||
} // namespace DeltaTime
|
||||
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
// Font única de delta_time per al joc. El loop principal NO té vsync ni
|
||||
// gates: cada escena crida `tick()` al començament del seu iterate() i rep
|
||||
// els segons reals transcorreguts des de l'última crida. Així el moviment és
|
||||
// independent del framerate (visualment suau a 2000 FPS o a 60 FPS).
|
||||
//
|
||||
// `reset()` reinicia el rellotge intern: cal cridar-lo en cada canvi
|
||||
// d'escena (després de càrregues llargues que podrien generar un primer
|
||||
// delta enorme) i quan es reprèn d'una pausa.
|
||||
|
||||
namespace DeltaTime {
|
||||
|
||||
// Reinicia el rellotge a "ara". Cap delta acumulat del passat.
|
||||
void reset();
|
||||
|
||||
// Retorna els segons des de l'última crida a `tick()` o `reset()`.
|
||||
auto tick() -> float;
|
||||
|
||||
} // namespace DeltaTime
|
||||
+142
-234
@@ -1,6 +1,7 @@
|
||||
#include "game/entities/balloon.h"
|
||||
|
||||
#include <cmath> // for std::fabs
|
||||
#include <algorithm> // for std::max
|
||||
#include <cmath> // for std::fabs
|
||||
|
||||
#include "core/rendering/animatedsprite.h" // for AnimatedSprite
|
||||
#include "core/rendering/movingsprite.h" // for MovingSprite
|
||||
@@ -17,194 +18,144 @@ Balloon::Balloon(float x, float y, Uint8 kind, float velx, float speed, Uint16 c
|
||||
|
||||
switch (kind) {
|
||||
case BALLOON_1:
|
||||
// Alto y ancho del objeto
|
||||
width_ = WIDTH_1;
|
||||
height_ = WIDTH_1;
|
||||
size_ = SIZE_1;
|
||||
power_ = 1;
|
||||
|
||||
// Inicializa los valores de velocidad y gravedad
|
||||
this->vel_x_ = velx;
|
||||
vel_y_ = 0;
|
||||
max_vel_y_ = 3.0F;
|
||||
gravity_ = 0.09F;
|
||||
default_vel_y_ = 2.6F;
|
||||
vel_x_s_ = velx;
|
||||
vel_y_s_ = 0.0F;
|
||||
max_vel_y_s_ = 180.0F;
|
||||
gravity_s_ = 324.0F;
|
||||
default_vel_y_s_ = 156.0F;
|
||||
|
||||
// Puntos que da el globo al ser destruido
|
||||
score_ = SCORE_1;
|
||||
|
||||
// Amenaza que genera el globo
|
||||
menace_ = 1;
|
||||
|
||||
break;
|
||||
|
||||
case BALLOON_2:
|
||||
// Alto y ancho del objeto
|
||||
width_ = WIDTH_2;
|
||||
height_ = WIDTH_2;
|
||||
size_ = SIZE_2;
|
||||
power_ = 3;
|
||||
|
||||
// Inicializa los valores de velocidad y gravedad
|
||||
this->vel_x_ = velx;
|
||||
vel_y_ = 0;
|
||||
max_vel_y_ = 3.0F;
|
||||
gravity_ = 0.10F;
|
||||
default_vel_y_ = 3.5F;
|
||||
vel_x_s_ = velx;
|
||||
vel_y_s_ = 0.0F;
|
||||
max_vel_y_s_ = 180.0F;
|
||||
gravity_s_ = 360.0F;
|
||||
default_vel_y_s_ = 210.0F;
|
||||
|
||||
// Puntos que da el globo al ser destruido
|
||||
score_ = SCORE_2;
|
||||
|
||||
// Amenaza que genera el globo
|
||||
menace_ = 2;
|
||||
|
||||
break;
|
||||
|
||||
case BALLOON_3:
|
||||
// Alto y ancho del objeto
|
||||
width_ = WIDTH_3;
|
||||
height_ = WIDTH_3;
|
||||
size_ = SIZE_3;
|
||||
power_ = 7;
|
||||
|
||||
// Inicializa los valores de velocidad y gravedad
|
||||
this->vel_x_ = velx;
|
||||
vel_y_ = 0;
|
||||
max_vel_y_ = 3.0F;
|
||||
gravity_ = 0.10F;
|
||||
default_vel_y_ = 4.50F;
|
||||
vel_x_s_ = velx;
|
||||
vel_y_s_ = 0.0F;
|
||||
max_vel_y_s_ = 180.0F;
|
||||
gravity_s_ = 360.0F;
|
||||
default_vel_y_s_ = 270.0F;
|
||||
|
||||
// Puntos que da el globo al ser destruido
|
||||
score_ = SCORE_3;
|
||||
|
||||
// Amenaza que genera el globo
|
||||
menace_ = 4;
|
||||
|
||||
break;
|
||||
|
||||
case BALLOON_4:
|
||||
// Alto y ancho del objeto
|
||||
width_ = WIDTH_4;
|
||||
height_ = WIDTH_4;
|
||||
size_ = SIZE_4;
|
||||
power_ = 15;
|
||||
|
||||
// Inicializa los valores de velocidad y gravedad
|
||||
this->vel_x_ = velx;
|
||||
vel_y_ = 0;
|
||||
max_vel_y_ = 3.0F;
|
||||
gravity_ = 0.10F;
|
||||
default_vel_y_ = 4.95F;
|
||||
vel_x_s_ = velx;
|
||||
vel_y_s_ = 0.0F;
|
||||
max_vel_y_s_ = 180.0F;
|
||||
gravity_s_ = 360.0F;
|
||||
default_vel_y_s_ = 297.0F;
|
||||
|
||||
// Puntos que da el globo al ser destruido
|
||||
score_ = SCORE_4;
|
||||
|
||||
// Amenaza que genera el globo
|
||||
menace_ = 8;
|
||||
|
||||
break;
|
||||
|
||||
case HEXAGON_1:
|
||||
// Alto y ancho del objeto
|
||||
width_ = WIDTH_1;
|
||||
height_ = WIDTH_1;
|
||||
size_ = SIZE_1;
|
||||
power_ = 1;
|
||||
|
||||
// Inicializa los valores de velocidad y gravedad
|
||||
this->vel_x_ = velx;
|
||||
vel_y_ = std::fabs(velx) * 2;
|
||||
max_vel_y_ = std::fabs(velx) * 2;
|
||||
gravity_ = 0.00F;
|
||||
default_vel_y_ = std::fabs(velx) * 2;
|
||||
vel_x_s_ = velx;
|
||||
vel_y_s_ = std::fabs(velx) * 2.0F;
|
||||
max_vel_y_s_ = std::fabs(velx) * 2.0F;
|
||||
gravity_s_ = 0.0F;
|
||||
default_vel_y_s_ = std::fabs(velx) * 2.0F;
|
||||
|
||||
// Puntos que da el globo al ser destruido
|
||||
score_ = SCORE_1;
|
||||
|
||||
// Amenaza que genera el globo
|
||||
menace_ = 1;
|
||||
|
||||
break;
|
||||
|
||||
case HEXAGON_2:
|
||||
// Alto y ancho del objeto
|
||||
width_ = WIDTH_2;
|
||||
height_ = WIDTH_2;
|
||||
size_ = SIZE_2;
|
||||
power_ = 3;
|
||||
|
||||
// Inicializa los valores de velocidad y gravedad
|
||||
this->vel_x_ = velx;
|
||||
vel_y_ = std::fabs(velx) * 2;
|
||||
max_vel_y_ = std::fabs(velx) * 2;
|
||||
gravity_ = 0.00F;
|
||||
default_vel_y_ = std::fabs(velx) * 2;
|
||||
vel_x_s_ = velx;
|
||||
vel_y_s_ = std::fabs(velx) * 2.0F;
|
||||
max_vel_y_s_ = std::fabs(velx) * 2.0F;
|
||||
gravity_s_ = 0.0F;
|
||||
default_vel_y_s_ = std::fabs(velx) * 2.0F;
|
||||
|
||||
// Puntos que da el globo al ser destruido
|
||||
score_ = SCORE_2;
|
||||
|
||||
// Amenaza que genera el globo
|
||||
menace_ = 2;
|
||||
|
||||
break;
|
||||
|
||||
case HEXAGON_3:
|
||||
// Alto y ancho del objeto
|
||||
width_ = WIDTH_3;
|
||||
height_ = WIDTH_3;
|
||||
size_ = SIZE_3;
|
||||
power_ = 7;
|
||||
|
||||
// Inicializa los valores de velocidad y gravedad
|
||||
this->vel_x_ = velx;
|
||||
vel_y_ = std::fabs(velx) * 2;
|
||||
max_vel_y_ = std::fabs(velx) * 2;
|
||||
gravity_ = 0.00F;
|
||||
default_vel_y_ = std::fabs(velx) * 2;
|
||||
vel_x_s_ = velx;
|
||||
vel_y_s_ = std::fabs(velx) * 2.0F;
|
||||
max_vel_y_s_ = std::fabs(velx) * 2.0F;
|
||||
gravity_s_ = 0.0F;
|
||||
default_vel_y_s_ = std::fabs(velx) * 2.0F;
|
||||
|
||||
// Puntos que da el globo al ser destruido
|
||||
score_ = SCORE_3;
|
||||
|
||||
// Amenaza que genera el globo
|
||||
menace_ = 4;
|
||||
|
||||
break;
|
||||
|
||||
case HEXAGON_4:
|
||||
// Alto y ancho del objeto
|
||||
width_ = WIDTH_4;
|
||||
height_ = WIDTH_4;
|
||||
size_ = SIZE_4;
|
||||
power_ = 15;
|
||||
|
||||
// Inicializa los valores de velocidad y gravedad
|
||||
this->vel_x_ = velx;
|
||||
vel_y_ = std::fabs(velx) * 2;
|
||||
max_vel_y_ = std::fabs(velx) * 2;
|
||||
gravity_ = 0.00F;
|
||||
default_vel_y_ = std::fabs(velx) * 2;
|
||||
vel_x_s_ = velx;
|
||||
vel_y_s_ = std::fabs(velx) * 2.0F;
|
||||
max_vel_y_s_ = std::fabs(velx) * 2.0F;
|
||||
gravity_s_ = 0.0F;
|
||||
default_vel_y_s_ = std::fabs(velx) * 2.0F;
|
||||
|
||||
// Puntos que da el globo al ser destruido
|
||||
score_ = SCORE_4;
|
||||
|
||||
// Amenaza que genera el globo
|
||||
menace_ = 8;
|
||||
|
||||
break;
|
||||
|
||||
case POWER_BALL:
|
||||
// Alto y ancho del objeto
|
||||
width_ = WIDTH_4;
|
||||
height_ = WIDTH_4;
|
||||
size_ = 4;
|
||||
power_ = 0;
|
||||
|
||||
// Inicializa los valores de velocidad y gravedad
|
||||
this->vel_x_ = velx;
|
||||
vel_y_ = 0;
|
||||
max_vel_y_ = 3.0F;
|
||||
gravity_ = 0.10F;
|
||||
default_vel_y_ = 4.95F;
|
||||
vel_x_s_ = velx;
|
||||
vel_y_s_ = 0.0F;
|
||||
max_vel_y_s_ = 180.0F;
|
||||
gravity_s_ = 360.0F;
|
||||
default_vel_y_s_ = 297.0F;
|
||||
|
||||
// Puntos que da el globo al ser destruido
|
||||
score_ = 0;
|
||||
@@ -217,7 +168,7 @@ Balloon::Balloon(float x, float y, Uint8 kind, float velx, float speed, Uint16 c
|
||||
// sempre que canvia. setRotateSpeed(1) evita la UB del `counter_
|
||||
// % 0` dins MovingSprite::rotate().
|
||||
sprite_->setRotateSpeed(1);
|
||||
sprite_->setRotateAmount(vel_x_ > 0.0F ? 2.0 : -2.0);
|
||||
sprite_->setRotateAmount(vel_x_s_ > 0.0F ? 2.0 : -2.0);
|
||||
|
||||
break;
|
||||
|
||||
@@ -257,18 +208,21 @@ Balloon::Balloon(float x, float y, Uint8 kind, float velx, float speed, Uint16 c
|
||||
// Inicializa variables
|
||||
stopped_ = true;
|
||||
stopped_counter_ = 0;
|
||||
stopped_counter_s_ = 0.0F;
|
||||
blinking_ = false;
|
||||
visible_ = true;
|
||||
creation_counter_ = creationtimer;
|
||||
creation_counter_ini_ = creationtimer;
|
||||
creation_counter_s_ = static_cast<float>(creationtimer) / 60.0F;
|
||||
creation_counter_ini_s_ = creation_counter_s_;
|
||||
creation_phase_s_ = 0.0F;
|
||||
bounce_phase_s_ = 0.0F;
|
||||
popping_ = false;
|
||||
|
||||
// Valores iniciales dependentes del timer
|
||||
being_created_ = creation_counter_ != 0;
|
||||
invulnerable_ = being_created_;
|
||||
|
||||
counter_ = 0;
|
||||
travel_y_ = 1.0F;
|
||||
this->speed_ = speed;
|
||||
|
||||
// Tipo
|
||||
@@ -334,87 +288,38 @@ void Balloon::render() {
|
||||
}
|
||||
}
|
||||
|
||||
// Actualiza la posición y estados del globo
|
||||
void Balloon::move() {
|
||||
// Comprueba si se puede mover
|
||||
// Actualiza la posición y estados del globo. Integració contínua: gravetat i
|
||||
// posició s'apliquen escalades per `speed_` (tempo del joc) cada tick.
|
||||
void Balloon::move(float dt_s) {
|
||||
if (!isStopped()) {
|
||||
// Lo mueve a izquierda o derecha
|
||||
pos_x_ += (vel_x_ * speed_);
|
||||
// Eix X
|
||||
pos_x_ += vel_x_s_ * speed_ * dt_s;
|
||||
|
||||
// Si queda fuera de pantalla, corregimos su posición y cambiamos su sentido
|
||||
if ((pos_x_ < PLAY_AREA_LEFT) || (pos_x_ + width_ > PLAY_AREA_RIGHT)) {
|
||||
// Corrige posición
|
||||
pos_x_ -= (vel_x_ * speed_);
|
||||
|
||||
// Invierte sentido
|
||||
vel_x_ = -vel_x_;
|
||||
|
||||
// Invierte la rotación
|
||||
pos_x_ -= vel_x_s_ * speed_ * dt_s;
|
||||
vel_x_s_ = -vel_x_s_;
|
||||
sprite_->switchRotate();
|
||||
|
||||
// Activa el efecto de rebote
|
||||
if (kind_ != POWER_BALL) {
|
||||
bounceStart();
|
||||
}
|
||||
if (kind_ != POWER_BALL) { bounceStart(); }
|
||||
}
|
||||
|
||||
// Mueve el globo hacia arriba o hacia abajo
|
||||
pos_y_ += (vel_y_ * speed_);
|
||||
// Eix Y
|
||||
pos_y_ += vel_y_s_ * speed_ * dt_s;
|
||||
|
||||
// Si se sale por arriba
|
||||
if (pos_y_ < PLAY_AREA_TOP) {
|
||||
// Corrige
|
||||
pos_y_ = PLAY_AREA_TOP;
|
||||
|
||||
// Invierte sentido
|
||||
vel_y_ = -vel_y_;
|
||||
|
||||
// Activa el efecto de rebote
|
||||
if (kind_ != POWER_BALL) {
|
||||
bounceStart();
|
||||
}
|
||||
vel_y_s_ = -vel_y_s_;
|
||||
if (kind_ != POWER_BALL) { bounceStart(); }
|
||||
}
|
||||
|
||||
// Si el globo se sale por la parte inferior
|
||||
if (pos_y_ + height_ > PLAY_AREA_BOTTOM) {
|
||||
// Corrige
|
||||
pos_y_ = PLAY_AREA_BOTTOM - height_;
|
||||
|
||||
// Invierte colocando una velocidad por defecto
|
||||
vel_y_ = -default_vel_y_;
|
||||
|
||||
// Activa el efecto de rebote
|
||||
if (kind_ != POWER_BALL) {
|
||||
bounceStart();
|
||||
}
|
||||
vel_y_s_ = -default_vel_y_s_;
|
||||
if (kind_ != POWER_BALL) { bounceStart(); }
|
||||
}
|
||||
|
||||
/*
|
||||
// Gravetat contínua (el tempo `speed_` escala també la gravetat).
|
||||
vel_y_s_ += gravity_s_ * speed_ * dt_s;
|
||||
|
||||
Para aplicar la gravedad, el diseño original la aplicaba en cada iteración del bucle
|
||||
Al añadir el modificador de velocidad se reduce la distancia que recorre el objeto y por
|
||||
tanto recibe mas gravedad. Para solucionarlo se va a aplicar la gravedad cuando se haya
|
||||
recorrido una distancia igual a la velocidad en Y, que era el cálculo inicial
|
||||
|
||||
*/
|
||||
|
||||
// Incrementa la variable que calcula la distancia acumulada en Y
|
||||
travel_y_ += speed_;
|
||||
|
||||
// Si la distancia acumulada en Y es igual a la velocidad, se aplica la gravedad
|
||||
if (travel_y_ >= 1.0F) {
|
||||
// Quita el excedente
|
||||
travel_y_ -= 1.0F;
|
||||
|
||||
// Aplica la gravedad al objeto sin pasarse de una velocidad máxima
|
||||
vel_y_ += gravity_;
|
||||
|
||||
// Al parecer esta asignación se quedó sin hacer y ahora el juego no funciona
|
||||
// correctamente si se aplica, así que se deja sin efecto
|
||||
// velY = std::min(velY, maxVelY);
|
||||
}
|
||||
|
||||
// Actualiza la posición del sprite
|
||||
sprite_->setPosX(getPosX());
|
||||
sprite_->setPosY(getPosY());
|
||||
}
|
||||
@@ -427,16 +332,19 @@ void Balloon::disable() {
|
||||
collider_.r = 0;
|
||||
collider_.x = 0;
|
||||
collider_.y = 0;
|
||||
counter_ = 0;
|
||||
creation_counter_ = 0;
|
||||
creation_counter_ini_ = 0;
|
||||
default_vel_y_ = 0.0F;
|
||||
creation_counter_s_ = 0.0F;
|
||||
creation_counter_ini_s_ = 0.0F;
|
||||
creation_phase_s_ = 0.0F;
|
||||
bounce_phase_s_ = 0.0F;
|
||||
default_vel_y_s_ = 0.0F;
|
||||
enabled_ = false;
|
||||
gravity_ = 0.0F;
|
||||
gravity_s_ = 0.0F;
|
||||
height_ = 0;
|
||||
invulnerable_ = false;
|
||||
kind_ = 0;
|
||||
max_vel_y_ = 0.0F;
|
||||
max_vel_y_s_ = 0.0F;
|
||||
menace_ = 0;
|
||||
popping_ = false;
|
||||
pos_x_ = 0.0F;
|
||||
@@ -447,9 +355,9 @@ void Balloon::disable() {
|
||||
speed_ = 0;
|
||||
stopped_ = false;
|
||||
stopped_counter_ = 0;
|
||||
travel_y_ = 0;
|
||||
vel_x_ = 0.0F;
|
||||
vel_y_ = 0.0F;
|
||||
stopped_counter_s_ = 0.0F;
|
||||
vel_x_s_ = 0.0F;
|
||||
vel_y_s_ = 0.0F;
|
||||
visible_ = false;
|
||||
width_ = 0;
|
||||
sprite_->clear();
|
||||
@@ -466,30 +374,29 @@ void Balloon::pop() {
|
||||
}
|
||||
|
||||
// Actualiza al globo a su posicion, animación y controla los contadores
|
||||
void Balloon::update() {
|
||||
void Balloon::update(float dt_s) {
|
||||
if (enabled_) {
|
||||
sprite_->MovingSprite::update();
|
||||
move();
|
||||
updateAnimation();
|
||||
// MovingSprite::update(dt_s) avança la rotació (entre altres). La posició
|
||||
// del sprite la posa move(dt_s) directament des de pos_x_/pos_y_.
|
||||
sprite_->MovingSprite::update(dt_s);
|
||||
move(dt_s);
|
||||
updateAnimation(dt_s);
|
||||
updateColliders();
|
||||
updateState();
|
||||
updateBounce();
|
||||
counter_++;
|
||||
updateState(dt_s);
|
||||
updateBounce(dt_s);
|
||||
}
|
||||
}
|
||||
|
||||
// Actualiza los estados del globo
|
||||
void Balloon::updateState() {
|
||||
void Balloon::updateState(float dt_s) {
|
||||
if (isPopping()) {
|
||||
updateStatePopping();
|
||||
}
|
||||
|
||||
if (isBeingCreated()) {
|
||||
updateStateBeingCreated();
|
||||
}
|
||||
// Solo comprueba el estado detenido cuando no se está creando
|
||||
else if (isStopped()) {
|
||||
updateStateStopped();
|
||||
updateStateBeingCreated(dt_s);
|
||||
} else if (isStopped()) {
|
||||
updateStateStopped(dt_s);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -502,58 +409,54 @@ void Balloon::updateStatePopping() {
|
||||
}
|
||||
}
|
||||
|
||||
// Rama de updateState: globo creándose
|
||||
void Balloon::updateStateBeingCreated() {
|
||||
// Rama de updateState: globo creándose. Manté el chunk pattern original:
|
||||
// cada CREATION_STEP_S s'aplica un step de drift (equivalent a "cada 10
|
||||
// frames" del codi original). El drift en X usa vel_x_s_/60 per a obtenir
|
||||
// el mateix delta px-per-step.
|
||||
void Balloon::updateStateBeingCreated(float dt_s) {
|
||||
setStop(true);
|
||||
setInvulnerable(true);
|
||||
|
||||
// Todavia tiene tiempo en el contador
|
||||
if (creation_counter_ > 0) {
|
||||
// Desplaza lentamente el globo hacia abajo y hacia un lado
|
||||
if (creation_counter_ % 10 == 0) {
|
||||
pos_y_++;
|
||||
pos_x_ += vel_x_;
|
||||
if (creation_counter_s_ > 0.0F) {
|
||||
creation_phase_s_ += dt_s;
|
||||
while (creation_phase_s_ >= CREATION_STEP_S) {
|
||||
creation_phase_s_ -= CREATION_STEP_S;
|
||||
pos_y_ += 1.0F;
|
||||
const float DRIFT_X = vel_x_s_ / 60.0F;
|
||||
pos_x_ += DRIFT_X;
|
||||
|
||||
// Comprueba no se salga por los laterales
|
||||
if ((pos_x_ < PLAY_AREA_LEFT) || (pos_x_ > (PLAY_AREA_RIGHT - width_))) {
|
||||
// Corrige y cambia el sentido de la velocidad
|
||||
pos_x_ -= vel_x_;
|
||||
vel_x_ = -vel_x_;
|
||||
pos_x_ -= DRIFT_X;
|
||||
vel_x_s_ = -vel_x_s_;
|
||||
}
|
||||
|
||||
// Actualiza la posición del sprite
|
||||
sprite_->setPosX(getPosX());
|
||||
sprite_->setPosY(getPosY());
|
||||
|
||||
// Actualiza la posición del circulo de colisión
|
||||
updateColliders();
|
||||
}
|
||||
|
||||
creation_counter_--;
|
||||
}
|
||||
// El contador ha llegado a cero
|
||||
else {
|
||||
creation_counter_s_ = std::max(0.0F, creation_counter_s_ - dt_s);
|
||||
creation_counter_ = static_cast<Uint16>(creation_counter_s_ * 60.0F);
|
||||
} else {
|
||||
setBeingCreated(false);
|
||||
setStop(false); // reactiva la rotació de la PowerBall si escau
|
||||
setStop(false);
|
||||
setVisible(true);
|
||||
setInvulnerable(false);
|
||||
}
|
||||
}
|
||||
|
||||
// Rama de updateState: globo detenido (no creándose)
|
||||
void Balloon::updateStateStopped() {
|
||||
// Reduce el contador
|
||||
if (stopped_counter_ > 0) {
|
||||
stopped_counter_--;
|
||||
}
|
||||
// Quitarles el estado "detenido" si no estan explosionando
|
||||
else if (!isPopping()) {
|
||||
setStop(false); // reactiva la rotació de la PowerBall si escau
|
||||
void Balloon::updateStateStopped(float dt_s) {
|
||||
if (stopped_counter_s_ > 0.0F) {
|
||||
stopped_counter_s_ = std::max(0.0F, stopped_counter_s_ - dt_s);
|
||||
stopped_counter_ = static_cast<Uint16>(stopped_counter_s_ * 60.0F);
|
||||
} else if (!isPopping()) {
|
||||
setStop(false);
|
||||
}
|
||||
}
|
||||
|
||||
// Establece la animación correspondiente al estado
|
||||
void Balloon::updateAnimation() {
|
||||
void Balloon::updateAnimation(float dt_s) {
|
||||
std::string creating_animation = "blue";
|
||||
std::string normal_animation = "orange";
|
||||
|
||||
@@ -565,7 +468,6 @@ void Balloon::updateAnimation() {
|
||||
normal_animation = "green";
|
||||
}
|
||||
|
||||
// Establece el frame de animación
|
||||
if (isPopping()) {
|
||||
sprite_->setCurrentAnimation("pop");
|
||||
} else if (isBeingCreated()) {
|
||||
@@ -574,7 +476,7 @@ void Balloon::updateAnimation() {
|
||||
sprite_->setCurrentAnimation(normal_animation);
|
||||
}
|
||||
|
||||
sprite_->animate();
|
||||
sprite_->animate(dt_s);
|
||||
}
|
||||
|
||||
// Comprueba si el globo está habilitado
|
||||
@@ -592,11 +494,6 @@ auto Balloon::getPosY() const -> float {
|
||||
return pos_y_;
|
||||
}
|
||||
|
||||
// Obtiene del valor de la variable
|
||||
auto Balloon::getVelY() const -> float {
|
||||
return vel_y_;
|
||||
}
|
||||
|
||||
// Obtiene del valor de la variable
|
||||
auto Balloon::getWidth() const -> int {
|
||||
return width_;
|
||||
@@ -607,9 +504,9 @@ auto Balloon::getHeight() const -> int {
|
||||
return height_;
|
||||
}
|
||||
|
||||
// Establece el valor de la variable
|
||||
// Establece el valor de la variable (px/s)
|
||||
void Balloon::setVelY(float vel_y) {
|
||||
this->vel_y_ = vel_y;
|
||||
this->vel_y_s_ = vel_y;
|
||||
}
|
||||
|
||||
// Establece el valor de la variable
|
||||
@@ -709,6 +606,7 @@ auto Balloon::isPopping() const -> bool {
|
||||
// Establece el valor de la variable
|
||||
void Balloon::setStoppedTimer(Uint16 time) {
|
||||
stopped_counter_ = time;
|
||||
stopped_counter_s_ = static_cast<float>(time) / 60.0F;
|
||||
}
|
||||
|
||||
// Obtiene del valor de la variable
|
||||
@@ -766,17 +664,27 @@ void Balloon::bounceStop() {
|
||||
bouncing_.desp_y = 0.0F;
|
||||
}
|
||||
|
||||
void Balloon::updateBounce() {
|
||||
if (bouncing_.enabled) {
|
||||
bouncing_.zoom_width = bouncing_.w[bouncing_.counter / bouncing_.speed];
|
||||
bouncing_.zoom_height = bouncing_.h[bouncing_.counter / bouncing_.speed];
|
||||
sprite_->setZoomW(bouncing_.zoom_width);
|
||||
sprite_->setZoomH(bouncing_.zoom_height);
|
||||
bouncing_.desp_x = (sprite_->getSpriteClip().w - (sprite_->getSpriteClip().w * bouncing_.zoom_width));
|
||||
bouncing_.desp_y = (sprite_->getSpriteClip().h - (sprite_->getSpriteClip().h * bouncing_.zoom_height));
|
||||
void Balloon::updateBounce(float dt_s) {
|
||||
if (!bouncing_.enabled) { return; }
|
||||
|
||||
bounce_phase_s_ += dt_s;
|
||||
const float STEP_S = static_cast<float>(bouncing_.speed) * BOUNCE_STEP_S;
|
||||
while (bounce_phase_s_ >= STEP_S) {
|
||||
bounce_phase_s_ -= STEP_S;
|
||||
bouncing_.counter++;
|
||||
if ((bouncing_.counter / bouncing_.speed) > (MAX_BOUNCE - 1)) {
|
||||
bounceStop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const int IDX = bouncing_.counter / bouncing_.speed;
|
||||
if (IDX > (MAX_BOUNCE - 1)) {
|
||||
bounceStop();
|
||||
bounce_phase_s_ = 0.0F;
|
||||
return;
|
||||
}
|
||||
|
||||
bouncing_.zoom_width = bouncing_.w[IDX];
|
||||
bouncing_.zoom_height = bouncing_.h[IDX];
|
||||
sprite_->setZoomW(bouncing_.zoom_width);
|
||||
sprite_->setZoomH(bouncing_.zoom_height);
|
||||
bouncing_.desp_x = (sprite_->getSpriteClip().w - (sprite_->getSpriteClip().w * bouncing_.zoom_width));
|
||||
bouncing_.desp_y = (sprite_->getSpriteClip().h - (sprite_->getSpriteClip().h * bouncing_.zoom_height));
|
||||
}
|
||||
|
||||
@@ -39,9 +39,9 @@ class Balloon {
|
||||
static constexpr int BALLOON_CLASS = 0;
|
||||
static constexpr int HEXAGON_CLASS = 1;
|
||||
|
||||
// Velocidad del globo
|
||||
static constexpr float VELX_POSITIVE = 0.7F;
|
||||
static constexpr float VELX_NEGATIVE = -0.7F;
|
||||
// Velocitat horitzontal en px/s (era 0.7 px/frame * 60).
|
||||
static constexpr float VELX_POSITIVE = 42.0F;
|
||||
static constexpr float VELX_NEGATIVE = -42.0F;
|
||||
|
||||
// Velocidades a las que se mueven los globos
|
||||
static constexpr float SPEED_1 = 0.60F;
|
||||
@@ -66,12 +66,12 @@ class Balloon {
|
||||
Balloon(const Balloon &) = delete;
|
||||
auto operator=(const Balloon &) -> Balloon & = delete;
|
||||
|
||||
void allignTo(int x); // Centra el globo en la posición X
|
||||
void render(); // Pinta el globo en la pantalla
|
||||
void move(); // Actualiza la posición y estados del globo
|
||||
void disable(); // Deshabilita el globo y pone a cero todos los valores
|
||||
void pop(); // Explosiona el globo
|
||||
void update(); // Actualiza al globo a su posicion, animación y controla los contadores
|
||||
void allignTo(int x); // Centra el globo en la posición X
|
||||
void render(); // Pinta el globo en la pantalla
|
||||
void move(float dt_s); // Actualiza la posición y estados del globo
|
||||
void disable(); // Deshabilita el globo y pone a cero todos los valores
|
||||
void pop(); // Explosiona el globo
|
||||
void update(float dt_s); // Actualiza al globo
|
||||
|
||||
[[nodiscard]] auto isEnabled() const -> bool; // Comprueba si el globo está habilitado
|
||||
[[nodiscard]] auto isStopped() const -> bool; // Obtiene del valor de la variable
|
||||
@@ -83,7 +83,6 @@ class Balloon {
|
||||
|
||||
[[nodiscard]] auto getPosX() const -> float; // Obtiene del valor de la variable
|
||||
[[nodiscard]] auto getPosY() const -> float; // Obtiene del valor de la variable
|
||||
[[nodiscard]] auto getVelY() const -> float; // Obtiene del valor de la variable
|
||||
[[nodiscard]] auto getWidth() const -> int; // Obtiene del valor de la variable
|
||||
[[nodiscard]] auto getHeight() const -> int; // Obtiene del valor de la variable
|
||||
[[nodiscard]] auto getKind() const -> int; // Obtiene del valor de la variable
|
||||
@@ -109,6 +108,11 @@ class Balloon {
|
||||
// Cantidad de elementos del vector con los valores de la deformación del globo al rebotar
|
||||
static constexpr int MAX_BOUNCE = 10;
|
||||
|
||||
// Time-based: la creació "desplaça" el globus cada 10 frames a 60Hz ⇒ 1/6 s.
|
||||
static constexpr float CREATION_STEP_S = 10.0F / 60.0F;
|
||||
// Time-based: el bounce avança w/h cada `speed_` frames a 60Hz; mantenim el mateix temps per pas.
|
||||
static constexpr float BOUNCE_STEP_S = 1.0F / 60.0F; // 1 frame
|
||||
|
||||
// Estructura para las variables para el efecto de los rebotes
|
||||
struct Bouncing {
|
||||
bool enabled; // Si el efecto está activo
|
||||
@@ -126,47 +130,50 @@ class Balloon {
|
||||
AnimatedSprite *sprite_; // Sprite del objeto globo
|
||||
|
||||
// Variables
|
||||
float pos_x_; // Posición en el eje X
|
||||
float pos_y_; // Posición en el eje Y
|
||||
Uint8 width_; // Ancho
|
||||
Uint8 height_; // Alto
|
||||
float vel_x_; // Velocidad en el eje X. Cantidad de pixeles a desplazarse
|
||||
float vel_y_; // Velocidad en el eje Y. Cantidad de pixeles a desplazarse
|
||||
float gravity_; // Aceleración en el eje Y. Modifica la velocidad
|
||||
float default_vel_y_; // Velocidad inicial que tienen al rebotar contra el suelo
|
||||
float max_vel_y_; // Máxima velocidad que puede alcanzar el objeto en el eje Y
|
||||
bool being_created_; // Indica si el globo se está creando
|
||||
bool blinking_; // Indica si el globo está intermitente
|
||||
bool enabled_; // Indica si el globo esta activo
|
||||
bool invulnerable_; // Indica si el globo es invulnerable
|
||||
bool popping_; // Indica si el globo está explotando
|
||||
bool stopped_; // Indica si el globo está parado
|
||||
bool visible_; // Indica si el globo es visible
|
||||
Circle collider_; // Circulo de colisión del objeto
|
||||
Uint16 creation_counter_; // Temporizador para controlar el estado "creandose"
|
||||
Uint16 creation_counter_ini_; // Valor inicial para el temporizador para controlar el estado "creandose"
|
||||
Uint16 score_; // Puntos que da el globo al ser destruido
|
||||
Uint16 stopped_counter_; // Contador para controlar el estado "parado"
|
||||
Uint8 kind_; // Tipo de globo
|
||||
Uint8 menace_; // Cantidad de amenaza que genera el globo
|
||||
Uint32 counter_; // Contador interno
|
||||
float travel_y_; // Distancia que ha de recorrer el globo en el eje Y antes de que se le aplique la gravedad
|
||||
float speed_; // Velocidad a la que se mueven los globos
|
||||
Uint8 size_; // Tamaño del globo
|
||||
Uint8 power_; // Cantidad de poder que alberga el globo
|
||||
Bouncing bouncing_; // Contiene las variables para el efecto de rebote
|
||||
float pos_x_; // Posición en el eje X
|
||||
float pos_y_; // Posición en el eje Y
|
||||
Uint8 width_; // Ancho
|
||||
Uint8 height_; // Alto
|
||||
float vel_x_s_{0.0F}; // Velocidad en X (px/s)
|
||||
float vel_y_s_{0.0F}; // Velocidad en Y (px/s)
|
||||
float gravity_s_{0.0F}; // Aceleración Y (px/s²)
|
||||
float default_vel_y_s_{0.0F}; // Velocitat inicial al rebotar (px/s)
|
||||
float max_vel_y_s_{0.0F}; // Velocitat màxima en Y (px/s, no aplicada en time-based actual)
|
||||
bool being_created_; // Indica si el globo se está creando
|
||||
bool blinking_; // Indica si el globo está intermitente
|
||||
bool enabled_; // Indica si el globo esta activo
|
||||
bool invulnerable_; // Indica si el globo es invulnerable
|
||||
bool popping_; // Indica si el globo está explotando
|
||||
bool stopped_; // Indica si el globo está parado
|
||||
bool visible_; // Indica si el globo es visible
|
||||
Circle collider_; // Circulo de colisión del objeto
|
||||
Uint16 creation_counter_; // Temporizador (frames, derivat de creation_counter_s_ per a render alpha)
|
||||
Uint16 creation_counter_ini_; // Valor inicial del temporizador (frames)
|
||||
float creation_counter_s_{0.0F}; // Temporizador (font de veritat, segons)
|
||||
float creation_counter_ini_s_{0.0F}; // Valor inicial del temporizador (segons)
|
||||
float creation_phase_s_{0.0F}; // Acumulador de fase per als steps de creació
|
||||
Uint16 score_; // Puntos que da el globo al ser destruido
|
||||
Uint16 stopped_counter_; // Contador (frames, derivat de stopped_counter_s_)
|
||||
float stopped_counter_s_{0.0F}; // Contador (font de veritat, segons)
|
||||
Uint8 kind_; // Tipo de globo
|
||||
Uint8 menace_; // Cantidad de amenaza que genera el globo
|
||||
float speed_; // Tempo del joc (multiplicador adimensional)
|
||||
Uint8 size_; // Tamaño del globo
|
||||
Uint8 power_; // Cantidad de poder que alberga el globo
|
||||
Bouncing bouncing_; // Contiene las variables para el efecto de rebote
|
||||
float bounce_phase_s_{0.0F}; // Fase del bounce
|
||||
|
||||
void updateColliders(); // Alinea el circulo de colisión con la posición del objeto globo
|
||||
void bounceStart(); // Activa el efecto
|
||||
void bounceStop(); // Detiene el efecto
|
||||
void updateBounce(); // Aplica el efecto
|
||||
void updateAnimation(); // Establece la animación correspondiente
|
||||
void updateBounce(float dt_s); // Aplica el efecto
|
||||
void updateAnimation(float dt_s); // Establece la animación correspondiente
|
||||
void setBeingCreated(bool value); // Establece el valor de la variable
|
||||
|
||||
void updateState(); // Actualiza los estados del globo
|
||||
void updateState(float dt_s); // Actualiza los estados del globo
|
||||
|
||||
// Helpers de updateState, uno por cada rama de estado
|
||||
void updateStatePopping();
|
||||
void updateStateBeingCreated();
|
||||
void updateStateStopped();
|
||||
void updateStateBeingCreated(float dt_s);
|
||||
void updateStateStopped(float dt_s);
|
||||
};
|
||||
|
||||
@@ -11,13 +11,15 @@ Bullet::Bullet(int x, int y, Bullet::Kind kind, bool powered_up, int owner, Text
|
||||
// Posición inicial del objeto
|
||||
pos_x_ = x;
|
||||
pos_y_ = y;
|
||||
pos_x_f_ = static_cast<float>(x);
|
||||
pos_y_f_ = static_cast<float>(y);
|
||||
|
||||
// Alto y ancho del objeto
|
||||
width_ = 10;
|
||||
height_ = 10;
|
||||
|
||||
// Velocidad inicial en el eje Y
|
||||
vel_y_ = -3;
|
||||
vel_y_s_ = VEL_Y_PX_PER_S;
|
||||
|
||||
// Tipo de bala
|
||||
this->kind_ = kind;
|
||||
@@ -29,7 +31,7 @@ Bullet::Bullet(int x, int y, Bullet::Kind kind, bool powered_up, int owner, Text
|
||||
switch (kind) {
|
||||
case Bullet::Kind::UP:
|
||||
// Establece la velocidad inicial
|
||||
vel_x_ = 0;
|
||||
vel_x_s_ = 0.0F;
|
||||
|
||||
// Rectangulo con los gráficos del objeto
|
||||
if (!powered_up) {
|
||||
@@ -41,7 +43,7 @@ Bullet::Bullet(int x, int y, Bullet::Kind kind, bool powered_up, int owner, Text
|
||||
|
||||
case Bullet::Kind::LEFT:
|
||||
// Establece la velocidad inicial
|
||||
vel_x_ = -2;
|
||||
vel_x_s_ = VEL_X_LEFT_PX_PER_S;
|
||||
|
||||
// Rectangulo con los gráficos del objeto
|
||||
if (!powered_up) {
|
||||
@@ -53,7 +55,7 @@ Bullet::Bullet(int x, int y, Bullet::Kind kind, bool powered_up, int owner, Text
|
||||
|
||||
case Bullet::Kind::RIGHT:
|
||||
// Establece la velocidad inicial
|
||||
vel_x_ = 2;
|
||||
vel_x_s_ = VEL_X_RIGHT_PX_PER_S;
|
||||
|
||||
// Rectangulo con los gráficos del objeto
|
||||
if (!powered_up) {
|
||||
@@ -84,40 +86,29 @@ void Bullet::render() {
|
||||
sprite_->render();
|
||||
}
|
||||
|
||||
// Actualiza la posición y estado del objeto en horizontal
|
||||
auto Bullet::move() -> MoveResult {
|
||||
// Variable con el valor de retorno
|
||||
// Actualiza la posición y estado del objeto
|
||||
auto Bullet::move(float dt_s) -> MoveResult {
|
||||
MoveResult msg = MoveResult::OK;
|
||||
|
||||
// Mueve el objeto a su nueva posición
|
||||
pos_x_ += vel_x_;
|
||||
pos_x_f_ += vel_x_s_ * dt_s;
|
||||
pos_x_ = static_cast<int>(pos_x_f_);
|
||||
|
||||
// Si el objeto se sale del area de juego por los laterales
|
||||
if ((pos_x_ < PLAY_AREA_LEFT - width_) || (pos_x_ > PLAY_AREA_RIGHT)) {
|
||||
// Se deshabilita
|
||||
kind_ = Bullet::Kind::NONE;
|
||||
|
||||
// Mensaje de salida
|
||||
msg = MoveResult::OUT;
|
||||
}
|
||||
|
||||
// Mueve el objeto a su nueva posición en vertical
|
||||
pos_y_ += vel_y_;
|
||||
pos_y_f_ += vel_y_s_ * dt_s;
|
||||
pos_y_ = static_cast<int>(pos_y_f_);
|
||||
|
||||
// Si el objeto se sale del area de juego por la parte superior
|
||||
if (pos_y_ < PLAY_AREA_TOP - height_) {
|
||||
// Se deshabilita
|
||||
kind_ = Bullet::Kind::NONE;
|
||||
|
||||
// Mensaje de salida
|
||||
msg = MoveResult::OUT;
|
||||
}
|
||||
|
||||
// Actualiza la posición del sprite
|
||||
sprite_->setPosX(pos_x_);
|
||||
sprite_->setPosY(pos_y_);
|
||||
|
||||
// Alinea el circulo de colisión con el objeto
|
||||
shiftColliders();
|
||||
|
||||
return msg;
|
||||
@@ -146,16 +137,13 @@ auto Bullet::getPosY() const -> int {
|
||||
// Establece el valor de la variable
|
||||
void Bullet::setPosX(int x) {
|
||||
pos_x_ = x;
|
||||
pos_x_f_ = static_cast<float>(x);
|
||||
}
|
||||
|
||||
// Establece el valor de la variable
|
||||
void Bullet::setPosY(int y) {
|
||||
pos_y_ = y;
|
||||
}
|
||||
|
||||
// Obtiene el valor de la variable
|
||||
auto Bullet::getVelY() const -> int {
|
||||
return vel_y_;
|
||||
pos_y_f_ = static_cast<float>(y);
|
||||
}
|
||||
|
||||
// Obtiene el valor de la variable
|
||||
|
||||
@@ -32,14 +32,13 @@ class Bullet {
|
||||
Bullet(const Bullet &) = delete;
|
||||
auto operator=(const Bullet &) -> Bullet & = delete;
|
||||
|
||||
void render(); // Pinta el objeto en pantalla
|
||||
auto move() -> MoveResult; // Actualiza la posición y estado del objeto
|
||||
void disable(); // Deshabilita el objeto
|
||||
void render(); // Pinta el objeto en pantalla
|
||||
auto move(float dt_s) -> MoveResult; // Actualiza la posición y estado del objeto
|
||||
void disable(); // Deshabilita el objeto
|
||||
|
||||
[[nodiscard]] auto isEnabled() const -> bool; // Comprueba si el objeto está habilitado
|
||||
[[nodiscard]] auto getPosX() const -> int; // Obtiene el valor de la variable
|
||||
[[nodiscard]] auto getPosY() const -> int; // Obtiene el valor de la variable
|
||||
[[nodiscard]] auto getVelY() const -> int; // Obtiene el valor de la variable
|
||||
[[nodiscard]] auto getKind() const -> Kind; // Obtiene el valor de la variable
|
||||
[[nodiscard]] auto getOwner() const -> int; // Obtiene el valor de la variable
|
||||
|
||||
@@ -49,19 +48,26 @@ class Bullet {
|
||||
auto getCollider() -> Circle &; // Obtiene el circulo de colisión
|
||||
|
||||
private:
|
||||
// Velocitats en px/s (derivades de les antigues px/frame * 60).
|
||||
static constexpr float VEL_Y_PX_PER_S = -180.0F; // Era -3 px/frame
|
||||
static constexpr float VEL_X_LEFT_PX_PER_S = -120.0F; // Era -2 px/frame
|
||||
static constexpr float VEL_X_RIGHT_PX_PER_S = 120.0F; // Era +2 px/frame
|
||||
|
||||
// Objetos y punteros
|
||||
Sprite *sprite_; // Sprite con los graficos y métodos de pintado
|
||||
|
||||
// Variables
|
||||
int pos_x_; // Posición en el eje X
|
||||
int pos_y_; // Posición en el eje Y
|
||||
Uint8 width_; // Ancho del objeto
|
||||
Uint8 height_; // Alto del objeto
|
||||
int vel_x_; // Velocidad en el eje X
|
||||
int vel_y_; // Velocidad en el eje Y
|
||||
Kind kind_; // Tipo de objeto
|
||||
int owner_; // Identificador del dueño del objeto
|
||||
Circle collider_; // Circulo de colisión del objeto
|
||||
int pos_x_; // Posición en el eje X (px enters per al sprite/collider)
|
||||
int pos_y_; // Posición en el eje Y
|
||||
float pos_x_f_{0}; // Acumulador subpíxel
|
||||
float pos_y_f_{0}; // Acumulador subpíxel
|
||||
Uint8 width_; // Ancho del objeto
|
||||
Uint8 height_; // Alto del objeto
|
||||
float vel_x_s_{0}; // Velocidad en el eje X (px/s)
|
||||
float vel_y_s_{0}; // Velocidad en el eje Y (px/s)
|
||||
Kind kind_; // Tipo de objeto
|
||||
int owner_; // Identificador del dueño del objeto
|
||||
Circle collider_; // Circulo de colisión del objeto
|
||||
|
||||
void shiftColliders(); // Alinea el circulo de colisión con el objeto
|
||||
};
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "game/entities/item.h"
|
||||
|
||||
#include <cstdlib> // for rand
|
||||
#include <algorithm> // for max
|
||||
#include <cstdlib> // for rand
|
||||
|
||||
#include "core/rendering/animatedsprite.h" // for AnimatedSprite
|
||||
#include "game/defaults.hpp" // for PLAY_AREA_LEFT, PLAY_AREA_RIGHT, PLAY_AR...
|
||||
@@ -13,7 +14,8 @@ Item::Item(Id id, float x, float y, Texture *texture, const std::vector<std::str
|
||||
this->id_ = id;
|
||||
enabled_ = true;
|
||||
time_to_live_ = 600;
|
||||
accel_x_ = 0.0F;
|
||||
time_to_live_s_ = TIME_TO_LIVE_S;
|
||||
accel_x_s_ = 0.0F;
|
||||
floor_collision_ = false;
|
||||
|
||||
if (id == Item::Id::COFFEE_MACHINE) {
|
||||
@@ -21,18 +23,20 @@ Item::Item(Id id, float x, float y, Texture *texture, const std::vector<std::str
|
||||
height_ = 29;
|
||||
pos_x_ = (((int)x + (PLAY_AREA_WIDTH / 2)) % (PLAY_AREA_WIDTH - width_ - 5)) + 2;
|
||||
pos_y_ = PLAY_AREA_TOP - height_;
|
||||
vel_x_ = 0.0F;
|
||||
vel_y_ = -0.1F;
|
||||
accel_y_ = 0.1F;
|
||||
vel_x_s_ = 0.0F;
|
||||
vel_y_s_ = COFFEE_VEL_Y_PX_PER_S;
|
||||
accel_y_s_ = COFFEE_ACCEL_Y_PX_PER_S2;
|
||||
collider_.r = 10;
|
||||
} else {
|
||||
width_ = 16;
|
||||
height_ = 16;
|
||||
pos_x_ = x;
|
||||
pos_y_ = y;
|
||||
vel_x_ = -1.0F + ((rand() % 5) * 0.5F);
|
||||
vel_y_ = -4.0F;
|
||||
accel_y_ = 0.2F;
|
||||
// Distribució original: -1.0, -0.5, 0.0, 0.5, 1.0 px/frame ⇒ -60..60 px/s en passos de 30.
|
||||
const int RAND_STEP = rand() % 5;
|
||||
vel_x_s_ = (-2.0F + static_cast<float>(RAND_STEP)) * ITEM_VEL_X_STEP_PX_PER_S;
|
||||
vel_y_s_ = ITEM_VEL_Y_PX_PER_S;
|
||||
accel_y_s_ = ITEM_ACCEL_Y_PX_PER_S2;
|
||||
collider_.r = width_ / 2;
|
||||
}
|
||||
|
||||
@@ -74,49 +78,41 @@ void Item::render() {
|
||||
}
|
||||
|
||||
// Actualiza la posición y estados del objeto
|
||||
void Item::move() {
|
||||
void Item::move(float dt_s) {
|
||||
floor_collision_ = false;
|
||||
|
||||
// Calcula la nueva posición
|
||||
pos_x_ += vel_x_;
|
||||
pos_y_ += vel_y_;
|
||||
// Posició
|
||||
pos_x_ += vel_x_s_ * dt_s;
|
||||
pos_y_ += vel_y_s_ * dt_s;
|
||||
|
||||
// Aplica las aceleraciones a la velocidad
|
||||
vel_x_ += accel_x_;
|
||||
vel_y_ += accel_y_;
|
||||
// Acceleració
|
||||
vel_x_s_ += accel_x_s_ * dt_s;
|
||||
vel_y_s_ += accel_y_s_ * dt_s;
|
||||
|
||||
// Si queda fuera de pantalla, corregimos su posición y cambiamos su sentido
|
||||
// Si surt per laterals, corregeix i inverteix
|
||||
if ((pos_x_ < PLAY_AREA_LEFT) || (pos_x_ + width_ > PLAY_AREA_RIGHT)) {
|
||||
// Corregir posición
|
||||
pos_x_ -= vel_x_;
|
||||
|
||||
// Invertir sentido
|
||||
vel_x_ = -vel_x_;
|
||||
pos_x_ -= vel_x_s_ * dt_s;
|
||||
vel_x_s_ = -vel_x_s_;
|
||||
}
|
||||
|
||||
// Si se sale por arriba rebota (excepto la maquina de café)
|
||||
// Rebot per dalt (excepte la màquina de cafè)
|
||||
if ((pos_y_ < PLAY_AREA_TOP) && !(id_ == Item::Id::COFFEE_MACHINE)) {
|
||||
// Corrige
|
||||
pos_y_ -= vel_y_;
|
||||
|
||||
// Invierte el sentido
|
||||
vel_y_ = -vel_y_;
|
||||
pos_y_ -= vel_y_s_ * dt_s;
|
||||
vel_y_s_ = -vel_y_s_;
|
||||
}
|
||||
|
||||
// Si el objeto se sale por la parte inferior
|
||||
// Topa amb el terra
|
||||
if (pos_y_ + height_ > PLAY_AREA_BOTTOM) {
|
||||
// Detiene el objeto
|
||||
vel_y_ = 0;
|
||||
vel_x_ = 0;
|
||||
accel_x_ = 0;
|
||||
accel_y_ = 0;
|
||||
vel_y_s_ = 0;
|
||||
vel_x_s_ = 0;
|
||||
accel_x_s_ = 0;
|
||||
accel_y_s_ = 0;
|
||||
pos_y_ = PLAY_AREA_BOTTOM - height_;
|
||||
if (id_ == Item::Id::COFFEE_MACHINE) {
|
||||
floor_collision_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Actualiza la posición del sprite
|
||||
sprite_->setPosX(int(pos_x_));
|
||||
sprite_->setPosY(int(pos_y_));
|
||||
shiftColliders();
|
||||
@@ -128,18 +124,22 @@ void Item::disable() {
|
||||
}
|
||||
|
||||
// Actualiza el objeto a su posicion, animación y controla los contadores
|
||||
void Item::update() {
|
||||
move();
|
||||
sprite_->animate();
|
||||
updateTimeToLive();
|
||||
void Item::update(float dt_s) {
|
||||
move(dt_s);
|
||||
sprite_->animate(dt_s);
|
||||
updateTimeToLive(dt_s);
|
||||
checkTimeToLive();
|
||||
}
|
||||
|
||||
// Actualiza el contador
|
||||
void Item::updateTimeToLive() {
|
||||
if (time_to_live_ > 0) {
|
||||
time_to_live_--;
|
||||
// Actualiza el contador. Manté time_to_live_ (frames) sincronitzat amb el
|
||||
// segons per a que render() segueixi funcionant amb la mateixa condició de
|
||||
// parpelleig.
|
||||
void Item::updateTimeToLive(float dt_s) {
|
||||
if (time_to_live_s_ > 0.0F) {
|
||||
time_to_live_s_ = std::max(0.0F, time_to_live_s_ - dt_s);
|
||||
}
|
||||
constexpr float FRAMES_PER_S = 60.0F;
|
||||
time_to_live_ = static_cast<Uint16>(time_to_live_s_ * FRAMES_PER_S);
|
||||
}
|
||||
|
||||
// Comprueba si el objeto sigue vivo
|
||||
|
||||
+32
-20
@@ -29,12 +29,12 @@ class Item {
|
||||
Item(const Item &) = delete;
|
||||
auto operator=(const Item &) -> Item & = delete;
|
||||
|
||||
void allignTo(int x); // Centra el objeto en la posición X
|
||||
void render(); // Pinta el objeto en la pantalla
|
||||
void disable(); // Pone a cero todos los valores del objeto
|
||||
void update(); // Actualiza al objeto a su posicion, animación y controla los contadores
|
||||
void updateTimeToLive(); // Actualiza el contador
|
||||
void checkTimeToLive(); // Comprueba si el objeto sigue vivo
|
||||
void allignTo(int x); // Centra el objeto en la posición X
|
||||
void render(); // Pinta el objeto en la pantalla
|
||||
void disable(); // Pone a cero todos los valores del objeto
|
||||
void update(float dt_s); // Actualiza al objeto
|
||||
void updateTimeToLive(float dt_s); // Actualiza el contador
|
||||
void checkTimeToLive(); // Comprueba si el objeto sigue vivo
|
||||
|
||||
[[nodiscard]] auto getPosX() const -> float; // Obtiene del valor de la variable
|
||||
[[nodiscard]] auto getPosY() const -> float; // Obtiene del valor de la variable
|
||||
@@ -47,24 +47,36 @@ class Item {
|
||||
auto getCollider() -> Circle &; // Obtiene el circulo de colisión
|
||||
|
||||
private:
|
||||
// Time-based: equivalents en unitats físiques precalculades a 60Hz.
|
||||
static constexpr float ITEM_VEL_Y_PX_PER_S = -240.0F; // Era -4.0 px/frame
|
||||
static constexpr float ITEM_ACCEL_Y_PX_PER_S2 = 720.0F; // Era +0.2 px/frame²
|
||||
static constexpr float ITEM_VEL_X_STEP_PX_PER_S = 30.0F; // Era 0.5 px/frame, 5 valors en [-1.0, 1.0]
|
||||
static constexpr float COFFEE_VEL_Y_PX_PER_S = -6.0F; // Era -0.1 px/frame
|
||||
static constexpr float COFFEE_ACCEL_Y_PX_PER_S2 = 360.0F; // Era +0.1 px/frame²
|
||||
static constexpr float TIME_TO_LIVE_S = 10.0F; // Era 600 frames
|
||||
static constexpr float BLINK_START_S = TIME_TO_LIVE_S - (200.0F / 60.0F); // Era time_to_live_ > 200
|
||||
static constexpr float BLINK_PERIOD_S = 20.0F / 60.0F; // Era % 20
|
||||
static constexpr float BLINK_OFF_S = 10.0F / 60.0F; // Era % 20 <= 10
|
||||
|
||||
// Objetos y punteros
|
||||
AnimatedSprite *sprite_; // Sprite con los graficos del objeto
|
||||
|
||||
// Variables
|
||||
float pos_x_; // Posición X del objeto
|
||||
float pos_y_; // Posición Y del objeto
|
||||
Uint8 width_; // Ancho del objeto
|
||||
Uint8 height_; // Alto del objeto
|
||||
float vel_x_; // Velocidad en el eje X
|
||||
float vel_y_; // Velocidad en el eje Y
|
||||
float accel_x_; // Aceleración en el eje X
|
||||
float accel_y_; // Aceleración en el eje Y
|
||||
bool floor_collision_; // Indica si el objeto colisiona con el suelo
|
||||
Id id_; // Especifica el tipo de objeto que es
|
||||
bool enabled_; // Especifica si el objeto está habilitado
|
||||
Uint16 time_to_live_; // Temporizador con el tiempo que el objeto está presente
|
||||
Circle collider_; // Circulo de colisión del objeto
|
||||
float pos_x_; // Posición X del objeto
|
||||
float pos_y_; // Posición Y del objeto
|
||||
Uint8 width_; // Ancho del objeto
|
||||
Uint8 height_; // Alto del objeto
|
||||
float vel_x_s_{0.0F}; // Velocidad en el eje X (px/s)
|
||||
float vel_y_s_{0.0F}; // Velocidad en el eje Y (px/s)
|
||||
float accel_x_s_{0.0F}; // Aceleración en el eje X (px/s²)
|
||||
float accel_y_s_{0.0F}; // Aceleración en el eje Y (px/s²)
|
||||
bool floor_collision_; // Indica si el objeto colisiona con el suelo
|
||||
Id id_; // Especifica el tipo de objeto que es
|
||||
bool enabled_; // Especifica si el objeto está habilitado
|
||||
Uint16 time_to_live_; // Temporizador (frames, derivat de time_to_live_s_ per a render())
|
||||
float time_to_live_s_{0.0F}; // Temporizador (font de veritat, segons)
|
||||
Circle collider_; // Circulo de colisión del objeto
|
||||
|
||||
void shiftColliders(); // Alinea el circulo de colisión con la posición del objeto
|
||||
void move(); // Actualiza la posición y estados del objeto
|
||||
void move(float dt_s); // Actualiza la posición y estados del objeto
|
||||
};
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "game/entities/player.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath> // for fmod
|
||||
#include <cstdlib> // for rand
|
||||
|
||||
#include "core/input/input.h" // for InputAction
|
||||
@@ -42,12 +43,15 @@ void Player::init() {
|
||||
// Inicializa variables de estado
|
||||
alive_ = true;
|
||||
death_counter_ = DEATH_COUNTER;
|
||||
death_counter_s_ = DEATH_DURATION_S;
|
||||
status_walking_ = STATUS_WALKING_STOP;
|
||||
status_firing_ = STATUS_FIRING_NO;
|
||||
invulnerable_ = false;
|
||||
invulnerable_counter_ = INVULNERABLE_COUNTER;
|
||||
invulnerable_counter_s_ = INVULNERABLE_DURATION_S;
|
||||
power_up_ = false;
|
||||
power_up_counter_ = POWERUP_COUNTER;
|
||||
power_up_counter_s_ = POWERUP_DURATION_S;
|
||||
extra_hit_ = false;
|
||||
coffees_ = 0;
|
||||
input_ = true;
|
||||
@@ -63,11 +67,10 @@ void Player::init() {
|
||||
shiftColliders();
|
||||
|
||||
// Establece la velocidad inicial
|
||||
vel_x_ = 0;
|
||||
vel_y_ = 0;
|
||||
vel_x_s_ = 0.0F;
|
||||
|
||||
// Establece la velocidad base
|
||||
base_speed_ = 1.5;
|
||||
base_speed_s_ = BASE_SPEED_PX_PER_S;
|
||||
|
||||
// Establece la puntuación inicial
|
||||
score_ = 0;
|
||||
@@ -77,6 +80,7 @@ void Player::init() {
|
||||
|
||||
// Inicia el contador para la cadencia de disparo
|
||||
cooldown_ = 10;
|
||||
cooldown_s_ = COOLDOWN_S;
|
||||
|
||||
// Establece la posición del sprite
|
||||
legs_sprite_->setPosX(pos_x_);
|
||||
@@ -98,12 +102,12 @@ void Player::init() {
|
||||
void Player::setInput(Input::Action input) {
|
||||
switch (input) {
|
||||
case Input::Action::LEFT:
|
||||
vel_x_ = -base_speed_;
|
||||
vel_x_s_ = -base_speed_s_;
|
||||
setWalkingStatus(STATUS_WALKING_LEFT);
|
||||
break;
|
||||
|
||||
case Input::Action::RIGHT:
|
||||
vel_x_ = base_speed_;
|
||||
vel_x_s_ = base_speed_s_;
|
||||
setWalkingStatus(STATUS_WALKING_RIGHT);
|
||||
break;
|
||||
|
||||
@@ -120,24 +124,21 @@ void Player::setInput(Input::Action input) {
|
||||
break;
|
||||
|
||||
default:
|
||||
vel_x_ = 0;
|
||||
vel_x_s_ = 0.0F;
|
||||
setWalkingStatus(STATUS_WALKING_STOP);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Mueve el jugador a la posición y animación que le corresponde
|
||||
void Player::move() {
|
||||
void Player::move(float dt_s) {
|
||||
if (isAlive()) {
|
||||
// Mueve el jugador a derecha o izquierda
|
||||
pos_x_ += vel_x_;
|
||||
pos_x_ += vel_x_s_ * dt_s;
|
||||
|
||||
// Si el jugador abandona el area de juego por los laterales
|
||||
if ((pos_x_ < PLAY_AREA_LEFT - 5) || (pos_x_ + width_ > PLAY_AREA_RIGHT + 5)) { // Restaura su posición
|
||||
pos_x_ -= vel_x_;
|
||||
if ((pos_x_ < PLAY_AREA_LEFT - 5) || (pos_x_ + width_ > PLAY_AREA_RIGHT + 5)) {
|
||||
pos_x_ -= vel_x_s_ * dt_s;
|
||||
}
|
||||
|
||||
// Actualiza la posición del sprite
|
||||
legs_sprite_->setPosX(getPosX());
|
||||
legs_sprite_->setPosY(pos_y_);
|
||||
|
||||
@@ -150,14 +151,11 @@ void Player::move() {
|
||||
fire_sprite_->setPosX(getPosX() - 2);
|
||||
fire_sprite_->setPosY(pos_y_ - 8);
|
||||
} else {
|
||||
death_sprite_->update();
|
||||
death_sprite_->update(dt_s);
|
||||
|
||||
// Si el cadaver abandona el area de juego por los laterales
|
||||
if ((death_sprite_->getPosX() < PLAY_AREA_LEFT) || (death_sprite_->getPosX() + width_ > PLAY_AREA_RIGHT)) { // Restaura su posición
|
||||
if ((death_sprite_->getPosX() < PLAY_AREA_LEFT) || (death_sprite_->getPosX() + width_ > PLAY_AREA_RIGHT)) {
|
||||
const float VX = death_sprite_->getVelX();
|
||||
death_sprite_->setPosX(death_sprite_->getPosX() - VX);
|
||||
|
||||
// Rebota
|
||||
death_sprite_->setPosX(death_sprite_->getPosX() - (VX * dt_s));
|
||||
death_sprite_->setVelX(-VX);
|
||||
}
|
||||
}
|
||||
@@ -199,8 +197,7 @@ void Player::setFiringStatus(Uint8 status) {
|
||||
}
|
||||
|
||||
// Establece la animación correspondiente al estado
|
||||
void Player::setAnimation() {
|
||||
// Crea cadenas de texto para componer el nombre de la animación
|
||||
void Player::setAnimation(float dt_s) {
|
||||
std::string body_coffees;
|
||||
std::string head_coffees;
|
||||
if (coffees_ > 0) {
|
||||
@@ -215,27 +212,25 @@ void Player::setAnimation() {
|
||||
const SDL_FlipMode FLIP_WALK = status_walking_ == STATUS_WALKING_RIGHT ? SDL_FLIP_HORIZONTAL : SDL_FLIP_NONE;
|
||||
const SDL_FlipMode FLIP_FIRE = status_firing_ == STATUS_FIRING_RIGHT ? SDL_FLIP_HORIZONTAL : SDL_FLIP_NONE;
|
||||
|
||||
// Establece la animación a partir de las cadenas
|
||||
legs_sprite_->setCurrentAnimation(WALKING);
|
||||
legs_sprite_->setFlip(FLIP_WALK);
|
||||
if (status_firing_ == STATUS_FIRING_NO) { // No esta disparando
|
||||
if (status_firing_ == STATUS_FIRING_NO) {
|
||||
body_sprite_->setCurrentAnimation(WALKING + body_coffees + POWER_UP);
|
||||
body_sprite_->setFlip(FLIP_WALK);
|
||||
head_sprite_->setCurrentAnimation(WALKING + head_coffees + POWER_UP);
|
||||
head_sprite_->setFlip(FLIP_WALK);
|
||||
} else { // Está disparando
|
||||
} else {
|
||||
body_sprite_->setCurrentAnimation(FIRING + body_coffees + POWER_UP);
|
||||
body_sprite_->setFlip(FLIP_FIRE);
|
||||
head_sprite_->setCurrentAnimation(FIRING + head_coffees + POWER_UP);
|
||||
head_sprite_->setFlip(FLIP_FIRE);
|
||||
}
|
||||
|
||||
// Actualiza las animaciones de los sprites
|
||||
legs_sprite_->animate();
|
||||
body_sprite_->animate();
|
||||
head_sprite_->animate();
|
||||
legs_sprite_->animate(dt_s);
|
||||
body_sprite_->animate(dt_s);
|
||||
head_sprite_->animate(dt_s);
|
||||
|
||||
fire_sprite_->animate();
|
||||
fire_sprite_->animate(dt_s);
|
||||
fire_sprite_->setFlip(FLIP_WALK);
|
||||
}
|
||||
|
||||
@@ -268,30 +263,37 @@ auto Player::canFire() const -> bool {
|
||||
// Establece el valor de la variable
|
||||
void Player::setFireCooldown(int time) {
|
||||
cooldown_ = time;
|
||||
cooldown_s_ = static_cast<float>(time) / 60.0F;
|
||||
}
|
||||
|
||||
// Actualiza el valor de la variable
|
||||
void Player::updateCooldown() {
|
||||
if (cooldown_ > 0) {
|
||||
cooldown_--;
|
||||
if (power_up_) {
|
||||
cooldown_--;
|
||||
}
|
||||
// Establece el valor del cooldown en segons (time-based)
|
||||
void Player::setFireCooldownS(float seconds) {
|
||||
cooldown_s_ = seconds;
|
||||
cooldown_ = static_cast<int>(seconds * 60.0F);
|
||||
}
|
||||
|
||||
// Actualiza el cooldown. Quan està en mode PowerUp, el cooldown
|
||||
// es consumeix el doble de ràpid (equivalent a decrementar 2 frames per tick).
|
||||
void Player::updateCooldown(float dt_s) {
|
||||
if (cooldown_s_ > 0.0F) {
|
||||
const float RATE = power_up_ ? 2.0F : 1.0F;
|
||||
cooldown_s_ = std::max(0.0F, cooldown_s_ - (dt_s * RATE));
|
||||
cooldown_ = static_cast<int>(cooldown_s_ * 60.0F);
|
||||
} else {
|
||||
setFiringStatus(STATUS_FIRING_NO);
|
||||
}
|
||||
}
|
||||
|
||||
// Actualiza al jugador a su posicion, animación y controla los contadores
|
||||
void Player::update() {
|
||||
move();
|
||||
setAnimation();
|
||||
void Player::update(float dt_s) {
|
||||
move(dt_s);
|
||||
setAnimation(dt_s);
|
||||
shiftColliders();
|
||||
updateCooldown();
|
||||
updatePowerUpCounter();
|
||||
updateInvulnerableCounter();
|
||||
updateDeathCounter();
|
||||
updatePowerUpHeadOffset();
|
||||
updateCooldown(dt_s);
|
||||
updatePowerUpCounter(dt_s);
|
||||
updateInvulnerableCounter(dt_s);
|
||||
updateDeathCounter(dt_s);
|
||||
updatePowerUpHeadOffset(dt_s);
|
||||
}
|
||||
|
||||
// Obtiene la puntuación del jugador
|
||||
@@ -321,11 +323,13 @@ void Player::setAlive(bool value) {
|
||||
if (!value) {
|
||||
death_sprite_->setPosX(head_sprite_->getRect().x);
|
||||
death_sprite_->setPosY(head_sprite_->getRect().y);
|
||||
death_sprite_->setAccelY(0.2F);
|
||||
death_sprite_->setVelY(-6.6F);
|
||||
death_sprite_->setVelX(3.3F);
|
||||
// Física del cadàver en px/s i px/s² — Game crida Player::update(dt_s)
|
||||
// que delega a death_sprite_->update(dt_s) (time-based).
|
||||
death_sprite_->setAccelY(DEATH_ACCEL_Y_PX_PER_S2);
|
||||
death_sprite_->setVelY(DEATH_VEL_Y_PX_PER_S);
|
||||
death_sprite_->setVelX(DEATH_VEL_X_PX_PER_S);
|
||||
if (rand() % 2 == 0) {
|
||||
death_sprite_->setVelX(-3.3F);
|
||||
death_sprite_->setVelX(-DEATH_VEL_X_PX_PER_S);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -376,25 +380,30 @@ auto Player::getInvulnerableCounter() const -> Uint16 {
|
||||
// Establece el valor de la variable
|
||||
void Player::setInvulnerableCounter(Uint16 value) {
|
||||
invulnerable_counter_ = value;
|
||||
invulnerable_counter_s_ = static_cast<float>(value) / 60.0F;
|
||||
}
|
||||
|
||||
// Actualiza el valor de la variable
|
||||
void Player::updateInvulnerableCounter() {
|
||||
// Actualiza el contador d'invulnerabilitat. Manté el counter enter
|
||||
// sincronitzat perquè render() segueixi parpellejant igual.
|
||||
void Player::updateInvulnerableCounter(float dt_s) {
|
||||
if (invulnerable_) {
|
||||
if (invulnerable_counter_ > 0) {
|
||||
invulnerable_counter_--;
|
||||
if (invulnerable_counter_s_ > 0.0F) {
|
||||
invulnerable_counter_s_ = std::max(0.0F, invulnerable_counter_s_ - dt_s);
|
||||
invulnerable_counter_ = static_cast<Uint16>(invulnerable_counter_s_ * 60.0F);
|
||||
} else {
|
||||
invulnerable_ = false;
|
||||
invulnerable_counter_ = INVULNERABLE_COUNTER;
|
||||
invulnerable_counter_s_ = INVULNERABLE_DURATION_S;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Actualiza el valor de la variable
|
||||
void Player::updateDeathCounter() {
|
||||
// Actualiza el comptador de mort
|
||||
void Player::updateDeathCounter(float dt_s) {
|
||||
if (!alive_) {
|
||||
if (death_counter_ > 0) {
|
||||
death_counter_--;
|
||||
if (death_counter_s_ > 0.0F) {
|
||||
death_counter_s_ = std::max(0.0F, death_counter_s_ - dt_s);
|
||||
death_counter_ = static_cast<Uint16>(death_counter_s_ * 60.0F);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -417,15 +426,18 @@ auto Player::getPowerUpCounter() const -> Uint16 {
|
||||
// Establece el valor de la variable
|
||||
void Player::setPowerUpCounter(Uint16 value) {
|
||||
power_up_counter_ = value;
|
||||
power_up_counter_s_ = static_cast<float>(value) / 60.0F;
|
||||
}
|
||||
|
||||
// Actualiza el valor de la variable
|
||||
void Player::updatePowerUpCounter() {
|
||||
if ((power_up_counter_ > 0) && (power_up_)) {
|
||||
power_up_counter_--;
|
||||
// Actualiza el comptador de PowerUp
|
||||
void Player::updatePowerUpCounter(float dt_s) {
|
||||
if ((power_up_counter_s_ > 0.0F) && (power_up_)) {
|
||||
power_up_counter_s_ = std::max(0.0F, power_up_counter_s_ - dt_s);
|
||||
power_up_counter_ = static_cast<Uint16>(power_up_counter_s_ * 60.0F);
|
||||
} else {
|
||||
power_up_ = false;
|
||||
power_up_counter_ = POWERUP_COUNTER;
|
||||
power_up_counter_s_ = POWERUP_DURATION_S;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -451,6 +463,7 @@ void Player::removeExtraHit() {
|
||||
}
|
||||
invulnerable_ = true;
|
||||
invulnerable_counter_ = INVULNERABLE_COUNTER;
|
||||
invulnerable_counter_s_ = INVULNERABLE_DURATION_S;
|
||||
}
|
||||
|
||||
// Habilita la entrada de ordenes
|
||||
@@ -490,21 +503,13 @@ auto Player::getDeathCounter() const -> Uint16 {
|
||||
return death_counter_;
|
||||
}
|
||||
|
||||
// Actualiza el valor de la variable
|
||||
void Player::updatePowerUpHeadOffset() {
|
||||
if (!power_up_) {
|
||||
// powerUpHeadOffset = 0;
|
||||
} else {
|
||||
// powerUpHeadOffset = 96;
|
||||
if (power_up_counter_ < 300) {
|
||||
if (power_up_counter_ % 10 > 4) {
|
||||
// powerUpHeadOffset = 96;
|
||||
fire_sprite_->setEnabled(false);
|
||||
} else {
|
||||
// powerUpHeadOffset = 0;
|
||||
fire_sprite_->setEnabled(true);
|
||||
}
|
||||
}
|
||||
// Actualiza l'offset. dt_s no s'usa directament: el blink final depèn de
|
||||
// power_up_counter_s_ que ja s'està actualitzant a updatePowerUpCounter.
|
||||
void Player::updatePowerUpHeadOffset([[maybe_unused]] float dt_s) {
|
||||
if (!power_up_) { return; }
|
||||
if (power_up_counter_s_ < POWERUP_BLINK_THRESHOLD_S) {
|
||||
const float PHASE = std::fmod(power_up_counter_s_, BLINK_PERIOD_S);
|
||||
fire_sprite_->setEnabled(PHASE <= BLINK_OFF_S);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
#include <string> // for string
|
||||
#include <vector> // for vector
|
||||
|
||||
#include "utils/utils.h" // for Circle
|
||||
#include "core/input/input.h" // for Input::Action
|
||||
#include "utils/utils.h" // for Circle
|
||||
class AnimatedSprite;
|
||||
class Texture;
|
||||
|
||||
@@ -21,14 +21,14 @@ class Player {
|
||||
Player(const Player &) = delete;
|
||||
auto operator=(const Player &) -> Player & = delete;
|
||||
|
||||
void init(); // Iniciador
|
||||
void update(); // Actualiza al jugador a su posicion, animación y controla los contadores
|
||||
void render(); // Pinta el jugador en pantalla
|
||||
void move(); // Mueve el jugador a la posición y animación que le corresponde
|
||||
void init(); // Iniciador
|
||||
void update(float dt_s); // Actualiza al jugador
|
||||
void render(); // Pinta el jugador en pantalla
|
||||
void move(float dt_s); // Mueve el jugador
|
||||
|
||||
void setPlayerTextures(const std::vector<Texture *> &texture); // Pone las texturas del jugador
|
||||
void setInput(Input::Action input); // Actua en consecuencia de la entrada recibida
|
||||
void setAnimation(); // Establece la animación correspondiente al estado
|
||||
void setAnimation(float dt_s); // Establece la animación correspondiente al estado
|
||||
|
||||
[[nodiscard]] auto getPosX() const -> int; // Obtiene el valor de la variable
|
||||
[[nodiscard]] auto getPosY() const -> int; // Obtiene el valor de la variable
|
||||
@@ -36,8 +36,9 @@ class Player {
|
||||
[[nodiscard]] auto getHeight() const -> int; // Obtiene el valor de la variable
|
||||
|
||||
[[nodiscard]] auto canFire() const -> bool; // Indica si el jugador puede disparar
|
||||
void setFireCooldown(int time); // Establece el valor de la variable
|
||||
void updateCooldown(); // Actualiza el valor de la variable
|
||||
void setFireCooldown(int time); // Establece el valor de la variable (frames)
|
||||
void setFireCooldownS(float seconds); // Establece el valor de la variable (segons)
|
||||
void updateCooldown(float dt_s); // Actualiza el valor de la variable
|
||||
|
||||
[[nodiscard]] auto getScore() const -> Uint32; // Obtiene la puntuación del jugador
|
||||
void setScore(Uint32 score); // Asigna un valor a la puntuación del jugador
|
||||
@@ -60,7 +61,7 @@ class Player {
|
||||
void setPowerUp(bool value); // Establece el valor de la variable
|
||||
[[nodiscard]] auto getPowerUpCounter() const -> Uint16; // Obtiene el valor de la variable
|
||||
void setPowerUpCounter(Uint16 value); // Establece el valor de la variable
|
||||
void updatePowerUpCounter(); // Actualiza el valor de la variable
|
||||
void updatePowerUpCounter(float dt_s); // Actualiza el valor de la variable
|
||||
|
||||
[[nodiscard]] auto hasExtraHit() const -> bool; // Obtiene el valor de la variable
|
||||
void giveExtraHit(); // Concede un toque extra al jugador
|
||||
@@ -89,6 +90,20 @@ class Player {
|
||||
static constexpr int INVULNERABLE_COUNTER = 200;
|
||||
static constexpr int POWERUP_COUNTER = 1500;
|
||||
|
||||
// Time-based: equivalents en segons/px·s a 60Hz (font de veritat).
|
||||
static constexpr float BASE_SPEED_PX_PER_S = 90.0F; // Era 1.5 px/frame
|
||||
static constexpr float COOLDOWN_S = 10.0F / 60.0F; // Era 10 frames
|
||||
static constexpr float INVULNERABLE_DURATION_S = 200.0F / 60.0F;
|
||||
static constexpr float POWERUP_DURATION_S = 1500.0F / 60.0F;
|
||||
static constexpr float DEATH_DURATION_S = 350.0F / 60.0F;
|
||||
static constexpr float POWERUP_BLINK_THRESHOLD_S = 300.0F / 60.0F; // Era power_up_counter_ < 300
|
||||
static constexpr float BLINK_PERIOD_S = 10.0F / 60.0F; // Era % 10
|
||||
static constexpr float BLINK_OFF_S = 4.0F / 60.0F; // Era % 10 > 4
|
||||
// Death sprite (cadàver) — physics convertides de px/frame a px/s.
|
||||
static constexpr float DEATH_ACCEL_Y_PX_PER_S2 = 0.2F * 60.0F * 60.0F; // = 720
|
||||
static constexpr float DEATH_VEL_Y_PX_PER_S = -6.6F * 60.0F; // = -396
|
||||
static constexpr float DEATH_VEL_X_PX_PER_S = 3.3F * 60.0F; // = 198
|
||||
|
||||
// Objetos y punteros
|
||||
SDL_Renderer *renderer_; // El renderizador de la ventana
|
||||
AnimatedSprite *head_sprite_; // Sprite para dibujar la cabeza
|
||||
@@ -104,11 +119,11 @@ class Player {
|
||||
Uint8 width_; // Anchura
|
||||
Uint8 height_; // Altura
|
||||
|
||||
float vel_x_; // Cantidad de pixeles a desplazarse en el eje X
|
||||
int vel_y_; // Cantidad de pixeles a desplazarse en el eje Y
|
||||
float vel_x_s_{0.0F}; // Velocidad en el eje X (px/s)
|
||||
|
||||
float base_speed_; // Velocidad base del jugador
|
||||
int cooldown_; // Contador durante el cual no puede disparar
|
||||
float base_speed_s_{0.0F}; // Velocidad base del jugador (px/s)
|
||||
int cooldown_; // Contador durante el cual no puede disparar (frames, derivat de cooldown_s_)
|
||||
float cooldown_s_{0.0F}; // Contador durante el cual no puede disparar (font de veritat, segons)
|
||||
|
||||
Uint32 score_; // Puntos del jugador
|
||||
float score_multiplier_; // Multiplicador de puntos
|
||||
@@ -116,22 +131,25 @@ class Player {
|
||||
Uint8 status_walking_; // Estado del jugador
|
||||
Uint8 status_firing_; // Estado del jugador
|
||||
|
||||
bool alive_; // Indica si el jugador está vivo
|
||||
Uint16 death_counter_; // Contador para la animación de morirse
|
||||
bool invulnerable_; // Indica si el jugador es invulnerable
|
||||
Uint16 invulnerable_counter_; // Contador para la invulnerabilidad
|
||||
bool extra_hit_; // Indica si el jugador tiene un toque extra
|
||||
Uint8 coffees_; // Indica cuantos cafes lleva acumulados
|
||||
bool power_up_; // Indica si el jugador tiene activo el modo PowerUp
|
||||
Uint16 power_up_counter_; // Temporizador para el modo PowerUp
|
||||
bool input_; // Indica si puede recibir ordenes de entrada
|
||||
Circle collider_; // Circulo de colisión del jugador
|
||||
bool alive_; // Indica si el jugador está vivo
|
||||
Uint16 death_counter_; // Contador (frame-based)
|
||||
float death_counter_s_{0.0F}; // Contador (time-based)
|
||||
bool invulnerable_; // Indica si el jugador es invulnerable
|
||||
Uint16 invulnerable_counter_; // Contador (frame-based)
|
||||
float invulnerable_counter_s_{0.0F}; // Contador (time-based)
|
||||
bool extra_hit_; // Indica si el jugador tiene un toque extra
|
||||
Uint8 coffees_; // Indica cuantos cafes lleva acumulados
|
||||
bool power_up_; // Indica si el jugador tiene activo el modo PowerUp
|
||||
Uint16 power_up_counter_; // Temporizador (frame-based)
|
||||
float power_up_counter_s_{0.0F}; // Temporizador (time-based)
|
||||
bool input_; // Indica si puede recibir ordenes de entrada
|
||||
Circle collider_; // Circulo de colisión del jugador
|
||||
|
||||
void setWalkingStatus(Uint8 status); // Establece el estado del jugador
|
||||
void setFiringStatus(Uint8 status); // Establece el estado del jugador
|
||||
|
||||
void shiftColliders(); // Actualiza el circulo de colisión a la posición del jugador
|
||||
void updateInvulnerableCounter(); // Actualiza el valor de la variable
|
||||
void updateDeathCounter(); // Actualiza el valor de la variable
|
||||
void updatePowerUpHeadOffset(); // Actualiza el valor de la variable
|
||||
void shiftColliders(); // Actualiza el circulo de colisión a la posición del jugador
|
||||
void updateInvulnerableCounter(float dt_s); // Actualiza el valor de la variable
|
||||
void updateDeathCounter(float dt_s); // Actualiza el valor de la variable
|
||||
void updatePowerUpHeadOffset(float dt_s); // Actualiza el valor de la variable
|
||||
};
|
||||
|
||||
+285
-313
@@ -20,13 +20,14 @@
|
||||
#include "core/rendering/texture.h" // for Texture
|
||||
#include "core/resources/asset.h" // for Asset
|
||||
#include "core/resources/resource.h"
|
||||
#include "game/defaults.hpp" // for PLAY_AREA_CENTER_X, BLOCK, PLAY_AREA_CEN...
|
||||
#include "game/entities/balloon.h" // for Balloon, Balloon::VELX_NEGATIVE, BALLOON_...
|
||||
#include "game/entities/bullet.h" // for Bullet, Bullet::Kind::LEFT, Bullet::Kind::RIGHT, BULLE...
|
||||
#include "game/entities/item.h" // for Item
|
||||
#include "game/entities/player.h" // for Player
|
||||
#include "game/options.hpp" // for Options
|
||||
#include "game/ui/menu.h" // for Menu
|
||||
#include "core/system/delta_time.hpp" // for DeltaTime
|
||||
#include "game/defaults.hpp" // for PLAY_AREA_CENTER_X, BLOCK, PLAY_AREA_CEN...
|
||||
#include "game/entities/balloon.h" // for Balloon, Balloon::VELX_NEGATIVE, BALLOON_...
|
||||
#include "game/entities/bullet.h" // for Bullet, Bullet::Kind::LEFT, Bullet::Kind::RIGHT, BULLE...
|
||||
#include "game/entities/item.h" // for Item
|
||||
#include "game/entities/player.h" // for Player
|
||||
#include "game/options.hpp" // for Options
|
||||
#include "game/ui/menu.h" // for Menu
|
||||
namespace Ja {
|
||||
struct Sound;
|
||||
} // namespace Ja
|
||||
@@ -102,6 +103,9 @@ Game::Game(int num_players, int current_stage, SDL_Renderer *renderer, bool demo
|
||||
|
||||
// Inicializa las variables necesarias para la sección 'Game'
|
||||
init();
|
||||
|
||||
// Reset del rellotge perquè el primer dt_s no inclogui el temps de càrrega.
|
||||
DeltaTime::reset();
|
||||
}
|
||||
|
||||
Game::~Game() {
|
||||
@@ -146,9 +150,6 @@ Game::~Game() {
|
||||
|
||||
// Inicializa las variables necesarias para la sección 'Game'
|
||||
void Game::init() {
|
||||
ticks_ = 0;
|
||||
ticks_speed_ = 15;
|
||||
|
||||
// Elimina qualquier jugador que hubiese antes de crear los nuevos
|
||||
for (auto *player : players_) {
|
||||
delete player;
|
||||
@@ -200,18 +201,27 @@ void Game::init() {
|
||||
|
||||
game_completed_ = false;
|
||||
game_completed_counter_ = 0;
|
||||
game_completed_counter_s_ = 0.0F;
|
||||
section_->name = SECTION_PROG_GAME;
|
||||
section_->subsection = SUBSECTION_GAME_PLAY_1P;
|
||||
menace_current_ = 0;
|
||||
menace_threshold_ = 0;
|
||||
hi_score_achieved_ = false;
|
||||
stage_bitmap_counter_ = STAGE_COUNTER;
|
||||
stage_bitmap_counter_s_ = STAGE_COUNTER / 60.0F;
|
||||
death_counter_ = Player::DEATH_COUNTER;
|
||||
death_counter_s_ = Player::DEATH_COUNTER / 60.0F;
|
||||
time_stopped_ = false;
|
||||
time_stopped_counter_ = 0;
|
||||
time_stopped_counter_s_ = 0.0F;
|
||||
counter_ = 0;
|
||||
elapsed_s_ = 0.0F;
|
||||
last_enemy_deploy_ = 0;
|
||||
enemy_deploy_counter_ = 0;
|
||||
enemy_deploy_counter_s_ = 0.0F;
|
||||
enemy_deploy_phase_s_ = 0.0F;
|
||||
shake_phase_s_ = 0.0F;
|
||||
helper_counter_s_ = 0.0F;
|
||||
enemy_speed_ = default_enemy_speed_;
|
||||
effect_.flash = false;
|
||||
effect_.shake = false;
|
||||
@@ -284,15 +294,15 @@ void Game::init() {
|
||||
// Con los globos creados, calcula el nivel de amenaza
|
||||
evaluateAndSetMenace();
|
||||
|
||||
// Inicializa el bitmap de 1000 puntos
|
||||
// Inicializa el bitmap de 1000 puntos (px/s i px/s²; -0.5 px/frame → -30 px/s)
|
||||
n1000_sprite_->setPosX(0);
|
||||
n1000_sprite_->setPosY(0);
|
||||
n1000_sprite_->setWidth(26);
|
||||
n1000_sprite_->setHeight(9);
|
||||
n1000_sprite_->setVelX(0.0F);
|
||||
n1000_sprite_->setVelY(-0.5F);
|
||||
n1000_sprite_->setVelY(-30.0F);
|
||||
n1000_sprite_->setAccelX(0.0F);
|
||||
n1000_sprite_->setAccelY(-0.1F);
|
||||
n1000_sprite_->setAccelY(-360.0F);
|
||||
n1000_sprite_->setSpriteClip(0, 0, 26, 9);
|
||||
n1000_sprite_->setEnabled(false);
|
||||
n1000_sprite_->setEnabledCounter(0);
|
||||
@@ -305,9 +315,9 @@ void Game::init() {
|
||||
n2500_sprite_->setWidth(28);
|
||||
n2500_sprite_->setHeight(9);
|
||||
n2500_sprite_->setVelX(0.0F);
|
||||
n2500_sprite_->setVelY(-0.5F);
|
||||
n2500_sprite_->setVelY(-30.0F);
|
||||
n2500_sprite_->setAccelX(0.0F);
|
||||
n2500_sprite_->setAccelY(-0.1F);
|
||||
n2500_sprite_->setAccelY(-360.0F);
|
||||
n2500_sprite_->setSpriteClip(26, 0, 28, 9);
|
||||
n2500_sprite_->setEnabled(false);
|
||||
n2500_sprite_->setEnabledCounter(0);
|
||||
@@ -320,9 +330,9 @@ void Game::init() {
|
||||
n5000_sprite_->setWidth(28);
|
||||
n5000_sprite_->setHeight(9);
|
||||
n5000_sprite_->setVelX(0.0F);
|
||||
n5000_sprite_->setVelY(-0.5F);
|
||||
n5000_sprite_->setVelY(-30.0F);
|
||||
n5000_sprite_->setAccelX(0.0F);
|
||||
n5000_sprite_->setAccelY(-0.1F);
|
||||
n5000_sprite_->setAccelY(-360.0F);
|
||||
n5000_sprite_->setSpriteClip(54, 0, 28, 9);
|
||||
n5000_sprite_->setEnabled(false);
|
||||
n5000_sprite_->setEnabledCounter(0);
|
||||
@@ -1429,11 +1439,10 @@ void Game::renderScoreBoard() {
|
||||
}
|
||||
|
||||
// Actualiza las variables del jugador
|
||||
void Game::updatePlayers() {
|
||||
void Game::updatePlayers(float dt_s) {
|
||||
for (auto *player : players_) {
|
||||
player->update();
|
||||
player->update(dt_s);
|
||||
|
||||
// Comprueba la colisión entre el jugador y los globos
|
||||
if (checkPlayerBalloonCollision(player)) {
|
||||
if (player->isAlive()) {
|
||||
if (demo_.enabled) {
|
||||
@@ -1445,7 +1454,6 @@ void Game::updatePlayers() {
|
||||
}
|
||||
}
|
||||
|
||||
// Comprueba las colisiones entre el jugador y los items
|
||||
checkPlayerItemCollision(player);
|
||||
}
|
||||
}
|
||||
@@ -1458,19 +1466,18 @@ void Game::renderPlayers() {
|
||||
}
|
||||
|
||||
// Actualiza las variables de la fase
|
||||
void Game::updateStage() {
|
||||
void Game::updateStage(float dt_s) {
|
||||
if (stage_[current_stage_].current_power >= stage_[current_stage_].power_to_complete) {
|
||||
// Cambio de fase
|
||||
current_stage_++;
|
||||
last_stage_reached_ = current_stage_;
|
||||
if (current_stage_ == 10) { // Ha llegado al final el juego
|
||||
game_completed_ = true; // Marca el juego como completado
|
||||
current_stage_ = 9; // Deja el valor dentro de los limites
|
||||
stage_[current_stage_].current_power = 0; // Deja el poder a cero para que no vuelva a entrar en esta condición
|
||||
destroyAllBalloons(); // Destruye a todos los enemigos
|
||||
stage_[current_stage_].current_power = 0; // Vuelve a dejar el poder a cero, por lo que hubiera podido subir al destruir todos lo globos
|
||||
menace_current_ = 255; // Sube el nivel de amenaza para que no cree mas globos
|
||||
for (auto *player : players_) { // Añade un millon de puntos a los jugadores que queden vivos
|
||||
if (current_stage_ == 10) {
|
||||
game_completed_ = true;
|
||||
current_stage_ = 9;
|
||||
stage_[current_stage_].current_power = 0;
|
||||
destroyAllBalloons();
|
||||
stage_[current_stage_].current_power = 0;
|
||||
menace_current_ = 255;
|
||||
for (auto *player : players_) {
|
||||
if (player->isAlive()) {
|
||||
player->addScore(1000000);
|
||||
}
|
||||
@@ -1480,45 +1487,52 @@ void Game::updateStage() {
|
||||
}
|
||||
Audio::get()->playSound(stage_change_sound_);
|
||||
stage_bitmap_counter_ = 0;
|
||||
stage_bitmap_counter_s_ = 0.0F;
|
||||
enemy_speed_ = default_enemy_speed_;
|
||||
setBalloonSpeed(enemy_speed_);
|
||||
effect_.flash = true;
|
||||
effect_.shake = true;
|
||||
}
|
||||
|
||||
// Incrementa el contador del bitmap que aparece mostrando el cambio de fase
|
||||
if (stage_bitmap_counter_ < STAGE_COUNTER) {
|
||||
stage_bitmap_counter_++;
|
||||
if (stage_bitmap_counter_s_ < (STAGE_COUNTER / 60.0F)) {
|
||||
stage_bitmap_counter_s_ += dt_s;
|
||||
}
|
||||
stage_bitmap_counter_ = std::min<int>(STAGE_COUNTER, static_cast<int>(stage_bitmap_counter_s_ * 60.0F));
|
||||
|
||||
// Si el juego se ha completado, el bitmap se detiene en el centro de la pantalla
|
||||
if (game_completed_) {
|
||||
stage_bitmap_counter_ = std::min<int>(stage_bitmap_counter_, 100);
|
||||
}
|
||||
}
|
||||
|
||||
// Actualiza el estado de muerte
|
||||
void Game::updateDeath() {
|
||||
// Comprueba si todos los jugadores estan muertos
|
||||
// Actualiza el estado de muerte. Detecta el creuament dels llindars 250/200/180/120/60
|
||||
// (en frames) per a reproduir els bubbles als mateixos moments.
|
||||
void Game::updateDeath(float dt_s) {
|
||||
bool all_dead = true;
|
||||
for (const auto *player : players_) {
|
||||
all_dead &= (!player->isAlive());
|
||||
}
|
||||
|
||||
if (all_dead) {
|
||||
if (death_counter_ > 0) {
|
||||
death_counter_--;
|
||||
if (!all_dead) { return; }
|
||||
|
||||
if ((death_counter_ == 250) || (death_counter_ == 200) || (death_counter_ == 180) || (death_counter_ == 120) || (death_counter_ == 60)) {
|
||||
// Hace sonar aleatoriamente uno de los 4 sonidos de burbujas
|
||||
if (!demo_.enabled) {
|
||||
const Uint8 INDEX = rand() % 4;
|
||||
Ja::Sound *sound[4] = {bubble1_sound_, bubble2_sound_, bubble3_sound_, bubble4_sound_};
|
||||
Audio::get()->playSound(sound[INDEX]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
section_->subsection = SUBSECTION_GAME_GAMEOVER;
|
||||
if (death_counter_s_ <= 0.0F) {
|
||||
section_->subsection = SUBSECTION_GAME_GAMEOVER;
|
||||
return;
|
||||
}
|
||||
|
||||
const float PREV_S = death_counter_s_;
|
||||
death_counter_s_ = std::max(0.0F, death_counter_s_ - dt_s);
|
||||
death_counter_ = static_cast<Uint16>(death_counter_s_ * 60.0F);
|
||||
|
||||
auto crossed = [&](float threshold_frames) {
|
||||
const float TS = threshold_frames / 60.0F;
|
||||
return (PREV_S > TS) && (death_counter_s_ <= TS);
|
||||
};
|
||||
|
||||
if (crossed(250.0F) || crossed(200.0F) || crossed(180.0F) || crossed(120.0F) || crossed(60.0F)) {
|
||||
if (!demo_.enabled) {
|
||||
const Uint8 INDEX = rand() % 4;
|
||||
Ja::Sound *sound[4] = {bubble1_sound_, bubble2_sound_, bubble3_sound_, bubble4_sound_};
|
||||
Audio::get()->playSound(sound[INDEX]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1549,9 +1563,9 @@ void Game::renderDeathFade(int counter) { // Counter debe ir de 0 a 150
|
||||
}
|
||||
|
||||
// Actualiza los globos
|
||||
void Game::updateBalloons() {
|
||||
void Game::updateBalloons(float dt_s) {
|
||||
for (auto *balloon : balloons_) {
|
||||
balloon->update();
|
||||
balloon->update(dt_s);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1662,7 +1676,7 @@ void Game::popBalloon(Balloon *balloon) {
|
||||
const int INDEX = createBalloon(0, balloon->getPosY(), balloon->getKind() - 1, Balloon::VELX_NEGATIVE, enemy_speed_, 0);
|
||||
balloons_[INDEX]->allignTo(balloon->getPosX() + (balloon->getWidth() / 2));
|
||||
if (balloons_[INDEX]->getClass() == Balloon::BALLOON_CLASS) {
|
||||
balloons_[INDEX]->setVelY(-2.50F);
|
||||
balloons_[INDEX]->setVelY(-150.0F); // -2.5 px/frame ⇒ -150 px/s
|
||||
} else {
|
||||
balloons_[INDEX]->setVelY(Balloon::VELX_NEGATIVE);
|
||||
}
|
||||
@@ -1670,7 +1684,7 @@ void Game::popBalloon(Balloon *balloon) {
|
||||
const int INDEX2 = createBalloon(0, balloon->getPosY(), balloon->getKind() - 1, Balloon::VELX_POSITIVE, enemy_speed_, 0);
|
||||
balloons_[INDEX2]->allignTo(balloon->getPosX() + (balloon->getWidth() / 2));
|
||||
if (balloons_[INDEX2]->getClass() == Balloon::BALLOON_CLASS) {
|
||||
balloons_[INDEX2]->setVelY(-2.50F);
|
||||
balloons_[INDEX2]->setVelY(-150.0F);
|
||||
} else {
|
||||
balloons_[INDEX2]->setVelY(Balloon::VELX_NEGATIVE);
|
||||
}
|
||||
@@ -1904,10 +1918,10 @@ void Game::resolveBulletBalloonHit(Bullet *bullet, Balloon *balloon) {
|
||||
}
|
||||
|
||||
// Mueve las balas activas
|
||||
void Game::moveBullets() {
|
||||
void Game::moveBullets(float dt_s) {
|
||||
for (auto *bullet : bullets_) {
|
||||
if (bullet->isEnabled()) {
|
||||
if (bullet->move() == Bullet::MoveResult::OUT) {
|
||||
if (bullet->move(dt_s) == Bullet::MoveResult::OUT) {
|
||||
players_[bullet->getOwner()]->decScoreMultiplier();
|
||||
}
|
||||
}
|
||||
@@ -1942,10 +1956,10 @@ void Game::freeBullets() {
|
||||
}
|
||||
|
||||
// Actualiza los items
|
||||
void Game::updateItems() {
|
||||
void Game::updateItems(float dt_s) {
|
||||
for (auto *item : items_) {
|
||||
if (item->isEnabled()) {
|
||||
item->update();
|
||||
item->update(dt_s);
|
||||
if (item->isOnFloor()) {
|
||||
Audio::get()->playSound(coffee_machine_sound_);
|
||||
effect_.shake = true;
|
||||
@@ -2067,14 +2081,26 @@ void Game::renderFlashEffect() {
|
||||
}
|
||||
}
|
||||
|
||||
// Actualiza el efecto de agitar la pantalla
|
||||
void Game::updateShakeEffect() {
|
||||
if (effect_.shake) {
|
||||
// Actualiza el efecto de agitar la pantalla. Decrementa `shake_counter` a
|
||||
// cadència fixa de 60Hz amb un acumulador de fase (independent del framerate)
|
||||
// — el render de `updateBackground` segueix llegint la paritat del counter
|
||||
// per fer vibrar els edificis.
|
||||
void Game::updateShakeEffect(float dt_s) {
|
||||
if (!effect_.shake) {
|
||||
shake_phase_s_ = 0.0F;
|
||||
return;
|
||||
}
|
||||
constexpr float STEP_S = 1.0F / 60.0F;
|
||||
shake_phase_s_ += dt_s;
|
||||
while (shake_phase_s_ >= STEP_S) {
|
||||
shake_phase_s_ -= STEP_S;
|
||||
if (effect_.shake_counter > 0) {
|
||||
effect_.shake_counter--;
|
||||
} else {
|
||||
effect_.shake = false;
|
||||
effect_.shake_counter = SHAKE_COUNTER;
|
||||
shake_phase_s_ = 0.0F;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2088,10 +2114,12 @@ void Game::throwCoffee(int x, int y) {
|
||||
ss->setPosY(y - 8);
|
||||
ss->setWidth(16);
|
||||
ss->setHeight(16);
|
||||
ss->setVelX(-1.0F + ((rand() % 5) * 0.5F));
|
||||
ss->setVelY(-4.0F);
|
||||
// Conversió a px/s i px/s² (era -1..1 px/frame i 0.2 px/frame²)
|
||||
const float VX_PX_PER_S = (-1.0F + (static_cast<float>(rand() % 5) * 0.5F)) * 60.0F;
|
||||
ss->setVelX(VX_PX_PER_S);
|
||||
ss->setVelY(-240.0F);
|
||||
ss->setAccelX(0.0F);
|
||||
ss->setAccelY(0.2F);
|
||||
ss->setAccelY(720.0F);
|
||||
ss->setDestX(x + (ss->getVelX() * 50));
|
||||
ss->setDestY(GAMECANVAS_HEIGHT + 1);
|
||||
ss->setEnabled(true);
|
||||
@@ -2103,9 +2131,9 @@ void Game::throwCoffee(int x, int y) {
|
||||
}
|
||||
|
||||
// Actualiza los SmartSprites
|
||||
void Game::updateSmartSprites() {
|
||||
void Game::updateSmartSprites(float dt_s) {
|
||||
for (auto *ss : smart_sprites_) {
|
||||
ss->update();
|
||||
ss->update(dt_s);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2196,114 +2224,87 @@ auto Game::isTimeStopped() const -> bool {
|
||||
// Establece el valor de la variable
|
||||
void Game::setTimeStoppedCounter(Uint16 value) {
|
||||
time_stopped_counter_ = value;
|
||||
time_stopped_counter_s_ = static_cast<float>(value) / 60.0F;
|
||||
}
|
||||
|
||||
// Incrementa el valor de la variable
|
||||
void Game::incTimeStoppedCounter(Uint16 value) {
|
||||
time_stopped_counter_ += value;
|
||||
time_stopped_counter_s_ += static_cast<float>(value) / 60.0F;
|
||||
}
|
||||
|
||||
// Actualiza y comprueba el valor de la variable
|
||||
void Game::updateTimeStoppedCounter() {
|
||||
if (isTimeStopped()) {
|
||||
if (time_stopped_counter_ > 0) {
|
||||
time_stopped_counter_--;
|
||||
stopAllBalloons(TIME_STOPPED_COUNTER);
|
||||
} else {
|
||||
disableTimeStopItem();
|
||||
}
|
||||
void Game::updateTimeStoppedCounter(float dt_s) {
|
||||
if (!isTimeStopped()) { return; }
|
||||
if (time_stopped_counter_s_ > 0.0F) {
|
||||
time_stopped_counter_s_ = std::max(0.0F, time_stopped_counter_s_ - dt_s);
|
||||
time_stopped_counter_ = static_cast<Uint16>(time_stopped_counter_s_ * 60.0F);
|
||||
stopAllBalloons(TIME_STOPPED_COUNTER);
|
||||
} else {
|
||||
disableTimeStopItem();
|
||||
}
|
||||
}
|
||||
|
||||
// Actualiza la variable enemyDeployCounter
|
||||
void Game::updateEnemyDeployCounter() {
|
||||
if (enemy_deploy_counter_ > 0) {
|
||||
// Actualiza la variable enemyDeployCounter. Decrementa a 60Hz fixe amb
|
||||
// acumulador de fase — és un comptador discret consultat per
|
||||
// `canPowerBallBeCreated()` i altres.
|
||||
void Game::updateEnemyDeployCounter(float dt_s) {
|
||||
if (enemy_deploy_counter_ <= 0) { return; }
|
||||
constexpr float STEP_S = 1.0F / 60.0F;
|
||||
enemy_deploy_phase_s_ += dt_s;
|
||||
while (enemy_deploy_phase_s_ >= STEP_S && enemy_deploy_counter_ > 0) {
|
||||
enemy_deploy_phase_s_ -= STEP_S;
|
||||
enemy_deploy_counter_--;
|
||||
}
|
||||
}
|
||||
|
||||
// Actualiza el juego
|
||||
void Game::update() {
|
||||
// Actualiza el audio
|
||||
// Actualiza el juego. La cadència la dicta dt_s, propagat des de iterate()
|
||||
// via DeltaTime::tick(). El comptador global `counter_` queda derivat de
|
||||
// `elapsed_s_*60` perquè els lectors existents (render de l'herba, paths del
|
||||
// get_ready, etc.) segueixin valuant.
|
||||
void Game::update(float dt_s) {
|
||||
Audio::update();
|
||||
|
||||
// Actualiza los efectos basados en tiempo real (no en el throttle del juego)
|
||||
updateDeathShake();
|
||||
updateDeathSequence();
|
||||
|
||||
// Durante la secuencia de muerte, congela el resto del juego
|
||||
if (death_sequence_.phase == DeathPhase::SHAKING || death_sequence_.phase == DeathPhase::WAITING) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Comprueba que la diferencia de ticks sea mayor a la velocidad del juego
|
||||
if (SDL_GetTicks() - ticks_ > ticks_speed_) {
|
||||
// Actualiza el contador de ticks
|
||||
ticks_ = SDL_GetTicks();
|
||||
// Acumulador i derivació del comptador legacy
|
||||
elapsed_s_ += dt_s;
|
||||
counter_ = static_cast<Uint32>(elapsed_s_ * 60.0F);
|
||||
|
||||
// Actualiza el contador de juego
|
||||
counter_++;
|
||||
checkGameInput();
|
||||
updatePlayers(dt_s);
|
||||
updateBackground(dt_s);
|
||||
updateBalloons(dt_s);
|
||||
moveBullets(dt_s);
|
||||
updateItems(dt_s);
|
||||
updateStage(dt_s);
|
||||
updateDeath(dt_s);
|
||||
updateSmartSprites(dt_s);
|
||||
updateTimeStoppedCounter(dt_s);
|
||||
updateEnemyDeployCounter(dt_s);
|
||||
updateShakeEffect(dt_s);
|
||||
updateHelper(dt_s);
|
||||
checkBulletBalloonCollision();
|
||||
updateMenace();
|
||||
updateBalloonSpeed();
|
||||
updateGameCompleted(dt_s);
|
||||
|
||||
// Comprueba el teclado/mando
|
||||
checkGameInput();
|
||||
|
||||
// Actualiza las variables del jugador
|
||||
updatePlayers();
|
||||
|
||||
// Actualiza el fondo
|
||||
updateBackground();
|
||||
|
||||
// Mueve los globos
|
||||
updateBalloons();
|
||||
|
||||
// Mueve las balas
|
||||
moveBullets();
|
||||
|
||||
// Actualiza los items
|
||||
updateItems();
|
||||
|
||||
// Actualiza el valor de currentStage
|
||||
updateStage();
|
||||
|
||||
// Actualiza el estado de muerte
|
||||
updateDeath();
|
||||
|
||||
// Actualiza los SmartSprites
|
||||
updateSmartSprites();
|
||||
|
||||
// Actualiza los contadores de estado y efectos
|
||||
updateTimeStoppedCounter();
|
||||
updateEnemyDeployCounter();
|
||||
updateShakeEffect();
|
||||
|
||||
// Actualiza el ayudante
|
||||
updateHelper();
|
||||
|
||||
// Comprueba las colisiones entre globos y balas
|
||||
checkBulletBalloonCollision();
|
||||
|
||||
// Comprueba el nivel de amenaza para ver si se han de crear nuevos enemigos
|
||||
updateMenace();
|
||||
|
||||
// Actualiza la velocidad de los enemigos
|
||||
updateBalloonSpeed();
|
||||
|
||||
// Actualiza el tramo final de juego, una vez completado
|
||||
updateGameCompleted();
|
||||
|
||||
// Vacia los vectores
|
||||
freeBullets();
|
||||
freeBalloons();
|
||||
freeItems();
|
||||
freeSmartSprites();
|
||||
}
|
||||
freeBullets();
|
||||
freeBalloons();
|
||||
freeItems();
|
||||
freeSmartSprites();
|
||||
}
|
||||
|
||||
// Actualiza el fondo
|
||||
void Game::updateBackground() {
|
||||
if (!game_completed_) { // Si el juego no esta completo, la velocidad de las nubes es igual a los globos explotados
|
||||
// Actualiza el fondo. Velocitats dels núvols expressades com a px/s.
|
||||
void Game::updateBackground(float dt_s) {
|
||||
if (!game_completed_) {
|
||||
clouds_speed_ = balloons_popped_;
|
||||
} else { // Si el juego está completado, se reduce la velocidad de las nubes
|
||||
} else {
|
||||
if (clouds_speed_ > 400) {
|
||||
clouds_speed_ -= 25;
|
||||
} else {
|
||||
@@ -2311,42 +2312,28 @@ void Game::updateBackground() {
|
||||
}
|
||||
}
|
||||
|
||||
// Calcula la velocidad en función de los globos explotados y el total de globos a explotar para acabar el juego
|
||||
const float SPEED = (-0.2F) + (-3.00F * ((float)clouds_speed_ / (float)total_power_to_complete_game_));
|
||||
// Velocitat per frame (mateixa fórmula); en time-based la passem com a px/s.
|
||||
const float SPEED_PX_PER_FRAME = (-0.2F) + (-3.00F * ((float)clouds_speed_ / (float)total_power_to_complete_game_));
|
||||
const float SPEED_PX_PER_S = SPEED_PX_PER_FRAME * 60.0F;
|
||||
|
||||
// Aplica la velocidad calculada a las nubes
|
||||
clouds1_a_->setVelX(SPEED);
|
||||
clouds1_b_->setVelX(SPEED);
|
||||
clouds2_a_->setVelX(SPEED / 2);
|
||||
clouds2_b_->setVelX(SPEED / 2);
|
||||
clouds1_a_->setVelX(SPEED_PX_PER_S);
|
||||
clouds1_b_->setVelX(SPEED_PX_PER_S);
|
||||
clouds2_a_->setVelX(SPEED_PX_PER_S / 2.0F);
|
||||
clouds2_b_->setVelX(SPEED_PX_PER_S / 2.0F);
|
||||
|
||||
// Mueve las nubes
|
||||
clouds1_a_->move();
|
||||
clouds1_b_->move();
|
||||
clouds2_a_->move();
|
||||
clouds2_b_->move();
|
||||
clouds1_a_->move(dt_s);
|
||||
clouds1_b_->move(dt_s);
|
||||
clouds2_a_->move(dt_s);
|
||||
clouds2_b_->move(dt_s);
|
||||
|
||||
// Calcula el offset de las nubes
|
||||
if (clouds1_a_->getPosX() < -clouds1_a_->getWidth()) {
|
||||
clouds1_a_->setPosX(clouds1_a_->getWidth());
|
||||
}
|
||||
if (clouds1_a_->getPosX() < -clouds1_a_->getWidth()) { clouds1_a_->setPosX(clouds1_a_->getWidth()); }
|
||||
if (clouds1_b_->getPosX() < -clouds1_b_->getWidth()) { clouds1_b_->setPosX(clouds1_b_->getWidth()); }
|
||||
if (clouds2_a_->getPosX() < -clouds2_a_->getWidth()) { clouds2_a_->setPosX(clouds2_a_->getWidth()); }
|
||||
if (clouds2_b_->getPosX() < -clouds2_b_->getWidth()) { clouds2_b_->setPosX(clouds2_b_->getWidth()); }
|
||||
|
||||
if (clouds1_b_->getPosX() < -clouds1_b_->getWidth()) {
|
||||
clouds1_b_->setPosX(clouds1_b_->getWidth());
|
||||
}
|
||||
|
||||
if (clouds2_a_->getPosX() < -clouds2_a_->getWidth()) {
|
||||
clouds2_a_->setPosX(clouds2_a_->getWidth());
|
||||
}
|
||||
|
||||
if (clouds2_b_->getPosX() < -clouds2_b_->getWidth()) {
|
||||
clouds2_b_->setPosX(clouds2_b_->getWidth());
|
||||
}
|
||||
|
||||
// Calcula el frame de la hierba
|
||||
// Herba: `counter_` derivat de `elapsed_s_*60` ja oscil·la a 60Hz.
|
||||
grass_sprite_->setSpriteClip(0, (6 * (counter_ / 20 % 2)), 256, 6);
|
||||
|
||||
// Mueve los edificios en funcion de si está activo el efecto de agitarlos
|
||||
if (death_shake_.active) {
|
||||
const int V[] = {-1, 1, -1, 1, -1, 1, -1, 0};
|
||||
buildings_sprite_->setPosX(V[death_shake_.step]);
|
||||
@@ -2679,55 +2666,62 @@ auto Game::isDeathShaking() const -> bool {
|
||||
|
||||
// Ejecuta un frame del juego
|
||||
void Game::iterate() {
|
||||
// En modo demo, no hay pausa ni game over
|
||||
if (demo_.enabled) {
|
||||
if (section_->subsection == SUBSECTION_GAME_PAUSE || section_->subsection == SUBSECTION_GAME_GAMEOVER) {
|
||||
section_->name = SECTION_PROG_TITLE;
|
||||
section_->subsection = SUBSECTION_TITLE_INSTRUCTIONS;
|
||||
return;
|
||||
}
|
||||
// Consum del temps real des de l'última iteració. Sempre s'ha de cridar
|
||||
// perquè el rellotge no acumuli a través de transicions/sub-estats.
|
||||
const float DELTA_TIME_S = DeltaTime::tick();
|
||||
|
||||
// En modo demo, ni pause ni game over: torna immediatament al títol
|
||||
if (demo_.enabled && (section_->subsection == SUBSECTION_GAME_PAUSE || section_->subsection == SUBSECTION_GAME_GAMEOVER)) {
|
||||
section_->name = SECTION_PROG_TITLE;
|
||||
section_->subsection = SUBSECTION_TITLE_INSTRUCTIONS;
|
||||
return;
|
||||
}
|
||||
|
||||
// Sección juego en pausa
|
||||
if (section_->subsection == SUBSECTION_GAME_PAUSE) {
|
||||
if (!pause_initialized_) {
|
||||
enterPausedGame();
|
||||
}
|
||||
updatePausedGame();
|
||||
renderPausedGame();
|
||||
switch (section_->subsection) {
|
||||
case SUBSECTION_GAME_PAUSE:
|
||||
iteratePaused(DELTA_TIME_S);
|
||||
break;
|
||||
case SUBSECTION_GAME_GAMEOVER:
|
||||
iterateGameOver(DELTA_TIME_S);
|
||||
break;
|
||||
case SUBSECTION_GAME_PLAY_1P:
|
||||
case SUBSECTION_GAME_PLAY_2P:
|
||||
iteratePlaying(DELTA_TIME_S);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Rama de iterate(): pause
|
||||
void Game::iteratePaused(float dt_s) {
|
||||
if (!pause_initialized_) { enterPausedGame(); }
|
||||
updatePausedGame(dt_s);
|
||||
renderPausedGame();
|
||||
}
|
||||
|
||||
// Rama de iterate(): game over
|
||||
void Game::iterateGameOver(float dt_s) {
|
||||
if (!game_over_initialized_) { enterGameOverScreen(); }
|
||||
updateGameOverScreen(dt_s);
|
||||
renderGameOverScreen();
|
||||
}
|
||||
|
||||
// Rama de iterate(): joc actiu
|
||||
void Game::iteratePlaying(float dt_s) {
|
||||
// Si veníem de Pause/GameOver, el dt acumulat seria enorme; descarta'l.
|
||||
if (pause_initialized_ || game_over_initialized_) {
|
||||
DeltaTime::reset();
|
||||
}
|
||||
pause_initialized_ = false;
|
||||
game_over_initialized_ = false;
|
||||
|
||||
if (Audio::getRealMusicState() == Audio::MusicState::STOPPED && !game_completed_ && !demo_.enabled && players_[0]->isAlive()) {
|
||||
Audio::get()->playMusic(game_music_);
|
||||
}
|
||||
|
||||
// Sección Game Over
|
||||
else if (section_->subsection == SUBSECTION_GAME_GAMEOVER) {
|
||||
if (!game_over_initialized_) {
|
||||
enterGameOverScreen();
|
||||
}
|
||||
updateGameOverScreen();
|
||||
renderGameOverScreen();
|
||||
}
|
||||
|
||||
// Sección juego jugando
|
||||
else if ((section_->subsection == SUBSECTION_GAME_PLAY_1P) || (section_->subsection == SUBSECTION_GAME_PLAY_2P)) {
|
||||
// Resetea los flags de inicialización de sub-estados
|
||||
pause_initialized_ = false;
|
||||
game_over_initialized_ = false;
|
||||
|
||||
// Si la música no está sonando
|
||||
if ((Audio::getRealMusicState() == Audio::MusicState::STOPPED) || (Audio::getRealMusicState() == Audio::MusicState::STOPPED)) {
|
||||
// Reproduce la música (nunca en modo demo: deja sonar la del título)
|
||||
if (!game_completed_ && !demo_.enabled) {
|
||||
if (players_[0]->isAlive()) {
|
||||
Audio::get()->playMusic(game_music_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Actualiza la lógica del juego
|
||||
update();
|
||||
|
||||
// Dibuja los objetos
|
||||
render();
|
||||
}
|
||||
update(dt_s);
|
||||
render();
|
||||
}
|
||||
|
||||
// Indica si el juego ha terminado
|
||||
@@ -2765,54 +2759,50 @@ void Game::run() {
|
||||
}
|
||||
}
|
||||
|
||||
// Actualiza las variables del menu de pausa del juego
|
||||
void Game::updatePausedGame() {
|
||||
if (SDL_GetTicks() - ticks_ <= ticks_speed_) {
|
||||
return;
|
||||
}
|
||||
ticks_ = SDL_GetTicks();
|
||||
|
||||
// Atalls globals (zoom finestra, fullscreen, shaders, presets, ...)
|
||||
// Actualiza el menu de pausa
|
||||
void Game::updatePausedGame(float dt_s) {
|
||||
GlobalInputs::handle();
|
||||
|
||||
if (leaving_pause_menu_) {
|
||||
updateLeavingPauseMenu();
|
||||
updateLeavingPauseMenu(dt_s);
|
||||
} else {
|
||||
updatePauseMenuUI();
|
||||
updatePauseMenuUI(dt_s);
|
||||
}
|
||||
}
|
||||
|
||||
// Rama de updatePausedGame: cuenta atrás de salida y vuelta al juego
|
||||
void Game::updateLeavingPauseMenu() {
|
||||
if (pause_counter_ > 0) { // El contador está descendiendo
|
||||
const bool A = pause_counter_ == 90;
|
||||
const bool B = pause_counter_ == 60;
|
||||
const bool C = pause_counter_ == 30;
|
||||
if (A || B || C) {
|
||||
Audio::get()->playSound(clock_sound_);
|
||||
// Rama de updatePausedGame: cuenta atrás de salida y vuelta al juego.
|
||||
// Decrementa pause_counter_ a 60Hz exactes amb un acumulador de fase per a
|
||||
// mantenir els sons de rellotge als llindars originals (90, 60, 30 frames =
|
||||
// 1.5s, 1.0s, 0.5s restants).
|
||||
void Game::updateLeavingPauseMenu(float dt_s) {
|
||||
if (pause_counter_ <= 0) {
|
||||
section_->name = SECTION_PROG_GAME;
|
||||
section_->subsection = num_players_ == 1 ? SUBSECTION_GAME_PLAY_1P : SUBSECTION_GAME_PLAY_2P;
|
||||
if (Audio::getRealMusicState() == Audio::MusicState::PAUSED) {
|
||||
Audio::get()->resumeMusic();
|
||||
}
|
||||
pause_counter_--;
|
||||
return;
|
||||
}
|
||||
|
||||
// Ha finalizado el contador
|
||||
section_->name = SECTION_PROG_GAME;
|
||||
section_->subsection = num_players_ == 1 ? SUBSECTION_GAME_PLAY_1P : SUBSECTION_GAME_PLAY_2P;
|
||||
|
||||
if (Audio::getRealMusicState() == Audio::MusicState::PAUSED) {
|
||||
Audio::get()->resumeMusic();
|
||||
constexpr float STEP_S = 1.0F / 60.0F;
|
||||
pause_counter_phase_s_ += dt_s;
|
||||
while (pause_counter_phase_s_ >= STEP_S && pause_counter_ > 0) {
|
||||
pause_counter_phase_s_ -= STEP_S;
|
||||
if (pause_counter_ == 90 || pause_counter_ == 60 || pause_counter_ == 30) {
|
||||
Audio::get()->playSound(clock_sound_);
|
||||
}
|
||||
pause_counter_--;
|
||||
}
|
||||
}
|
||||
|
||||
// Rama de updatePausedGame: lógica del menú de pausa
|
||||
void Game::updatePauseMenuUI() {
|
||||
// Lógica del menú de pausa (time-based)
|
||||
void Game::updatePauseMenuUI(float dt_s) {
|
||||
pause_menu_->update();
|
||||
pause_menu_->checkInput();
|
||||
|
||||
// F12 (Action::PAUSE) també tanca el menú de pausa — mateix comportament
|
||||
// que seleccionar "Continue" / cancel·lar amb BACKSPACE.
|
||||
if (Input::get()->checkInput(Input::Action::PAUSE, Input::Repeat::OFF)) {
|
||||
leaving_pause_menu_ = true;
|
||||
pause_counter_phase_s_ = 0.0F;
|
||||
if (!Options::gameplay.pause_countdown) {
|
||||
pause_counter_ = 0;
|
||||
}
|
||||
@@ -2822,21 +2812,20 @@ void Game::updatePauseMenuUI() {
|
||||
switch (pause_menu_->getItemSelected()) {
|
||||
case 1:
|
||||
leaving_pause_menu_ = true;
|
||||
pause_counter_phase_s_ = 0.0F;
|
||||
if (!Options::gameplay.pause_countdown) {
|
||||
pause_counter_ = 0; // salta el compte enrere de 3 segons
|
||||
pause_counter_ = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
fade_->setFadeType(Fade::Type::CENTER);
|
||||
fade_->activateFade();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
fade_->update();
|
||||
fade_->update(dt_s);
|
||||
if (fade_->hasEnded()) {
|
||||
section_->name = SECTION_PROG_TITLE;
|
||||
section_->subsection = SUBSECTION_TITLE_1;
|
||||
@@ -2904,60 +2893,42 @@ void Game::enterPausedGame() {
|
||||
}
|
||||
|
||||
// Actualiza los elementos de la pantalla de game over
|
||||
void Game::updateGameOverScreen() {
|
||||
// Calcula la lógica de los objetos
|
||||
if (SDL_GetTicks() - ticks_ > ticks_speed_) {
|
||||
// Actualiza el contador de ticks
|
||||
ticks_ = SDL_GetTicks();
|
||||
void Game::updateGameOverScreen(float dt_s) {
|
||||
GlobalInputs::handle();
|
||||
|
||||
// Atalls globals (zoom finestra, fullscreen, shaders, presets, ...)
|
||||
GlobalInputs::handle();
|
||||
game_over_menu_->update();
|
||||
fade_->update(dt_s);
|
||||
|
||||
// Actualiza la lógica del menu
|
||||
game_over_menu_->update();
|
||||
|
||||
// Actualiza el fade
|
||||
fade_->update();
|
||||
|
||||
// Si ha terminado el fade, actua segun se haya operado
|
||||
if (fade_->hasEnded()) {
|
||||
switch (game_over_post_fade_) {
|
||||
case 0: // YES
|
||||
section_->name = SECTION_PROG_GAME;
|
||||
deleteAllVectorObjects();
|
||||
init();
|
||||
section_->subsection = num_players_ == 1 ? SUBSECTION_GAME_PLAY_1P : SUBSECTION_GAME_PLAY_2P;
|
||||
break;
|
||||
|
||||
case 1: // NO
|
||||
section_->name = SECTION_PROG_TITLE;
|
||||
section_->subsection = SUBSECTION_TITLE_1;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (fade_->hasEnded()) {
|
||||
switch (game_over_post_fade_) {
|
||||
case 0: // YES
|
||||
section_->name = SECTION_PROG_GAME;
|
||||
deleteAllVectorObjects();
|
||||
init();
|
||||
section_->subsection = num_players_ == 1 ? SUBSECTION_GAME_PLAY_1P : SUBSECTION_GAME_PLAY_2P;
|
||||
break;
|
||||
case 1: // NO
|
||||
section_->name = SECTION_PROG_TITLE;
|
||||
section_->subsection = SUBSECTION_TITLE_1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Comprueba las entradas para el menu solo si no esta el juego completo
|
||||
if (!game_completed_) {
|
||||
game_over_menu_->checkInput();
|
||||
|
||||
// Comprueba si se ha seleccionado algún item del menú
|
||||
switch (game_over_menu_->getItemSelected()) {
|
||||
case 0: // YES
|
||||
game_over_post_fade_ = 0;
|
||||
fade_->activateFade();
|
||||
break;
|
||||
|
||||
case 1: // NO
|
||||
game_over_post_fade_ = 1;
|
||||
fade_->activateFade();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (!game_completed_) {
|
||||
game_over_menu_->checkInput();
|
||||
switch (game_over_menu_->getItemSelected()) {
|
||||
case 0: // YES
|
||||
game_over_post_fade_ = 0;
|
||||
fade_->activateFade();
|
||||
break;
|
||||
case 1: // NO
|
||||
game_over_post_fade_ = 1;
|
||||
fade_->activateFade();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3099,23 +3070,24 @@ void Game::initPaths() {
|
||||
}
|
||||
|
||||
// Actualiza el tramo final de juego, una vez completado
|
||||
void Game::updateGameCompleted() {
|
||||
if (game_completed_) {
|
||||
game_completed_counter_++;
|
||||
}
|
||||
void Game::updateGameCompleted(float dt_s) {
|
||||
if (!game_completed_) { return; }
|
||||
|
||||
if (game_completed_counter_ == GAME_COMPLETED_END) {
|
||||
const int PREV = game_completed_counter_;
|
||||
game_completed_counter_s_ += dt_s;
|
||||
game_completed_counter_ = static_cast<int>(game_completed_counter_s_ * 60.0F);
|
||||
|
||||
if (PREV < GAME_COMPLETED_END && game_completed_counter_ >= GAME_COMPLETED_END) {
|
||||
section_->subsection = SUBSECTION_GAME_GAMEOVER;
|
||||
}
|
||||
}
|
||||
|
||||
// Actualiza las variables de ayuda
|
||||
void Game::updateHelper() {
|
||||
// Solo ofrece ayuda cuando la amenaza es elevada
|
||||
// Actualiza las variables de ayuda. De moment cap timer real dins helper_;
|
||||
// el dt_s queda reservat per a futurs comptadors d'helper.
|
||||
void Game::updateHelper([[maybe_unused]] float dt_s) {
|
||||
if (menace_current_ > 15) {
|
||||
for (const auto *player : players_) {
|
||||
helper_.need_coffee = player->getCoffees() == 0;
|
||||
|
||||
helper_.need_coffee_machine = !player->isPowerUp();
|
||||
}
|
||||
} else {
|
||||
|
||||
+53
-40
@@ -38,6 +38,11 @@ class Game {
|
||||
void handleEvent(const SDL_Event *event); // Procesa un evento
|
||||
|
||||
private:
|
||||
// Branques de iterate() — separades per a reduir la complexitat cognitiva
|
||||
void iteratePaused(float dt_s);
|
||||
void iterateGameOver(float dt_s);
|
||||
void iteratePlaying(float dt_s);
|
||||
|
||||
// Cantidad de elementos a escribir en los ficheros de datos
|
||||
static constexpr int TOTAL_SCORE_DATA = 3;
|
||||
static constexpr int TOTAL_DEMO_DATA = 2000;
|
||||
@@ -141,10 +146,10 @@ class Game {
|
||||
DemoKeys data_file[TOTAL_DEMO_DATA]; // Datos del fichero con los movimientos para la demo
|
||||
};
|
||||
|
||||
void update(); // Actualiza el juego
|
||||
void render(); // Dibuja el juego
|
||||
void init(); // Inicializa las variables necesarias para la sección 'Game'
|
||||
void loadMedia(); // Carga los recursos necesarios para la sección 'Game'
|
||||
void update(float dt_s); // Actualiza el juego
|
||||
void render(); // Dibuja el juego
|
||||
void init(); // Inicializa las variables necesarias para la sección 'Game'
|
||||
void loadMedia(); // Carga los recursos necesarios para la sección 'Game'
|
||||
|
||||
auto loadScoreFile() -> bool; // Carga el fichero de puntos
|
||||
auto loadDemoFile() -> bool; // Carga el fichero de datos para la demo
|
||||
@@ -167,14 +172,14 @@ class Game {
|
||||
static auto updateScoreText(Uint32 num) -> std::string; // Transforma un valor numérico en una cadena de 6 cifras
|
||||
void renderScoreBoard(); // Pinta el marcador en pantalla usando un objeto texto
|
||||
|
||||
void updatePlayers(); // Actualiza las variables del jugador
|
||||
void renderPlayers(); // Dibuja a los jugadores
|
||||
void updatePlayers(float dt_s); // Actualiza las variables del jugador
|
||||
void renderPlayers(); // Dibuja a los jugadores
|
||||
|
||||
void updateStage(); // Actualiza las variables de la fase
|
||||
void updateDeath(); // Actualiza el estado de muerte
|
||||
void updateStage(float dt_s); // Actualiza las variables de la fase
|
||||
void updateDeath(float dt_s); // Actualiza el estado de muerte
|
||||
void renderDeathFade(int counter); // Renderiza el fade final cuando se acaba la partida
|
||||
|
||||
void updateBalloons(); // Actualiza los globos
|
||||
void updateBalloons(float dt_s); // Actualiza los globos
|
||||
void renderBalloons(); // Pinta en pantalla todos los globos activos
|
||||
auto createBalloon(float x, float y, Uint8 kind, float velx, float speed, Uint16 creationtimer) -> Uint8; // Crea un globo nuevo en el vector de globos
|
||||
void createPowerBall(); // Crea una PowerBall
|
||||
@@ -193,12 +198,12 @@ class Game {
|
||||
void checkBulletBalloonCollision(); // Comprueba la colisión entre las balas y los globos
|
||||
void resolveBulletBalloonHit(Bullet *bullet, Balloon *balloon); // Resuelve un impacto bala-globo (helper de checkBulletBalloonCollision)
|
||||
|
||||
void moveBullets(); // Mueve las balas activas
|
||||
void moveBullets(float dt_s); // Mueve las balas activas
|
||||
void renderBullets(); // Pinta las balas activas
|
||||
void createBullet(int x, int y, Bullet::Kind kind, bool powered_up, int owner); // Crea un objeto bala
|
||||
void freeBullets(); // Vacia el vector de balas
|
||||
|
||||
void updateItems(); // Actualiza los items
|
||||
void updateItems(float dt_s); // Actualiza los items
|
||||
void renderItems(); // Pinta los items activos
|
||||
auto dropItem() -> Item::Id; // Devuelve un item en función del azar
|
||||
void createItem(Item::Id kind, float x, float y); // Crea un objeto item
|
||||
@@ -207,11 +212,11 @@ class Game {
|
||||
void createItemScoreSprite(int x, int y, const SmartSprite *sprite); // Crea un objeto SmartSprite
|
||||
void freeSmartSprites(); // Vacia el vector de smartsprites
|
||||
|
||||
void renderFlashEffect(); // Dibuja el efecto de flash
|
||||
void updateShakeEffect(); // Actualiza el efecto de agitar la pantalla
|
||||
void throwCoffee(int x, int y); // Crea un SmartSprite para arrojar el item café al recibir un impacto
|
||||
void updateSmartSprites(); // Actualiza los SmartSprites
|
||||
void renderSmartSprites(); // Pinta los SmartSprites activos
|
||||
void renderFlashEffect(); // Dibuja el efecto de flash
|
||||
void updateShakeEffect(float dt_s); // Actualiza el efecto de agitar la pantalla
|
||||
void throwCoffee(int x, int y); // Crea un SmartSprite para arrojar el item café al recibir un impacto
|
||||
void updateSmartSprites(float dt_s); // Actualiza los SmartSprites
|
||||
void renderSmartSprites(); // Pinta los SmartSprites activos
|
||||
|
||||
void killPlayer(Player *player); // Acciones a realizar cuando el jugador muere
|
||||
void evaluateAndSetMenace(); // Calcula y establece el valor de amenaza en funcion de los globos activos
|
||||
@@ -222,11 +227,11 @@ class Game {
|
||||
void setTimeStoppedCounter(Uint16 value); // Establece el valor de la variable
|
||||
void incTimeStoppedCounter(Uint16 value); // Incrementa el valor de la variable
|
||||
|
||||
void updateEnemyDeployCounter(); // Actualiza la variable EnemyDeployCounter
|
||||
void updateTimeStoppedCounter(); // Actualiza y comprueba el valor de la variable
|
||||
void updateMenace(); // Gestiona el nivel de amenaza
|
||||
void updateBackground(); // Actualiza el fondo
|
||||
void renderBackground(); // Dibuja el fondo
|
||||
void updateEnemyDeployCounter(float dt_s); // Actualiza la variable EnemyDeployCounter
|
||||
void updateTimeStoppedCounter(float dt_s); // Actualiza y comprueba el valor de la variable
|
||||
void updateMenace(); // Gestiona el nivel de amenaza
|
||||
void updateBackground(float dt_s); // Actualiza el fondo
|
||||
void renderBackground(); // Dibuja el fondo
|
||||
|
||||
void checkGameInput(); // Gestiona la entrada durante el juego
|
||||
void processDemoInput(); // Helper de checkGameInput
|
||||
@@ -241,21 +246,21 @@ class Game {
|
||||
[[nodiscard]] auto isDeathShaking() const -> bool; // Indica si el efecto de agitación intensa está activo
|
||||
void updateDeathSequence(); // Actualiza la secuencia de muerte del jugador
|
||||
|
||||
void updatePausedGame(); // Actualiza las variables del menu de pausa del juego
|
||||
void updateLeavingPauseMenu(); // Helper de updatePausedGame
|
||||
void updatePauseMenuUI(); // Helper de updatePausedGame
|
||||
void renderPausedGame(); // Dibuja el menu de pausa del juego
|
||||
void enterPausedGame(); // Inicializa el estado de pausa del juego
|
||||
void updatePausedGame(float dt_s); // Actualiza el menu de pausa
|
||||
void updateLeavingPauseMenu(float dt_s); // Helper
|
||||
void updatePauseMenuUI(float dt_s); // Helper
|
||||
void renderPausedGame(); // Dibuja el menu de pausa del juego
|
||||
void enterPausedGame(); // Inicializa el estado de pausa del juego
|
||||
|
||||
void updateGameOverScreen(); // Actualiza los elementos de la pantalla de game over
|
||||
void renderGameOverScreen(); // Dibuja los elementos de la pantalla de game over
|
||||
void enterGameOverScreen(); // Inicializa el estado de game over
|
||||
void updateGameOverScreen(float dt_s); // Actualiza game over
|
||||
void renderGameOverScreen(); // Dibuja los elementos de la pantalla de game over
|
||||
void enterGameOverScreen(); // Inicializa el estado de game over
|
||||
|
||||
auto canPowerBallBeCreated() -> bool; // Indica si se puede crear una powerball
|
||||
auto calculateScreenPower() -> int; // Calcula el poder actual de los globos en pantalla
|
||||
void initPaths(); // Inicializa las variables que contienen puntos de ruta para mover objetos
|
||||
void updateGameCompleted(); // Actualiza el tramo final de juego, una vez completado
|
||||
void updateHelper(); // Actualiza las variables de ayuda
|
||||
void updateGameCompleted(float dt_s); // Actualiza el tramo final de juego
|
||||
void updateHelper(float dt_s); // Actualiza las variables de ayuda
|
||||
auto allPlayersAreDead() -> bool; // Comprueba si todos los jugadores han muerto
|
||||
void deleteAllVectorObjects(); // Elimina todos los objetos contenidos en vectores
|
||||
void setHiScore(); // Establece la máxima puntuación desde fichero o desde las puntuaciones online
|
||||
@@ -337,30 +342,36 @@ class Game {
|
||||
|
||||
// Variables
|
||||
int num_players_; // Numero de jugadores
|
||||
Uint32 ticks_; // Contador de ticks para ajustar la velocidad del programa
|
||||
Uint8 ticks_speed_; // Velocidad a la que se repiten los bucles del programa
|
||||
float elapsed_s_{0.0F}; // Acumulador global de temps de joc
|
||||
Uint32 hi_score_; // Puntuación máxima
|
||||
bool hi_score_achieved_; // Indica si se ha superado la puntuación máxima
|
||||
std::string hi_score_name_; // Nombre del jugador que ostenta la máxima puntuación
|
||||
Stage stage_[10]; // Variable con los datos de cada pantalla
|
||||
Uint8 current_stage_; // Indica la fase actual
|
||||
Uint8 stage_bitmap_counter_; // Contador para el tiempo visible del texto de Stage
|
||||
Uint8 stage_bitmap_counter_; // Contador para el tiempo visible del texto de Stage (frame-based)
|
||||
float stage_bitmap_counter_s_{0.0F}; // Contador (time-based)
|
||||
float stage_bitmap_path_[STAGE_COUNTER]; // Vector con los puntos Y por donde se desplaza el texto
|
||||
float get_ready_bitmap_path_[STAGE_COUNTER]; // Vector con los puntos X por donde se desplaza el texto
|
||||
Uint16 death_counter_; // Contador para la animación de muerte del jugador
|
||||
Uint16 death_counter_; // Contador para la animación de muerte del jugador (frame-based)
|
||||
float death_counter_s_{0.0F}; // Contador (time-based)
|
||||
Uint8 menace_current_; // Nivel de amenaza actual
|
||||
Uint8 menace_threshold_; // Umbral del nivel de amenaza. Si el nivel de amenaza cae por debajo del umbral, se generan más globos. Si el umbral aumenta, aumenta el numero de globos
|
||||
bool time_stopped_; // Indica si el tiempo está detenido
|
||||
Uint16 time_stopped_counter_; // Temporizador para llevar la cuenta del tiempo detenido
|
||||
Uint32 counter_; // Contador para el juego
|
||||
Uint16 time_stopped_counter_; // Temporizador (frame-based)
|
||||
float time_stopped_counter_s_{0.0F}; // Temporizador (time-based)
|
||||
Uint32 counter_; // Contador para el juego (frame-based, derivat de elapsed_s_*60 en time-based)
|
||||
Uint32 score_data_file_[TOTAL_SCORE_DATA]; // Datos del fichero de puntos
|
||||
SDL_Rect sky_colors_rect_[4]; // Vector con las coordenadas de los 4 colores de cielo
|
||||
Uint16 balloons_popped_; // Lleva la cuenta de los globos explotados
|
||||
Uint8 last_enemy_deploy_; // Guarda cual ha sido la última formación desplegada para no repetir;
|
||||
int enemy_deploy_counter_; // Cuando se lanza una formación, se le da un valor y no sale otra hasta que llegue a cero
|
||||
int enemy_deploy_counter_; // Cuando se lanza una formación, se le da un valor y no sale otra hasta que llegue a cero (frame-based)
|
||||
float enemy_deploy_counter_s_{0.0F}; // Comptador (time-based)
|
||||
float enemy_speed_; // Velocidad a la que se mueven los enemigos
|
||||
float default_enemy_speed_; // Velocidad base de los enemigos, sin incrementar
|
||||
Effect effect_; // Variable para gestionar los efectos visuales
|
||||
float shake_phase_s_{0.0F}; // Acumulador per decrementar shake_counter a 60Hz (time-based)
|
||||
float helper_counter_s_{0.0F}; // Acumulador per al comptador helper_.counter (time-based)
|
||||
float enemy_deploy_phase_s_{0.0F}; // Acumulador per al decrement de enemy_deploy_counter_ (time-based)
|
||||
DeathShake death_shake_; // Variable para gestionar el efecto de agitación intensa
|
||||
DeathSequence death_sequence_; // Variable para gestionar la secuencia de muerte
|
||||
Helper helper_; // Variable para gestionar las ayudas
|
||||
@@ -368,7 +379,8 @@ class Game {
|
||||
Uint8 power_ball_counter_; // Contador de formaciones enemigas entre la aparicion de una PowerBall y otra
|
||||
bool coffee_machine_enabled_; // Indica si hay una máquina de café en el terreno de juego
|
||||
bool game_completed_; // Indica si se ha completado la partida, llegando al final de la ultima pantalla
|
||||
int game_completed_counter_; // Contador para el tramo final, cuando se ha completado la partida y ya no aparecen más enemigos
|
||||
int game_completed_counter_; // Contador per al tram final (frame-based)
|
||||
float game_completed_counter_s_{0.0F}; // Comptador (time-based)
|
||||
Uint8 difficulty_; // Dificultad del juego
|
||||
float difficulty_score_multiplier_; // Multiplicador de puntos en función de la dificultad
|
||||
Color difficulty_color_; // Color asociado a la dificultad
|
||||
@@ -379,7 +391,8 @@ class Game {
|
||||
Demo demo_; // Variable con todas las variables relacionadas con el modo demo
|
||||
int total_power_to_complete_game_; // La suma del poder necesario para completar todas las fases
|
||||
int clouds_speed_{0}; // Velocidad a la que se desplazan las nubes
|
||||
int pause_counter_; // Contador para salir del menu de pausa y volver al juego
|
||||
int pause_counter_; // Contador per a sortir del menu de pausa (frame-based, frames)
|
||||
float pause_counter_phase_s_{0.0F}; // Acumulador de fase per decrementar pause_counter_ a 60Hz (time-based)
|
||||
bool leaving_pause_menu_; // Indica si esta saliendo del menu de pausa para volver al juego
|
||||
bool pause_initialized_; // Indica si la pausa ha sido inicializada
|
||||
bool game_over_initialized_; // Indica si el game over ha sido inicializado
|
||||
|
||||
@@ -97,6 +97,31 @@ void Instructions::update() {
|
||||
}
|
||||
}
|
||||
|
||||
// Time-based. counter_ es deriva de elapsed_s_*60 (cadència de referència 60Hz)
|
||||
// per a no haver de refactoritzar render() — la geometria del scroll, la
|
||||
// pulsació dels sprites i el blink de manual segueixen llegint counter_.
|
||||
void Instructions::update(float dt_s) {
|
||||
Audio::update();
|
||||
checkInput();
|
||||
|
||||
elapsed_s_ += dt_s;
|
||||
constexpr float FRAMES_PER_S = 60.0F;
|
||||
|
||||
if (mode_ == Mode::AUTO) {
|
||||
counter_ = static_cast<Uint16>(elapsed_s_ * FRAMES_PER_S);
|
||||
if (elapsed_s_ >= SCENE_DURATION_S) {
|
||||
finished_ = true;
|
||||
}
|
||||
} else {
|
||||
// counter_ acotat al rang original 0..59999 per no trencar les expressions
|
||||
// de blink i sprite-cycling que en depenen.
|
||||
counter_ = static_cast<Uint16>(static_cast<int>(elapsed_s_ * FRAMES_PER_S) % 60000);
|
||||
if (manual_quit_) {
|
||||
finished_ = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pinta en pantalla
|
||||
void Instructions::render() {
|
||||
// Pinta en pantalla
|
||||
@@ -252,6 +277,7 @@ void Instructions::start(Mode mode) {
|
||||
manual_quit_ = false;
|
||||
counter_ = 0;
|
||||
ticks_ = 0;
|
||||
elapsed_s_ = 0.0F;
|
||||
}
|
||||
|
||||
// Indica si las instrucciones han terminado
|
||||
|
||||
@@ -23,11 +23,12 @@ class Instructions {
|
||||
Instructions(const Instructions &) = delete;
|
||||
auto operator=(const Instructions &) -> Instructions & = delete;
|
||||
|
||||
void run(Mode mode); // Bucle principal
|
||||
void start(Mode mode); // Inicia las instrucciones (sin bucle)
|
||||
void update(); // Actualiza las variables
|
||||
void render(); // Pinta en pantalla
|
||||
void checkEvents(); // Comprueba los eventos
|
||||
void run(Mode mode); // Bucle principal
|
||||
void start(Mode mode); // Inicia las instrucciones (sin bucle)
|
||||
void update(); // Actualiza las variables (frame-based)
|
||||
void update(float dt_s); // Actualiza las variables (time-based)
|
||||
void render(); // Pinta en pantalla
|
||||
void checkEvents(); // Comprueba los eventos
|
||||
|
||||
[[nodiscard]] auto hasFinished() const -> bool; // Indica si las instrucciones han terminado
|
||||
[[nodiscard]] auto isQuitRequested() const -> bool; // Indica si se ha solicitado salir de la aplicación
|
||||
@@ -43,14 +44,20 @@ class Instructions {
|
||||
Section *section_; // Estado del bucle principal para saber si continua o se sale
|
||||
|
||||
// Variables
|
||||
Uint16 counter_; // Contador
|
||||
Uint16 counter_; // Contador (derivat de elapsed_s_ * 60 en mode time-based)
|
||||
Uint16 counter_end_; // Valor final para el contador
|
||||
Uint32 ticks_; // Contador de ticks para ajustar la velocidad del programa
|
||||
Uint32 ticks_speed_; // Velocidad a la que se repiten los bucles del programa
|
||||
Uint32 ticks_; // Contador de ticks para ajustar la velocidad del programa (frame-based)
|
||||
Uint32 ticks_speed_; // Velocidad a la que se repiten los bucles del programa (frame-based)
|
||||
float elapsed_s_{0.0F}; // Acumulador de temps (time-based)
|
||||
bool manual_quit_; // Indica si se quiere salir del modo manual
|
||||
Mode mode_{Instructions::Mode::AUTO}; // Modo en el que se van a ejecutar las instrucciones
|
||||
bool finished_; // Indica si las instrucciones han terminado
|
||||
bool quit_requested_; // Indica si se ha solicitado salir de la aplicación
|
||||
|
||||
// Time-based: durada total de la escena en mode AUTO (600 frames a 60Hz).
|
||||
static constexpr float SCENE_DURATION_S = 10.0F;
|
||||
// Time-based: temps mínim al mode MANUAL abans de poder sortir (30 frames a 60Hz).
|
||||
static constexpr float MANUAL_QUIT_DELAY_S = 0.5F;
|
||||
|
||||
void checkInput(); // Comprueba las entradas
|
||||
};
|
||||
|
||||
@@ -12,9 +12,29 @@
|
||||
#include "core/rendering/smartsprite.h" // for SmartSprite
|
||||
#include "core/rendering/writer.h" // for Writer
|
||||
#include "core/resources/resource.h"
|
||||
#include "core/system/delta_time.hpp"
|
||||
#include "game/defaults.hpp" // for GAMECANVAS_CENTER_X, GAMECANVAS_FIRST_QU...
|
||||
#include "utils/utils.h" // for Section, Color
|
||||
|
||||
// ===========================================================================
|
||||
// Time-based. Tots els valors precalculats: velocitats en px/s, acceleracions
|
||||
// en px/s^2, durades en segons. La cadència real de la versió frame-based era
|
||||
// empíricament ~60 Hz (el gate `> 15 ms` esperava al següent múltiple del
|
||||
// refresh del SO, típicament 16.67 ms). Per això la conversió és:
|
||||
// vel px/tick → vel * 60 = px/s
|
||||
// acc px/tick² → acc * 3600 = px/s²
|
||||
// counter frames → counter/60 = segons
|
||||
// ===========================================================================
|
||||
|
||||
namespace {
|
||||
// Durades comunes (segons). Arrodonides amunt respecte a la conversió
|
||||
// exacta des de frames per a tenir valors més "bonics" i una mica més
|
||||
// de respir visual.
|
||||
constexpr float BITMAP_REMAINING_TIME_S = 0.5F; // de 0.333 (20/60) ⇒ 0.5
|
||||
constexpr float BITMAP_FALLING_REMAINING_TIME_S = 5.0F; // de 4.167 (250/60) ⇒ 5.0
|
||||
constexpr float TEXT_REMAINING_TIME_S = 3.0F; // 180/60 ⇒ ja és exacte
|
||||
} // namespace
|
||||
|
||||
// Constructor
|
||||
Intro::Intro(SDL_Renderer *renderer, Section *section) {
|
||||
// Copia los punteros
|
||||
@@ -30,8 +50,6 @@ Intro::Intro(SDL_Renderer *renderer, Section *section) {
|
||||
// Inicializa variables
|
||||
section->name = SECTION_PROG_INTRO;
|
||||
section->subsection = 0;
|
||||
ticks_ = 0;
|
||||
ticks_speed_ = 15;
|
||||
scene_ = 1;
|
||||
|
||||
// Inicializa los bitmaps de la intro
|
||||
@@ -40,62 +58,69 @@ Intro::Intro(SDL_Renderer *renderer, Section *section) {
|
||||
auto *ss = new SmartSprite(texture_, renderer);
|
||||
ss->setWidth(128);
|
||||
ss->setHeight(96);
|
||||
ss->setEnabledCounter(20);
|
||||
ss->setRemainingTime(BITMAP_REMAINING_TIME_S);
|
||||
ss->setDestX(GAMECANVAS_CENTER_X - 64);
|
||||
ss->setDestY(GAMECANVAS_FIRST_QUARTER_Y - 24);
|
||||
bitmaps_.push_back(ss);
|
||||
}
|
||||
|
||||
// bitmap 0: entra des de l'esquerra, accelerant cap a la dreta
|
||||
bitmaps_[0]->setPosX(-128);
|
||||
bitmaps_[0]->setPosY(GAMECANVAS_FIRST_QUARTER_Y - 24);
|
||||
bitmaps_[0]->setVelX(0.0F);
|
||||
bitmaps_[0]->setVelY(0.0F);
|
||||
bitmaps_[0]->setAccelX(0.6F);
|
||||
bitmaps_[0]->setAccelX(2160.0F); // 0.6 px/tick² ⇒ 0.6 * 3600 px/s²
|
||||
bitmaps_[0]->setAccelY(0.0F);
|
||||
bitmaps_[0]->setSpriteClip(0, 0, 128, 96);
|
||||
|
||||
// bitmap 1: entra des de la dreta amb velocitat negativa i accelera més
|
||||
bitmaps_[1]->setPosX(GAMECANVAS_WIDTH);
|
||||
bitmaps_[1]->setPosY(GAMECANVAS_FIRST_QUARTER_Y - 24);
|
||||
bitmaps_[1]->setVelX(-1.0F);
|
||||
bitmaps_[1]->setVelX(-60.0F); // -1 px/tick ⇒ -60 px/s
|
||||
bitmaps_[1]->setVelY(0.0F);
|
||||
bitmaps_[1]->setAccelX(-0.3F);
|
||||
bitmaps_[1]->setAccelX(-1080.0F); // -0.3 px/tick² ⇒ -1080 px/s²
|
||||
bitmaps_[1]->setAccelY(0.0F);
|
||||
bitmaps_[1]->setSpriteClip(128, 0, 128, 96);
|
||||
|
||||
// bitmap 2: cau des de dalt; queda visible més temps (escena "GRITO")
|
||||
bitmaps_[2]->setPosX(GAMECANVAS_CENTER_X - 64);
|
||||
bitmaps_[2]->setPosY(-96);
|
||||
bitmaps_[2]->setVelX(0.0F);
|
||||
bitmaps_[2]->setVelY(3.0F);
|
||||
bitmaps_[2]->setAccelX(0.1F);
|
||||
bitmaps_[2]->setAccelY(0.3F);
|
||||
bitmaps_[2]->setVelY(180.0F); // 3 px/tick ⇒ 180 px/s
|
||||
bitmaps_[2]->setAccelX(360.0F); // 0.1 px/tick² ⇒ 360 px/s²
|
||||
bitmaps_[2]->setAccelY(1080.0F); // 0.3 px/tick² ⇒ 1080 px/s²
|
||||
bitmaps_[2]->setSpriteClip(0, 96, 128, 96);
|
||||
bitmaps_[2]->setEnabledCounter(250);
|
||||
bitmaps_[2]->setRemainingTime(BITMAP_FALLING_REMAINING_TIME_S);
|
||||
|
||||
// bitmap 3: puja lentament des de baix (reflexió)
|
||||
bitmaps_[3]->setPosX(GAMECANVAS_CENTER_X - 64);
|
||||
bitmaps_[3]->setPosY(GAMECANVAS_HEIGHT);
|
||||
bitmaps_[3]->setVelX(0.0F);
|
||||
bitmaps_[3]->setVelY(-0.7F);
|
||||
bitmaps_[3]->setVelY(-42.0F); // -0.7 px/tick ⇒ -42 px/s
|
||||
bitmaps_[3]->setAccelX(0.0F);
|
||||
bitmaps_[3]->setAccelY(0.0F);
|
||||
bitmaps_[3]->setSpriteClip(128, 96, 128, 96);
|
||||
|
||||
// bitmap 4: cau des de dalt (mateix que bitmap 2, sense temps allargat)
|
||||
bitmaps_[4]->setPosX(GAMECANVAS_CENTER_X - 64);
|
||||
bitmaps_[4]->setPosY(-96);
|
||||
bitmaps_[4]->setVelX(0.0F);
|
||||
bitmaps_[4]->setVelY(3.0F);
|
||||
bitmaps_[4]->setAccelX(0.1F);
|
||||
bitmaps_[4]->setAccelY(0.3F);
|
||||
bitmaps_[4]->setVelY(180.0F);
|
||||
bitmaps_[4]->setAccelX(360.0F);
|
||||
bitmaps_[4]->setAccelY(1080.0F);
|
||||
bitmaps_[4]->setSpriteClip(0, 192, 128, 96);
|
||||
|
||||
// bitmap 5: entra des de la dreta lentament
|
||||
bitmaps_[5]->setPosX(GAMECANVAS_WIDTH);
|
||||
bitmaps_[5]->setPosY(GAMECANVAS_FIRST_QUARTER_Y - 24);
|
||||
bitmaps_[5]->setVelX(-0.7F);
|
||||
bitmaps_[5]->setVelX(-42.0F); // -0.7 px/tick ⇒ -42 px/s
|
||||
bitmaps_[5]->setVelY(0.0F);
|
||||
bitmaps_[5]->setAccelX(0.0F);
|
||||
bitmaps_[5]->setAccelY(0.0F);
|
||||
bitmaps_[5]->setSpriteClip(128, 192, 128, 96);
|
||||
|
||||
// Inicializa los textos de la intro
|
||||
// Inicializa los textos de la intro. Time-based: setSecondsPerChar.
|
||||
// Conversió: frames_per_char / 60 = segons_per_char.
|
||||
const int TOTAL_TEXTS = 9;
|
||||
for (int i = 0; i < TOTAL_TEXTS; ++i) {
|
||||
auto *w = new Writer(text_);
|
||||
@@ -103,51 +128,53 @@ Intro::Intro(SDL_Renderer *renderer, Section *section) {
|
||||
w->setPosY(GAMECANVAS_HEIGHT - (BLOCK * 6));
|
||||
w->setKerning(-1);
|
||||
w->setEnabled(false);
|
||||
w->setEnabledCounter(180);
|
||||
w->setRemainingTime(TEXT_REMAINING_TIME_S);
|
||||
texts_.push_back(w);
|
||||
}
|
||||
|
||||
// Un dia qualsevol de l'any 2000
|
||||
texts_[0]->setCaption(Lang::get()->getText(27));
|
||||
texts_[0]->setSpeed(8);
|
||||
texts_[0]->setSecondsPerChar(0.15F); // de 0.1333 (8/60) ⇒ 0.15
|
||||
|
||||
// Tot esta tranquil a la UPV
|
||||
texts_[1]->setCaption(Lang::get()->getText(28));
|
||||
texts_[1]->setSpeed(8);
|
||||
texts_[1]->setSecondsPerChar(0.15F);
|
||||
|
||||
// Fins que un desaprensiu...
|
||||
texts_[2]->setCaption(Lang::get()->getText(29));
|
||||
texts_[2]->setSpeed(12);
|
||||
texts_[2]->setSecondsPerChar(0.2F); // 12/60 ⇒ ja és 0.2
|
||||
|
||||
// HEY! ME ANE A FERME UN CORTAET...
|
||||
texts_[3]->setCaption(Lang::get()->getText(30));
|
||||
texts_[3]->setSpeed(8);
|
||||
texts_[3]->setSecondsPerChar(0.15F);
|
||||
|
||||
// UAAAAAAAAAAAAA!!!
|
||||
texts_[4]->setCaption(Lang::get()->getText(31));
|
||||
texts_[4]->setSpeed(1);
|
||||
texts_[4]->setSecondsPerChar(0.02F); // de 0.0167 (1/60) ⇒ 0.02
|
||||
|
||||
// Espera un moment...
|
||||
texts_[5]->setCaption(Lang::get()->getText(32));
|
||||
texts_[5]->setSpeed(16);
|
||||
texts_[5]->setSecondsPerChar(0.3F); // de 0.2667 (16/60) ⇒ 0.3
|
||||
|
||||
// Si resulta que no tinc solt!
|
||||
texts_[6]->setCaption(Lang::get()->getText(33));
|
||||
texts_[6]->setSpeed(2);
|
||||
texts_[6]->setSecondsPerChar(0.05F); // de 0.0333 (2/60) ⇒ 0.05
|
||||
|
||||
// MERDA DE MAQUINA!
|
||||
texts_[7]->setCaption(Lang::get()->getText(34));
|
||||
texts_[7]->setSpeed(3);
|
||||
texts_[7]->setSecondsPerChar(0.05F); // 3/60 ⇒ ja és 0.05
|
||||
|
||||
// Blop... blop... blop...
|
||||
texts_[8]->setCaption(Lang::get()->getText(35));
|
||||
texts_[8]->setSpeed(16);
|
||||
texts_[8]->setSecondsPerChar(0.3F);
|
||||
|
||||
for (auto *t : texts_) {
|
||||
t->center(GAMECANVAS_CENTER_X);
|
||||
}
|
||||
|
||||
Audio::get()->playMusic(music_, 0);
|
||||
|
||||
DeltaTime::reset();
|
||||
}
|
||||
|
||||
// Destructor
|
||||
@@ -332,26 +359,19 @@ void Intro::updateScene6() {
|
||||
}
|
||||
|
||||
// Actualiza las variables del objeto
|
||||
void Intro::update() {
|
||||
void Intro::update(float dt_s) {
|
||||
Audio::update();
|
||||
checkInput();
|
||||
|
||||
if (SDL_GetTicks() - ticks_ > ticks_speed_) {
|
||||
// Actualiza el contador de ticks
|
||||
ticks_ = SDL_GetTicks();
|
||||
|
||||
// Actualiza los objetos
|
||||
for (auto *bitmap : bitmaps_) {
|
||||
bitmap->update();
|
||||
}
|
||||
|
||||
for (auto *t : texts_) {
|
||||
t->update();
|
||||
}
|
||||
|
||||
// Actualiza las escenas de la intro
|
||||
updateScenes();
|
||||
for (auto *bitmap : bitmaps_) {
|
||||
bitmap->update(dt_s);
|
||||
}
|
||||
|
||||
for (auto *t : texts_) {
|
||||
t->update(dt_s);
|
||||
}
|
||||
|
||||
updateScenes();
|
||||
}
|
||||
|
||||
// Dibuja el objeto en pantalla
|
||||
@@ -386,7 +406,8 @@ void Intro::run() {
|
||||
|
||||
// Ejecuta un frame
|
||||
void Intro::iterate() {
|
||||
update();
|
||||
const float DELTA_TIME_S = DeltaTime::tick();
|
||||
update(DELTA_TIME_S);
|
||||
render();
|
||||
}
|
||||
|
||||
|
||||
@@ -36,15 +36,13 @@ class Intro {
|
||||
Section *section_; // Estado del bucle principal para saber si continua o se sale
|
||||
|
||||
// Variables
|
||||
Uint32 ticks_; // Contador de ticks para ajustar la velocidad del programa
|
||||
Uint8 ticks_speed_; // Velocidad a la que se repiten los bucles del programa
|
||||
Ja::Music *music_; // Musica para la intro
|
||||
int scene_; // Indica que escena está activa
|
||||
int scene_; // Indica que escena está activa
|
||||
|
||||
void update(); // Actualiza las variables del objeto
|
||||
void render(); // Dibuja el objeto en pantalla
|
||||
void checkInput(); // Comprueba las entradas
|
||||
void updateScenes(); // Actualiza las escenas de la intro
|
||||
void update(float dt_s); // Actualiza las variables del objeto (time-based)
|
||||
void render(); // Dibuja el objeto en pantalla
|
||||
void checkInput(); // Comprueba las entradas
|
||||
void updateScenes(); // Actualiza las escenas de la intro
|
||||
|
||||
// Helpers de updateScenes, uno por cada escena
|
||||
void updateScene1();
|
||||
|
||||
+21
-24
@@ -11,12 +11,16 @@
|
||||
#include "core/rendering/screen.h" // for Screen
|
||||
#include "core/rendering/sprite.h" // for Sprite
|
||||
#include "core/resources/resource.h"
|
||||
#include "game/defaults.hpp" // for bgColor, SECTION_PROG_LOGO, SECTION_PROG...
|
||||
#include "utils/utils.h" // for Section, Color
|
||||
#include "core/system/delta_time.hpp" // for DeltaTime::reset / tick
|
||||
#include "game/defaults.hpp" // for bgColor, SECTION_PROG_LOGO, SECTION_PROG...
|
||||
#include "utils/utils.h" // for Section, Color
|
||||
|
||||
// Valores de inicialización y fin
|
||||
constexpr int INIT_FADE = 100;
|
||||
constexpr int END_LOGO = 200;
|
||||
// Durades de l'escena (segons). Time-based: ja no comptem frames. Valors
|
||||
// equivalents al comportament anterior (frame counter a 15ms): 100 i 200
|
||||
// frames ⇒ 1.5s i 3.0s; fi a 220 frames ⇒ 3.3s.
|
||||
constexpr float FADE_START_S = 1.5F;
|
||||
constexpr float FADE_END_S = 3.0F;
|
||||
constexpr float SCENE_END_S = 3.3F;
|
||||
|
||||
// Constructor
|
||||
Logo::Logo(SDL_Renderer *renderer, Section *section) {
|
||||
@@ -30,13 +34,14 @@ Logo::Logo(SDL_Renderer *renderer, Section *section) {
|
||||
sprite_ = new Sprite(14, 75, 226, 44, texture_, renderer);
|
||||
|
||||
// Inicializa variables
|
||||
counter_ = 0;
|
||||
section->name = SECTION_PROG_LOGO;
|
||||
section->subsection = 0;
|
||||
ticks_ = 0;
|
||||
ticks_speed_ = 15;
|
||||
|
||||
Audio::get()->stopMusic();
|
||||
|
||||
// Reset del rellotge: la primera crida a tick() retornarà ~0 i no un
|
||||
// delta gegant arrossegat des del boot o l'escena anterior.
|
||||
DeltaTime::reset();
|
||||
}
|
||||
|
||||
// Destructor
|
||||
@@ -48,7 +53,7 @@ Logo::~Logo() {
|
||||
|
||||
// Comprueba si ha terminado el logo
|
||||
void Logo::checkLogoEnd() {
|
||||
if (counter_ >= END_LOGO + 20) {
|
||||
if (elapsed_time_s_ >= SCENE_END_S) {
|
||||
section_->name = SECTION_PROG_INTRO;
|
||||
section_->subsection = 0;
|
||||
}
|
||||
@@ -68,9 +73,8 @@ void Logo::checkInput() {
|
||||
|
||||
// Dibuja el fade
|
||||
void Logo::renderFade() {
|
||||
// Dibuja el fade
|
||||
if (counter_ >= INIT_FADE) {
|
||||
const float STEP = (float)(counter_ - INIT_FADE) / (float)(END_LOGO - INIT_FADE);
|
||||
if (elapsed_time_s_ >= FADE_START_S) {
|
||||
const float STEP = (elapsed_time_s_ - FADE_START_S) / (FADE_END_S - FADE_START_S);
|
||||
const int ALPHA = std::min((int)(255 * STEP), 255);
|
||||
SDL_SetRenderDrawColor(renderer_, BG_COLOR.r, BG_COLOR.g, BG_COLOR.b, ALPHA);
|
||||
SDL_RenderFillRect(renderer_, nullptr);
|
||||
@@ -78,20 +82,12 @@ void Logo::renderFade() {
|
||||
}
|
||||
|
||||
// Actualiza las variables del objeto
|
||||
void Logo::update() {
|
||||
void Logo::update(float delta_time_s) {
|
||||
Audio::update();
|
||||
checkInput();
|
||||
|
||||
if (SDL_GetTicks() - ticks_ > ticks_speed_) {
|
||||
// Actualiza el contador de ticks
|
||||
ticks_ = SDL_GetTicks();
|
||||
|
||||
// Actualiza el contador
|
||||
counter_++;
|
||||
|
||||
// Comprueba si ha terminado el logo
|
||||
checkLogoEnd();
|
||||
}
|
||||
elapsed_time_s_ += delta_time_s;
|
||||
checkLogoEnd();
|
||||
}
|
||||
|
||||
// Dibuja el objeto en pantalla
|
||||
@@ -123,7 +119,8 @@ void Logo::run() {
|
||||
|
||||
// Ejecuta un frame
|
||||
void Logo::iterate() {
|
||||
update();
|
||||
const float DELTA_TIME_S = DeltaTime::tick();
|
||||
update(DELTA_TIME_S);
|
||||
render();
|
||||
}
|
||||
|
||||
|
||||
@@ -26,14 +26,12 @@ class Logo {
|
||||
Sprite *sprite_; // Sprite con la textura del logo
|
||||
Section *section_; // Estado del bucle principal para saber si continua o se sale
|
||||
|
||||
// Variables
|
||||
Uint32 ticks_; // Contador de ticks para ajustar la velocidad del programa
|
||||
Uint32 ticks_speed_; // Velocidad a la que se repiten los bucles del programa
|
||||
int counter_; // Contador
|
||||
// Temps acumulat de l'escena (segons). Time-based: no comptem frames.
|
||||
float elapsed_time_s_{0.0F};
|
||||
|
||||
void update(); // Actualiza las variables del objeto
|
||||
void render(); // Dibuja el objeto en pantalla
|
||||
void checkLogoEnd(); // Comprueba si ha terminado el logo
|
||||
void checkInput(); // Comprueba las entradas
|
||||
void renderFade(); // Dibuja el fade
|
||||
void update(float delta_time_s); // Actualiza las variables del objeto
|
||||
void render(); // Dibuja el objeto en pantalla
|
||||
void checkLogoEnd(); // Comprueba si ha terminado el logo
|
||||
void checkInput(); // Comprueba las entradas
|
||||
void renderFade(); // Dibuja el fade
|
||||
};
|
||||
|
||||
@@ -2,9 +2,10 @@
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#include <cstdlib> // for rand
|
||||
#include <iostream> // for basic_ostream, operator<<, basic_ostrea...
|
||||
#include <string> // for basic_string, operator+, char_traits
|
||||
#include <algorithm> // for min
|
||||
#include <cstdlib> // for rand
|
||||
#include <iostream> // for basic_ostream, operator<<, basic_ostrea...
|
||||
#include <string> // for basic_string, operator+, char_traits
|
||||
|
||||
#include "core/audio/audio.hpp" // for Audio
|
||||
#include "core/input/global_inputs.hpp" // for GlobalInputs::handle
|
||||
@@ -19,10 +20,11 @@
|
||||
#include "core/rendering/texture.h" // for Texture
|
||||
#include "core/resources/asset.h" // for Asset
|
||||
#include "core/resources/resource.h"
|
||||
#include "game/defaults.hpp" // for GAMECANVAS_CENTER_X, SECTION_PROG_QUIT
|
||||
#include "game/game.h" // for Game
|
||||
#include "game/options.hpp" // for Options
|
||||
#include "game/ui/menu.h" // for Menu
|
||||
#include "core/system/delta_time.hpp" // for DeltaTime::reset / tick
|
||||
#include "game/defaults.hpp" // for GAMECANVAS_CENTER_X, SECTION_PROG_QUIT
|
||||
#include "game/game.h" // for Game
|
||||
#include "game/options.hpp" // for Options
|
||||
#include "game/ui/menu.h" // for Menu
|
||||
|
||||
// Constructor
|
||||
Title::Title(SDL_Renderer *renderer, Section *section) {
|
||||
@@ -70,6 +72,9 @@ Title::Title(SDL_Renderer *renderer, Section *section) {
|
||||
|
||||
// Inicializa los valores
|
||||
init();
|
||||
|
||||
// Reset del rellotge: la primera crida a tick() retornarà ~0.
|
||||
DeltaTime::reset();
|
||||
}
|
||||
|
||||
// Destructor
|
||||
@@ -92,18 +97,19 @@ Title::~Title() {
|
||||
void Title::init() {
|
||||
// Inicializa variables
|
||||
section_->subsection = SUBSECTION_TITLE_1;
|
||||
counter_ = COUNTER;
|
||||
background_counter_ = 0;
|
||||
demo_remaining_s_ = DEMO_TIMEOUT_S;
|
||||
bg_scroll_x_s_ = 0.0F;
|
||||
bg_scroll_y_s_ = 0.0F;
|
||||
bg_phase_s_ = 0.0F;
|
||||
blink_phase_s_ = 0.0F;
|
||||
background_mode_ = rand() % 2;
|
||||
menu_visible_ = false;
|
||||
menu_.active = menu_.title;
|
||||
next_section_.name = SECTION_PROG_GAME;
|
||||
post_fade_ = 0;
|
||||
ticks_ = 0;
|
||||
ticks_speed_ = 15;
|
||||
fade_->init(0x17, 0x17, 0x26);
|
||||
demo_ = true;
|
||||
vibration_step_ = 0;
|
||||
vibration_elapsed_s_ = 0.0F;
|
||||
vibration_initialized_ = false;
|
||||
instructions_active_ = false;
|
||||
demo_game_active_ = false;
|
||||
@@ -148,12 +154,12 @@ void Title::init() {
|
||||
coffee_bitmap_->setWidth(167);
|
||||
coffee_bitmap_->setHeight(46);
|
||||
coffee_bitmap_->setVelX(0.0F);
|
||||
coffee_bitmap_->setVelY(2.5F);
|
||||
coffee_bitmap_->setVelY(150.0F); // 2.5 px/tick ⇒ 150 px/s
|
||||
coffee_bitmap_->setAccelX(0.0F);
|
||||
coffee_bitmap_->setAccelY(0.1F);
|
||||
coffee_bitmap_->setAccelY(360.0F); // 0.1 px/tick² ⇒ 360 px/s²
|
||||
coffee_bitmap_->setSpriteClip(0, 0, 167, 46);
|
||||
coffee_bitmap_->setEnabled(true);
|
||||
coffee_bitmap_->setEnabledCounter(0);
|
||||
coffee_bitmap_->setRemainingTime(0.0F);
|
||||
coffee_bitmap_->setDestX(45);
|
||||
coffee_bitmap_->setDestY(11);
|
||||
|
||||
@@ -164,12 +170,12 @@ void Title::init() {
|
||||
crisis_bitmap_->setWidth(137);
|
||||
crisis_bitmap_->setHeight(46);
|
||||
crisis_bitmap_->setVelX(0.0F);
|
||||
crisis_bitmap_->setVelY(-2.5F);
|
||||
crisis_bitmap_->setVelY(-150.0F); // -2.5 px/tick ⇒ -150 px/s
|
||||
crisis_bitmap_->setAccelX(0.0F);
|
||||
crisis_bitmap_->setAccelY(-0.1F);
|
||||
crisis_bitmap_->setAccelY(-360.0F); // -0.1 px/tick² ⇒ -360 px/s²
|
||||
crisis_bitmap_->setSpriteClip(0, 0, 137, 46);
|
||||
crisis_bitmap_->setEnabled(true);
|
||||
crisis_bitmap_->setEnabledCounter(0);
|
||||
crisis_bitmap_->setRemainingTime(0.0F);
|
||||
crisis_bitmap_->setDestX(60);
|
||||
crisis_bitmap_->setDestY(57);
|
||||
|
||||
@@ -209,25 +215,20 @@ void Title::init() {
|
||||
updateMenuLabels();
|
||||
}
|
||||
|
||||
// Actualiza las variables del objeto
|
||||
void Title::update() {
|
||||
// Actualiza las variables del objeto (time-based)
|
||||
void Title::update(float dt_s) {
|
||||
Audio::update();
|
||||
checkInput();
|
||||
|
||||
if (SDL_GetTicks() - ticks_ <= ticks_speed_) {
|
||||
return;
|
||||
}
|
||||
ticks_ = SDL_GetTicks();
|
||||
|
||||
switch (section_->subsection) {
|
||||
case SUBSECTION_TITLE_1:
|
||||
updateTitle1();
|
||||
updateTitle1(dt_s);
|
||||
break;
|
||||
case SUBSECTION_TITLE_2:
|
||||
updateTitle2();
|
||||
updateTitle2(dt_s);
|
||||
break;
|
||||
case SUBSECTION_TITLE_3:
|
||||
updateTitle3();
|
||||
updateTitle3(dt_s);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -235,9 +236,9 @@ void Title::update() {
|
||||
}
|
||||
|
||||
// Sección 1 - Titulo desplazandose
|
||||
void Title::updateTitle1() {
|
||||
coffee_bitmap_->update();
|
||||
crisis_bitmap_->update();
|
||||
void Title::updateTitle1(float dt_s) {
|
||||
coffee_bitmap_->update(dt_s);
|
||||
crisis_bitmap_->update(dt_s);
|
||||
|
||||
// Si los objetos han llegado a su destino, cambiamos de Sección
|
||||
if (coffee_bitmap_->hasFinished() && crisis_bitmap_->hasFinished()) {
|
||||
@@ -257,7 +258,7 @@ void Title::updateTitle1() {
|
||||
}
|
||||
|
||||
// Sección 2 - Titulo vibrando
|
||||
void Title::updateTitle2() {
|
||||
void Title::updateTitle2(float dt_s) {
|
||||
// Captura las posiciones base la primera vez
|
||||
if (!vibration_initialized_) {
|
||||
vibration_coffee_base_x_ = coffee_bitmap_->getPosX();
|
||||
@@ -265,37 +266,44 @@ void Title::updateTitle2() {
|
||||
vibration_initialized_ = true;
|
||||
}
|
||||
|
||||
// Patró d'offset horitzontal (15 valors, una vegada cadascun cada 3 frames)
|
||||
const int V[] = {-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 0};
|
||||
coffee_bitmap_->setPosX(vibration_coffee_base_x_ + V[vibration_step_ / 3]);
|
||||
crisis_bitmap_->setPosX(vibration_crisis_base_x_ + V[vibration_step_ / 3]);
|
||||
dust_bitmap_right_->update();
|
||||
dust_bitmap_left_->update();
|
||||
constexpr int V_SIZE = static_cast<int>(sizeof(V) / sizeof(V[0]));
|
||||
const int IDX = std::min(static_cast<int>(vibration_elapsed_s_ / VIBRATION_STEP_DURATION_S), V_SIZE - 1);
|
||||
coffee_bitmap_->setPosX(vibration_coffee_base_x_ + V[IDX]);
|
||||
crisis_bitmap_->setPosX(vibration_crisis_base_x_ + V[IDX]);
|
||||
dust_bitmap_right_->update(dt_s);
|
||||
dust_bitmap_left_->update(dt_s);
|
||||
|
||||
vibration_step_++;
|
||||
vibration_elapsed_s_ += dt_s;
|
||||
|
||||
if (vibration_step_ >= 33) {
|
||||
if (vibration_elapsed_s_ >= VIBRATION_DURATION_S) {
|
||||
section_->subsection = SUBSECTION_TITLE_3;
|
||||
vibration_step_ = 0;
|
||||
vibration_elapsed_s_ = 0.0F;
|
||||
vibration_initialized_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Sección 3 - La pantalla de titulo con el menú y la música
|
||||
void Title::updateTitle3() {
|
||||
if (counter_ > 0) {
|
||||
void Title::updateTitle3(float dt_s) {
|
||||
if (demo_remaining_s_ > 0.0F) {
|
||||
if (Audio::getRealMusicState() == Audio::MusicState::STOPPED) {
|
||||
Audio::get()->playMusic(title_music_);
|
||||
}
|
||||
|
||||
dust_bitmap_right_->update();
|
||||
dust_bitmap_left_->update();
|
||||
fade_->update();
|
||||
dust_bitmap_right_->update(dt_s);
|
||||
dust_bitmap_left_->update(dt_s);
|
||||
fade_->update(dt_s);
|
||||
|
||||
if (fade_->hasEnded()) {
|
||||
handlePostFadeAction();
|
||||
}
|
||||
|
||||
updateBG();
|
||||
updateBG(dt_s);
|
||||
blink_phase_s_ += dt_s;
|
||||
if (blink_phase_s_ >= PRESS_ANY_KEY_PERIOD_S) {
|
||||
blink_phase_s_ -= PRESS_ANY_KEY_PERIOD_S;
|
||||
}
|
||||
|
||||
if (menu_visible_ && !fade_->isEnabled()) {
|
||||
menu_.active->update();
|
||||
@@ -312,9 +320,9 @@ void Title::updateTitle3() {
|
||||
}
|
||||
|
||||
if (menu_.active->getName() == "TITLE") {
|
||||
counter_--;
|
||||
demo_remaining_s_ -= dt_s;
|
||||
}
|
||||
} else if (counter_ == 0) {
|
||||
} else {
|
||||
if (demo_) {
|
||||
demo_then_instructions_ = true;
|
||||
runDemoGame();
|
||||
@@ -351,7 +359,7 @@ void Title::handlePostFadeAction() {
|
||||
break;
|
||||
|
||||
case 3: // TIME OUT
|
||||
counter_ = COUNTER;
|
||||
demo_remaining_s_ = DEMO_TIMEOUT_S;
|
||||
menu_.active->reset();
|
||||
if (demo_) {
|
||||
demo_then_instructions_ = true;
|
||||
@@ -602,8 +610,8 @@ void Title::render() {
|
||||
dust_bitmap_right_->render();
|
||||
dust_bitmap_left_->render();
|
||||
|
||||
// PRESS ANY KEY!
|
||||
if ((counter_ % 50 > 14) && (!menu_visible_)) {
|
||||
// PRESS ANY KEY! Blink: visible quan la fase passa l'umbral "off".
|
||||
if ((blink_phase_s_ > PRESS_ANY_KEY_OFF_S) && (!menu_visible_)) {
|
||||
text1_->writeDX(Text::FLAG_CENTER | Text::FLAG_SHADOW, GAMECANVAS_CENTER_X, PLAY_AREA_THIRD_QUARTER_Y + BLOCK, Lang::get()->getText(23), 1, NO_COLOR, 1, SHADOW_COLOR);
|
||||
}
|
||||
|
||||
@@ -626,15 +634,26 @@ void Title::checkInput() {
|
||||
GlobalInputs::handle();
|
||||
}
|
||||
|
||||
// Actualiza el tileado de fondo
|
||||
void Title::updateBG() {
|
||||
if (background_mode_ == 0) { // El tileado de fondo se desplaza en diagonal
|
||||
++background_window_.x %= 64;
|
||||
++background_window_.y %= 64;
|
||||
} else { // El tileado de fondo se desplaza en circulo
|
||||
++background_counter_ %= 360;
|
||||
background_window_.x = 128 + (int(sin_[(background_counter_ + 270) % 360] * 128));
|
||||
background_window_.y = 96 + (int(sin_[(360 - background_counter_) % 360] * 96));
|
||||
// Actualiza el tileado de fondo (time-based).
|
||||
void Title::updateBG(float dt_s) {
|
||||
if (background_mode_ == 0) {
|
||||
// Diagonal: 60 px/s a 60Hz ⇒ un cicle de 64 px cada 64/60 = 1.067 s.
|
||||
// Ancorat a la posició inicial (128, 96): t=0 dona la mateixa vista que
|
||||
// l'estàtic dels Title1/Title2, sense salt visual a l'entrada del Title3.
|
||||
constexpr float SCROLL_PERIOD_S = 64.0F / BG_SCROLL_SPEED_PX_PER_S;
|
||||
bg_scroll_x_s_ += dt_s;
|
||||
bg_scroll_y_s_ += dt_s;
|
||||
if (bg_scroll_x_s_ >= SCROLL_PERIOD_S) { bg_scroll_x_s_ -= SCROLL_PERIOD_S; }
|
||||
if (bg_scroll_y_s_ >= SCROLL_PERIOD_S) { bg_scroll_y_s_ -= SCROLL_PERIOD_S; }
|
||||
background_window_.x = 128 + static_cast<int>(bg_scroll_x_s_ * BG_SCROLL_SPEED_PX_PER_S);
|
||||
background_window_.y = 96 + static_cast<int>(bg_scroll_y_s_ * BG_SCROLL_SPEED_PX_PER_S);
|
||||
} else {
|
||||
// Cercle: 360 graus en BG_CIRCLE_PERIOD_S segons.
|
||||
bg_phase_s_ += dt_s;
|
||||
if (bg_phase_s_ >= BG_CIRCLE_PERIOD_S) { bg_phase_s_ -= BG_CIRCLE_PERIOD_S; }
|
||||
const int ANGLE = static_cast<int>((bg_phase_s_ / BG_CIRCLE_PERIOD_S) * 360.0F) % 360;
|
||||
background_window_.x = 128 + (int(sin_[(ANGLE + 270) % 360] * 128));
|
||||
background_window_.y = 96 + (int(sin_[(360 - ANGLE) % 360] * 96));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -834,9 +853,11 @@ void Title::applyOptions() {
|
||||
|
||||
// Ejecuta un frame
|
||||
void Title::iterate() {
|
||||
const float DELTA_TIME_S = DeltaTime::tick();
|
||||
|
||||
// Si las instrucciones están activas, delega el frame
|
||||
if (instructions_active_) {
|
||||
instructions_->update();
|
||||
instructions_->update(DELTA_TIME_S);
|
||||
instructions_->render();
|
||||
|
||||
if (instructions_->hasFinished()) {
|
||||
@@ -855,6 +876,8 @@ void Title::iterate() {
|
||||
section_->name = SECTION_PROG_TITLE;
|
||||
section_->subsection = SUBSECTION_TITLE_3;
|
||||
}
|
||||
// Reset del rellotge per evitar un dt enorme al tornar al Title.
|
||||
DeltaTime::reset();
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -884,6 +907,8 @@ void Title::iterate() {
|
||||
section_->name = SECTION_PROG_TITLE;
|
||||
section_->subsection = SUBSECTION_TITLE_1;
|
||||
}
|
||||
// Reset del rellotge per evitar un dt enorme al tornar al Title.
|
||||
DeltaTime::reset();
|
||||
} else {
|
||||
// Restaura section para que Director no transicione fuera de Title
|
||||
section_->name = SECTION_PROG_TITLE;
|
||||
@@ -892,7 +917,7 @@ void Title::iterate() {
|
||||
}
|
||||
|
||||
// Ejecución normal del título
|
||||
update();
|
||||
update(DELTA_TIME_S);
|
||||
render();
|
||||
}
|
||||
|
||||
@@ -918,7 +943,7 @@ void Title::handleEvent(const SDL_Event *event) {
|
||||
if (section_->subsection == SUBSECTION_TITLE_3) {
|
||||
if ((event->type == SDL_EVENT_KEY_UP) || (event->type == SDL_EVENT_JOYSTICK_BUTTON_UP)) {
|
||||
menu_visible_ = true;
|
||||
counter_ = COUNTER;
|
||||
demo_remaining_s_ = DEMO_TIMEOUT_S;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+38
-23
@@ -34,7 +34,21 @@ class Title {
|
||||
|
||||
private:
|
||||
static constexpr const char *COPYRIGHT = "@2020 JailDesigner (v2.3.4)";
|
||||
static constexpr int COUNTER = 800;
|
||||
// Time-based: temps màxim a la pantalla del títol abans de tornar al
|
||||
// logo o llançar el demo. 800 frames a 60Hz ⇒ 13.333 s.
|
||||
static constexpr float DEMO_TIMEOUT_S = 13.333F;
|
||||
// Període i fracció "off" del blink del text "PRESS ANY KEY".
|
||||
// Original: counter_ % 50 > 14 ⇒ 50 frames de període, 14 frames off.
|
||||
static constexpr float PRESS_ANY_KEY_PERIOD_S = 50.0F / 60.0F;
|
||||
static constexpr float PRESS_ANY_KEY_OFF_S = 14.0F / 60.0F;
|
||||
// Vibració post-impacte (SUBSECTION_TITLE_2): 33 frames a 60Hz ⇒ 0.55 s,
|
||||
// 11 valors del patró V[] consumits a `step/3` (3 frames per pas).
|
||||
static constexpr float VIBRATION_DURATION_S = 33.0F / 60.0F;
|
||||
static constexpr float VIBRATION_STEP_DURATION_S = 3.0F / 60.0F;
|
||||
// BG mode 0: scroll diagonal 1 px/tick a 60Hz ⇒ 60 px/s.
|
||||
static constexpr float BG_SCROLL_SPEED_PX_PER_S = 60.0F;
|
||||
// BG mode 1: cicle de 360 frames a 60Hz ⇒ 6 s per volta.
|
||||
static constexpr float BG_CIRCLE_PERIOD_S = 6.0F;
|
||||
|
||||
struct MenuData {
|
||||
Menu *title; // Menu de la pantalla de título
|
||||
@@ -72,19 +86,20 @@ class Title {
|
||||
Fade *fade_; // Objeto para realizar fundidos en pantalla
|
||||
|
||||
// Variables
|
||||
Ja::Music *title_music_; // Musica para el titulo
|
||||
Ja::Sound *crash_sound_; // Sonido con el impacto del título
|
||||
int background_counter_; // Temporizador para el fondo de tiles de la pantalla de titulo
|
||||
int counter_; // Temporizador para la pantalla de titulo
|
||||
Uint32 ticks_; // Contador de ticks para ajustar la velocidad del programa
|
||||
Uint8 background_mode_; // Variable para almacenar el tipo de efecto que hará el fondo de la pantalla de titulo
|
||||
float sin_[360]; // Vector con los valores del seno precalculados
|
||||
bool menu_visible_; // Indicador para saber si se muestra el menu del titulo o la frase intermitente
|
||||
bool demo_; // Indica si el modo demo estará activo
|
||||
Section next_section_; // Indica cual es la siguiente sección a cargar cuando termine el contador del titulo
|
||||
Uint32 ticks_speed_; // Velocidad a la que se repiten los bucles del programa
|
||||
Uint8 post_fade_; // Opción a realizar cuando termina el fundido
|
||||
MenuData menu_; // Variable con todos los objetos menus y sus variables
|
||||
Ja::Music *title_music_; // Musica para el titulo
|
||||
Ja::Sound *crash_sound_; // Sonido con el impacto del título
|
||||
float bg_scroll_x_s_{0.0F}; // Acumulador d'scroll horitzontal (segons) per al BG mode 0
|
||||
float bg_scroll_y_s_{0.0F}; // Acumulador d'scroll vertical (segons) per al BG mode 0
|
||||
float bg_phase_s_{0.0F}; // Fase del cicle del BG mode 1 (0..BG_CIRCLE_PERIOD_S)
|
||||
float demo_remaining_s_; // Temps que queda al títol abans de tornar al logo / demo
|
||||
float blink_phase_s_{0.0F}; // Fase del blink de "PRESS ANY KEY"
|
||||
Uint8 background_mode_; // Variable para almacenar el tipo de efecto que hará el fondo de la pantalla de titulo
|
||||
float sin_[360]; // Vector con los valores del seno precalculados
|
||||
bool menu_visible_; // Indicador para saber si se muestra el menu del titulo o la frase intermitente
|
||||
bool demo_; // Indica si el modo demo estará activo
|
||||
Section next_section_; // Indica cual es la siguiente sección a cargar cuando termine el contador del titulo
|
||||
Uint8 post_fade_; // Opción a realizar cuando termina el fundido
|
||||
MenuData menu_; // Variable con todos los objetos menus y sus variables
|
||||
// Snapshot per a permetre CANCEL al menú d'opcions.
|
||||
Options::Video prev_video_;
|
||||
Options::Window prev_window_;
|
||||
@@ -94,10 +109,10 @@ class Title {
|
||||
std::vector<int> device_index_; // Indice para el jugador [i] del vector de dispositivos de entrada disponibles
|
||||
|
||||
// Variables para la vibración del título (SUBSECTION_TITLE_2)
|
||||
int vibration_step_; // Paso actual de la vibración
|
||||
int vibration_coffee_base_x_{0}; // Posición X base del bitmap Coffee
|
||||
int vibration_crisis_base_x_{0}; // Posición X base del bitmap Crisis
|
||||
bool vibration_initialized_; // Indica si se han capturado las posiciones base
|
||||
float vibration_elapsed_s_{0.0F}; // Temps transcorregut des de l'inici de la vibració
|
||||
int vibration_coffee_base_x_{0}; // Posición X base del bitmap Coffee
|
||||
int vibration_crisis_base_x_{0}; // Posición X base del bitmap Crisis
|
||||
bool vibration_initialized_; // Indica si se han capturado las posiciones base
|
||||
|
||||
// Variables para sub-estados delegados (instrucciones y demo)
|
||||
bool instructions_active_; // Indica si las instrucciones están activas
|
||||
@@ -106,20 +121,20 @@ class Title {
|
||||
bool demo_then_instructions_; // Indica si tras la demo hay que mostrar instrucciones
|
||||
|
||||
void init(); // Inicializa los valores
|
||||
void update(); // Actualiza las variables del objeto
|
||||
void update(float dt_s); // Actualiza las variables del objeto (time-based)
|
||||
void render(); // Dibuja el objeto en pantalla
|
||||
static void checkInput(); // Comprueba las entradas (només delega a GlobalInputs)
|
||||
|
||||
// Helpers de update, uno por cada subsección y por cada switch dentro del título 3
|
||||
void updateTitle1();
|
||||
void updateTitle2();
|
||||
void updateTitle3();
|
||||
void updateTitle1(float dt_s);
|
||||
void updateTitle2(float dt_s);
|
||||
void updateTitle3(float dt_s);
|
||||
void handlePostFadeAction();
|
||||
void handleTitleMenuSelection();
|
||||
void handlePlayerSelectMenuSelection();
|
||||
void handleOptionsMenuSelection();
|
||||
|
||||
void updateBG(); // Actualiza el tileado de fondo
|
||||
void updateBG(float dt_s); // Actualiza el tileado de fondo (time-based)
|
||||
static void switchFullScreenModeVar(); // Cambia el valor de la variable de modo de pantalla completa
|
||||
void updateMenuLabels() const; // Actualiza los elementos de los menus
|
||||
void applyOptions(); // Aplica las opciones de menu seleccionadas
|
||||
|
||||
Reference in New Issue
Block a user