Files
coffee_crisis_arcade_edition/source/item.cpp

225 lines
7.6 KiB
C++

#include "item.h"
#include <algorithm> // Para clamp
#include <cmath> // Para fmod
#include <cstdlib> // Para rand
#include "animated_sprite.h" // Para AnimatedSprite
#include "param.h" // Para Param, ParamGame, param
class Texture; // lines 6-6
Item::Item(ItemType type, float x, float y, SDL_FRect &play_area, const std::shared_ptr<Texture> &texture, const std::vector<std::string> &animation)
: sprite_(std::make_unique<AnimatedSprite>(texture, animation)),
play_area_(play_area),
type_(type) {
switch (type) {
case ItemType::COFFEE_MACHINE: {
width_ = COFFEE_MACHINE_WIDTH;
height_ = COFFEE_MACHINE_HEIGHT;
pos_x_ = getCoffeeMachineSpawn(x, width_, play_area_.w);
pos_y_ = y;
vel_x_ = ((rand() % 3) - 1) * 0.5F;
vel_y_ = -0.1F;
accel_y_ = 0.1F;
collider_.r = 10;
break;
}
default: {
width_ = param.game.item_size;
height_ = param.game.item_size;
pos_x_ = x;
pos_y_ = y;
// 6 velocidades: 3 negativas (-1.0, -0.66, -0.33) y 3 positivas (0.33, 0.66, 1.0)
const int direction = rand() % 6;
if (direction < 3) {
// Velocidades negativas: -1.0, -0.66, -0.33
vel_x_ = -1.0F + (direction * 0.33F);
} else {
// Velocidades positivas: 0.33, 0.66, 1.0
vel_x_ = 0.33F + ((direction - 3) * 0.33F);
}
vel_y_ = -4.0F;
accel_y_ = 0.2F;
collider_.r = width_ / 2;
break;
}
}
// Actualiza el sprite
shiftSprite();
shiftColliders();
}
void Item::alignTo(int x) {
const float MIN_X = param.game.play_area.rect.x + 1;
const float MAX_X = play_area_.w - width_ - 1;
pos_x_ = x - (width_ / 2);
// Ajusta para que no quede fuera de la zona de juego
pos_x_ = std::clamp(pos_x_, MIN_X, MAX_X);
// Actualiza el sprite
shiftSprite();
shiftColliders();
}
void Item::render() {
if (enabled_) {
// Muestra normalmente hasta los últimos ~3.3 segundos (200 frames)
constexpr float BLINK_START_MS = LIFETIME_DURATION_MS - (200.0f * (1000.0f / 60.0f));
if (lifetime_timer_ < BLINK_START_MS) {
sprite_->render();
} else {
// Efecto de parpadeo en los últimos segundos (cada ~333ms o 20 frames)
constexpr float BLINK_INTERVAL_MS = 20.0f * (1000.0f / 60.0f);
const float phase = fmod(lifetime_timer_, BLINK_INTERVAL_MS);
const float half_interval = BLINK_INTERVAL_MS / 2.0f;
if (phase < half_interval) {
sprite_->render();
}
}
}
}
void Item::move(float deltaTime) {
// Convertir deltaTime a factor de frame para compatibilidad (asumiendo 60fps)
const float frameFactor = deltaTime / (1000.0f / 60.0f);
floor_collision_ = false;
// Calcula la nueva posición (time-based)
pos_x_ += vel_x_ * frameFactor;
pos_y_ += vel_y_ * frameFactor;
// Aplica las aceleraciones a la velocidad (time-based)
vel_x_ += accel_x_ * frameFactor;
vel_y_ += accel_y_ * frameFactor;
// Comprueba los laterales de la zona de juego
const float MIN_X = param.game.play_area.rect.x;
const float MAX_X = play_area_.w - width_;
pos_x_ = std::clamp(pos_x_, MIN_X, MAX_X);
// Si toca el borde lateral
if (pos_x_ == MIN_X || pos_x_ == MAX_X) {
vel_x_ = -vel_x_; // Invierte la velocidad horizontal
}
// Si colisiona por arriba, rebota (excepto la máquina de café)
if ((pos_y_ < param.game.play_area.rect.y) && !(type_ == ItemType::COFFEE_MACHINE)) {
// Corrige
pos_y_ = param.game.play_area.rect.y;
// Fuerza la velocidad hacia abajo para evitar oscilaciones
vel_y_ = std::abs(vel_y_);
}
// Si colisiona con la parte inferior
if (pos_y_ > play_area_.h - height_) {
// Corrige la posición
pos_y_ = play_area_.h - height_;
switch (type_) {
case ItemType::COFFEE_MACHINE:
// La máquina de café es mas pesada y tiene una fisica diferente, ademas hace ruido
floor_collision_ = true;
if (vel_y_ < 1.0F) {
// Si la velocidad vertical es baja, detiene el objeto
vel_y_ = vel_x_ = accel_x_ = accel_y_ = 0;
} else {
// Si la velocidad vertical es alta, el objeto rebota y pierde velocidad
vel_y_ *= -0.20F;
vel_x_ *= 0.75F;
}
break;
default:
// Si no es una máquina de café
if (vel_y_ < 1.0F) {
// Si la velocidad vertical es baja, detiene el objeto
vel_y_ = vel_x_ = accel_x_ = accel_y_ = 0;
} else {
// Si la velocidad vertical es alta, el objeto rebota y pierde velocidad
vel_y_ *= -0.5F;
vel_x_ *= 0.75F;
}
break;
}
}
// Actualiza la posición del sprite
shiftSprite();
shiftColliders();
}
void Item::disable() { enabled_ = false; }
void Item::update(float deltaTime) {
move(deltaTime);
sprite_->update(deltaTime);
updateTimeToLive(deltaTime);
}
void Item::updateTimeToLive(float deltaTime) {
lifetime_timer_ += deltaTime;
if (lifetime_timer_ >= LIFETIME_DURATION_MS) {
disable();
}
}
void Item::shiftColliders() {
collider_.x = static_cast<int>(pos_x_ + (width_ / 2));
collider_.y = static_cast<int>(pos_y_ + (height_ / 2));
}
void Item::shiftSprite() {
sprite_->setPosX(pos_x_);
sprite_->setPosY(pos_y_);
}
// Calcula la zona de aparición de la máquina de café
auto Item::getCoffeeMachineSpawn(int player_x, int item_width, int area_width, int margin) -> int {
// Distancia mínima del jugador (ajusta según necesites)
const int MIN_DISTANCE_FROM_PLAYER = area_width / 2;
const int LEFT_BOUND = margin;
const int RIGHT_BOUND = area_width - item_width - margin;
// Calcular zona de exclusión alrededor del jugador
int exclude_left = player_x - MIN_DISTANCE_FROM_PLAYER;
int exclude_right = player_x + MIN_DISTANCE_FROM_PLAYER;
// Verificar si hay espacio suficiente a la izquierda
bool can_spawn_left = (exclude_left > LEFT_BOUND) && (exclude_left - LEFT_BOUND > item_width);
// Verificar si hay espacio suficiente a la derecha
bool can_spawn_right = (exclude_right < RIGHT_BOUND) && (RIGHT_BOUND - exclude_right > item_width);
if (can_spawn_left && can_spawn_right) {
// Ambos lados disponibles, elegir aleatoriamente
if (rand() % 2 == 0) {
// Lado izquierdo
return (rand() % (exclude_left - LEFT_BOUND)) + LEFT_BOUND;
} // Lado derecho
return (rand() % (RIGHT_BOUND - exclude_right)) + exclude_right;
}
if (can_spawn_left) {
// Solo lado izquierdo disponible
return (rand() % (exclude_left - LEFT_BOUND)) + LEFT_BOUND;
}
if (can_spawn_right) {
// Solo lado derecho disponible
return (rand() % (RIGHT_BOUND - exclude_right)) + exclude_right;
} // No hay espacio suficiente lejos del jugador
// Por ahora, intentar spawn en el extremo más lejano posible
int distance_to_left = abs(player_x - LEFT_BOUND);
int distance_to_right = abs(RIGHT_BOUND - player_x);
if (distance_to_left > distance_to_right) {
return LEFT_BOUND;
}
return RIGHT_BOUND - item_width;
}