628 lines
20 KiB
C++
628 lines
20 KiB
C++
// IWYU pragma: no_include <bits/std_abs.h>
|
|
#include "game/entities/player.hpp"
|
|
|
|
#include <algorithm> // Para max, min
|
|
#include <cmath> // Para ceil, abs
|
|
|
|
#include "core/audio/audio.hpp" // Para Audio
|
|
#include "core/input/input.hpp" // Para Input, InputAction
|
|
#include "core/rendering/sprite/animated_sprite.hpp" // Para AnimatedSprite
|
|
#include "core/resources/resource_cache.hpp" // Para Resource
|
|
#include "game/gameplay/room.hpp" // Para Room
|
|
#include "game/gameplay/tile_collider.hpp" // Para TileCollider
|
|
#include "game/options.hpp" // Para Cheat, Options
|
|
#include "utils/defines.hpp" // Para PlayArea, Collision
|
|
|
|
#ifdef _DEBUG
|
|
#include "core/system/debug.hpp" // Para Debug
|
|
#endif
|
|
|
|
// ============================================================================
|
|
// Constructor
|
|
// ============================================================================
|
|
|
|
Player::Player(const Data& player)
|
|
: room_(player.room) {
|
|
initSprite(player.animations_path);
|
|
setColor();
|
|
applySpawnValues(player.spawn_data);
|
|
placeSprite();
|
|
initSounds();
|
|
previous_state_ = state_;
|
|
}
|
|
|
|
// ============================================================================
|
|
// Render
|
|
// ============================================================================
|
|
|
|
void Player::render() {
|
|
sprite_->render(1, color_);
|
|
}
|
|
|
|
// ============================================================================
|
|
// Update — pipeline principal (6 fases)
|
|
// ============================================================================
|
|
|
|
void Player::update(float delta_time) {
|
|
if (is_paused_) { return; }
|
|
|
|
// 1. Leer input
|
|
handleInput();
|
|
|
|
// 2. Calcular velocidades
|
|
updateVelocity(delta_time);
|
|
if (state_ == State::ON_AIR) {
|
|
applyGravity(delta_time);
|
|
}
|
|
|
|
// 3. Saltar o caer a través de plataformas/slopes
|
|
handleJumpAndDrop();
|
|
|
|
// 4. Aplicar movimiento con colisión
|
|
moveHorizontal(delta_time);
|
|
moveVertical(delta_time);
|
|
|
|
// 5. Detectar caída
|
|
checkFalling();
|
|
|
|
// 6. Kill tiles
|
|
auto [ktc, kox, koy] = getCollisionContext();
|
|
if (ktc.touchesKillTile(x_ + kox, y_ + koy, WIDTH, HEIGHT)) {
|
|
markAsDead();
|
|
}
|
|
|
|
// 7. Finalizar
|
|
syncSpriteAndCollider();
|
|
animate(delta_time);
|
|
border_ = handleBorders();
|
|
|
|
#ifdef _DEBUG
|
|
Debug::get()->set("P.X", std::to_string(static_cast<int>(x_)));
|
|
Debug::get()->set("P.Y", std::to_string(static_cast<int>(y_)));
|
|
Debug::get()->set("P.LGP", std::to_string(last_grounded_position_));
|
|
switch (state_) {
|
|
case State::ON_GROUND:
|
|
Debug::get()->set("P.STATE", "ON_GROUND");
|
|
break;
|
|
case State::ON_SLOPE:
|
|
Debug::get()->set("P.STATE", "ON_SLOPE");
|
|
break;
|
|
case State::ON_AIR:
|
|
Debug::get()->set("P.STATE", "ON_AIR");
|
|
break;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// ============================================================================
|
|
// Fase 1: Input
|
|
// ============================================================================
|
|
|
|
void Player::handleInput() {
|
|
if (ignore_input_) { return; }
|
|
if (Input::get()->checkAction(InputAction::LEFT)) {
|
|
wanna_go_ = Direction::LEFT;
|
|
} else if (Input::get()->checkAction(InputAction::RIGHT)) {
|
|
wanna_go_ = Direction::RIGHT;
|
|
} else {
|
|
wanna_go_ = Direction::NONE;
|
|
}
|
|
|
|
const bool JUMP_PRESSED = Input::get()->checkAction(InputAction::JUMP);
|
|
wanna_jump_ = JUMP_PRESSED && !jump_held_;
|
|
jump_held_ = JUMP_PRESSED;
|
|
|
|
const bool DOWN_PRESSED = Input::get()->checkAction(InputAction::DOWN);
|
|
wanna_down_ = DOWN_PRESSED && !down_held_;
|
|
down_held_ = DOWN_PRESSED;
|
|
}
|
|
|
|
// ============================================================================
|
|
// Fase 2: Velocidades
|
|
// ============================================================================
|
|
|
|
void Player::updateVelocity(float delta_time) {
|
|
float target = 0.0F;
|
|
switch (wanna_go_) {
|
|
case Direction::LEFT:
|
|
target = -HORIZONTAL_VELOCITY;
|
|
break;
|
|
case Direction::RIGHT:
|
|
target = HORIZONTAL_VELOCITY;
|
|
break;
|
|
default:
|
|
target = 0.0F;
|
|
break;
|
|
}
|
|
|
|
// Orientación del sprite
|
|
if (target > 0.0F) {
|
|
sprite_->setFlip(Flip::RIGHT);
|
|
} else if (target < 0.0F) {
|
|
sprite_->setFlip(Flip::LEFT);
|
|
}
|
|
|
|
// Inercia: aire = gradual ambas direcciones, suelo = instantáneo arranque, gradual frenada
|
|
const float STEP = HORIZONTAL_ACCEL * delta_time;
|
|
if (state_ == State::ON_AIR) {
|
|
if (vx_ < target) {
|
|
vx_ = std::min(vx_ + STEP, target);
|
|
} else if (vx_ > target) {
|
|
vx_ = std::max(vx_ - STEP, target);
|
|
}
|
|
} else {
|
|
if (target != 0.0F) {
|
|
vx_ = target;
|
|
} else if (vx_ > 0.0F) {
|
|
vx_ = std::max(vx_ - STEP, 0.0F);
|
|
} else if (vx_ < 0.0F) {
|
|
vx_ = std::min(vx_ + STEP, 0.0F);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Player::applyGravity(float delta_time) {
|
|
const float GRAVITY = (vy_ < 0.0F && !jump_held_)
|
|
? GRAVITY_FORCE * LOW_JUMP_GRAVITY_MULT
|
|
: GRAVITY_FORCE;
|
|
vy_ += GRAVITY * delta_time;
|
|
vy_ = std::min(vy_, MAX_VY);
|
|
}
|
|
|
|
// ============================================================================
|
|
// Fase 3: Saltar y drop-through
|
|
// ============================================================================
|
|
|
|
void Player::handleJumpAndDrop() {
|
|
if (state_ == State::ON_AIR) { return; }
|
|
|
|
if (wanna_jump_) {
|
|
startJump();
|
|
return;
|
|
}
|
|
|
|
// Drop-through: plataforma passable
|
|
if (wanna_down_ && state_ == State::ON_GROUND) {
|
|
auto [tc, ox, oy] = getCollisionContext();
|
|
float foot_y = (y_ + oy) + HEIGHT;
|
|
int foot_row = static_cast<int>(foot_y) / Tile::SIZE;
|
|
int left_col = static_cast<int>(x_ + ox) / Tile::SIZE;
|
|
int right_col = static_cast<int>((x_ + ox) + WIDTH - 1) / Tile::SIZE;
|
|
|
|
for (int col = left_col; col <= right_col; ++col) {
|
|
if (tc.getTileAt(col, foot_row) == TileCollider::Tile::PASSABLE) {
|
|
y_ += 1.0F;
|
|
vy_ = 0.0F;
|
|
transitionToState(State::ON_AIR);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Player::startJump() {
|
|
vy_ = JUMP_VELOCITY;
|
|
last_grounded_position_ = y_;
|
|
Audio::get()->playSound(jump_sound_, Audio::Group::GAME);
|
|
transitionToState(State::ON_AIR);
|
|
}
|
|
|
|
// ============================================================================
|
|
// Fase 4a: Movimiento horizontal
|
|
// ============================================================================
|
|
|
|
void Player::moveHorizontal(float delta_time) {
|
|
if (vx_ == 0.0F) {
|
|
// Aunque no haya movimiento horizontal, si estamos en slope hay que seguirla
|
|
// (por si la gravedad nos ha movido o algo)
|
|
return;
|
|
}
|
|
|
|
auto [tc, ox, oy] = getCollisionContext();
|
|
float new_x = x_ + (vx_ * delta_time);
|
|
|
|
// Colisión con paredes
|
|
if (vx_ < 0.0F) {
|
|
float wall = tc.checkWallLeft(new_x + ox, y_ + oy, WIDTH, HEIGHT);
|
|
if (wall != Collision::NONE) {
|
|
new_x = wall - ox;
|
|
}
|
|
} else {
|
|
float wall = tc.checkWallRight(new_x + ox, y_ + oy, WIDTH, HEIGHT);
|
|
if (wall != Collision::NONE) {
|
|
new_x = wall - WIDTH - ox;
|
|
}
|
|
}
|
|
|
|
x_ = new_x;
|
|
|
|
// Si estamos en una slope, ajustar Y para seguirla
|
|
if (state_ == State::ON_SLOPE) {
|
|
followSlope();
|
|
}
|
|
|
|
// Si estamos en suelo plano, detectar entrada a slope
|
|
if (state_ == State::ON_GROUND) {
|
|
detectSlopeEntry();
|
|
}
|
|
}
|
|
|
|
// Ajusta Y del jugador para seguir la superficie de la slope mientras camina.
|
|
// Si el pie sale del tile actual, busca el siguiente tile de slope en la fila
|
|
// actual y la inferior (las slopes en escalera bajan una fila por tile).
|
|
// Si no encuentra slope, llama a exitSlope().
|
|
void Player::followSlope() {
|
|
auto [tc, ox, oy] = getCollisionContext();
|
|
|
|
// SLOPE_L (\): pie izquierdo. SLOPE_R (/): pie derecho.
|
|
float foot_x = (slope_type_ == TileCollider::Tile::SLOPE_L) ? (x_ + ox) : (x_ + ox) + WIDTH - 1;
|
|
|
|
// Calcular Y en la slope actual
|
|
float surface_y = tc.getSlopeY(slope_tile_x_, slope_tile_y_, foot_x);
|
|
y_ = surface_y - HEIGHT - oy;
|
|
|
|
// Comprobar si hemos salido del tile actual
|
|
int foot_tile_x = static_cast<int>(foot_x) / Tile::SIZE;
|
|
int foot_tile_y = static_cast<int>((y_ + oy) + HEIGHT) / Tile::SIZE;
|
|
|
|
if (foot_tile_x != slope_tile_x_ || foot_tile_y != slope_tile_y_) {
|
|
// Buscar slope en el tile calculado y en el de abajo (la escalera de slopes
|
|
// siempre tiene el siguiente tile una fila arriba o abajo)
|
|
for (int row = foot_tile_y; row <= foot_tile_y + 1; ++row) {
|
|
auto new_tile = tc.getTileAt(foot_tile_x, row);
|
|
if (new_tile == TileCollider::Tile::SLOPE_L || new_tile == TileCollider::Tile::SLOPE_R) {
|
|
slope_tile_x_ = foot_tile_x;
|
|
slope_tile_y_ = row;
|
|
slope_type_ = new_tile;
|
|
surface_y = tc.getSlopeY(slope_tile_x_, slope_tile_y_, foot_x);
|
|
y_ = surface_y - HEIGHT - oy;
|
|
return;
|
|
}
|
|
}
|
|
exitSlope();
|
|
}
|
|
}
|
|
|
|
// El jugador ha salido del tile de slope sin encontrar otra slope adyacente.
|
|
// Comprueba si hay suelo debajo (foot_y y foot_y+1 para cubrir el boundary exacto
|
|
// entre filas cuando se sale por el extremo inferior de la slope).
|
|
// Si hay suelo, snapea al borde del tile. Si no, empieza a caer.
|
|
void Player::exitSlope() {
|
|
auto [tc, ox, oy] = getCollisionContext();
|
|
float foot_y = (y_ + oy) + HEIGHT;
|
|
|
|
// Comprobar suelo en la fila actual y la siguiente (al salir por abajo de una slope,
|
|
// los pies pueden estar en el último pixel de la fila, justo antes del suelo)
|
|
for (int check = 0; check <= 1; ++check) {
|
|
float check_y = foot_y + check;
|
|
if (tc.hasGroundBelow(x_ + ox, check_y, WIDTH)) {
|
|
int row = static_cast<int>(check_y) / Tile::SIZE;
|
|
y_ = static_cast<float>(row * Tile::SIZE) - HEIGHT - oy;
|
|
transitionToState(State::ON_GROUND);
|
|
return;
|
|
}
|
|
}
|
|
|
|
vy_ = 0.0F;
|
|
transitionToState(State::ON_AIR);
|
|
}
|
|
|
|
// Detecta si el jugador ha pisado una slope caminando desde suelo plano.
|
|
// Las slopes en escalera están una fila arriba del suelo, así que checkSlopeBelow
|
|
// también mira la fila superior.
|
|
void Player::detectSlopeEntry() {
|
|
auto [tc, ox, oy] = getCollisionContext();
|
|
float foot_y = (y_ + oy) + HEIGHT;
|
|
|
|
auto slope = tc.checkSlopeBelow(x_ + ox, foot_y, WIDTH);
|
|
if (slope.on_slope) {
|
|
y_ = slope.surface_y - HEIGHT - oy;
|
|
slope_tile_x_ = slope.tile_x;
|
|
slope_tile_y_ = slope.tile_y;
|
|
slope_type_ = slope.type;
|
|
transitionToState(State::ON_SLOPE);
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// Fase 4b: Movimiento vertical
|
|
// ============================================================================
|
|
|
|
void Player::moveVertical(float delta_time) {
|
|
if (state_ != State::ON_AIR) { return; }
|
|
|
|
auto [tc, ox, oy] = getCollisionContext();
|
|
float displacement = vy_ * delta_time;
|
|
|
|
if (vy_ < 0.0F) {
|
|
// Subiendo: comprobar techo
|
|
float new_y = y_ + displacement;
|
|
float ceiling = tc.checkCeiling(x_ + ox, new_y + oy, WIDTH);
|
|
if (ceiling != Collision::NONE) {
|
|
y_ = ceiling - oy;
|
|
vy_ = 0.0F;
|
|
} else {
|
|
y_ = new_y;
|
|
}
|
|
} else if (vy_ > 0.0F) {
|
|
// Bajando: comprobar suelo
|
|
float foot_y = (y_ + oy) + HEIGHT;
|
|
float new_foot_y = foot_y + displacement;
|
|
auto hit = tc.checkFloor(x_ + ox, foot_y, WIDTH, new_foot_y);
|
|
|
|
if (hit.y != Collision::NONE) {
|
|
y_ = hit.y - HEIGHT - oy;
|
|
if (hit.type == TileCollider::Tile::SLOPE_L || hit.type == TileCollider::Tile::SLOPE_R) {
|
|
slope_tile_x_ = hit.tile_x;
|
|
slope_tile_y_ = hit.tile_y;
|
|
slope_type_ = hit.type;
|
|
transitionToState(State::ON_SLOPE);
|
|
} else {
|
|
transitionToState(State::ON_GROUND);
|
|
}
|
|
} else {
|
|
y_ += displacement;
|
|
#ifdef _DEBUG
|
|
if (y_ > PlayArea::BOTTOM + 100) { y_ = PlayArea::TOP + 2; }
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// Fase 5: Detección de caída
|
|
// ============================================================================
|
|
|
|
void Player::checkFalling() {
|
|
if (state_ == State::ON_AIR) { return; }
|
|
|
|
auto [tc, ox, oy] = getCollisionContext();
|
|
|
|
if (state_ == State::ON_SLOPE) {
|
|
// Verificar que el tile de slope sigue existiendo
|
|
auto tile = tc.getTileAt(slope_tile_x_, slope_tile_y_);
|
|
if (tile != TileCollider::Tile::SLOPE_L && tile != TileCollider::Tile::SLOPE_R) {
|
|
vy_ = 0.0F;
|
|
transitionToState(State::ON_AIR);
|
|
}
|
|
return;
|
|
}
|
|
|
|
// ON_GROUND: comprobar si sigue habiendo suelo
|
|
float foot_y = (y_ + oy) + HEIGHT;
|
|
if (!tc.hasGroundBelow(x_ + ox, foot_y, WIDTH)) {
|
|
// Sticking: si no hay suelo pero hay slope debajo, snapear a ella
|
|
// para transición suave suelo→slope (bajada de rampas sin caer)
|
|
auto slope = tc.checkSlopeBelow(x_ + ox, foot_y, WIDTH);
|
|
if (slope.on_slope) {
|
|
y_ = slope.surface_y - HEIGHT - oy;
|
|
slope_tile_x_ = slope.tile_x;
|
|
slope_tile_y_ = slope.tile_y;
|
|
slope_type_ = slope.type;
|
|
transitionToState(State::ON_SLOPE);
|
|
return;
|
|
}
|
|
vy_ = 0.0F;
|
|
transitionToState(State::ON_AIR);
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// Gestión de estado
|
|
// ============================================================================
|
|
|
|
void Player::transitionToState(State state) {
|
|
previous_state_ = state_;
|
|
state_ = state;
|
|
|
|
switch (state) {
|
|
case State::ON_GROUND:
|
|
vy_ = 0;
|
|
if (previous_state_ == State::ON_AIR) {
|
|
Audio::get()->playSound(land_sound_, Audio::Group::GAME);
|
|
}
|
|
break;
|
|
case State::ON_SLOPE:
|
|
vy_ = 0;
|
|
if (previous_state_ == State::ON_AIR) {
|
|
Audio::get()->playSound(land_sound_, Audio::Group::GAME);
|
|
}
|
|
break;
|
|
case State::ON_AIR:
|
|
last_grounded_position_ = static_cast<int>(y_);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// Bordes de pantalla
|
|
// ============================================================================
|
|
|
|
auto Player::handleBorders() const -> Room::Border {
|
|
const float CENTER_X = x_ + (WIDTH / 2.0F);
|
|
const float CENTER_Y = y_ + (HEIGHT / 2.0F);
|
|
|
|
if (CENTER_X < PlayArea::LEFT) { return Room::Border::LEFT; }
|
|
if (CENTER_X > PlayArea::RIGHT) { return Room::Border::RIGHT; }
|
|
if (CENTER_Y < PlayArea::TOP) { return Room::Border::TOP; }
|
|
if (CENTER_Y > PlayArea::BOTTOM) { return Room::Border::BOTTOM; }
|
|
return Room::Border::NONE;
|
|
}
|
|
|
|
void Player::setAdjacentRoom(std::shared_ptr<Room> room, Room::Border direction) {
|
|
adjacent_room_ = std::move(room);
|
|
adjacent_direction_ = direction;
|
|
}
|
|
|
|
void Player::clearAdjacentRoom() {
|
|
adjacent_room_.reset();
|
|
adjacent_direction_ = Room::Border::NONE;
|
|
}
|
|
|
|
auto Player::getCollisionContext() const -> CollisionContext {
|
|
if (!adjacent_room_) {
|
|
return {room_->getTileCollider(), 0.0F, 0.0F};
|
|
}
|
|
|
|
const float CENTER_X = x_ + (WIDTH / 2.0F);
|
|
const float CENTER_Y = y_ + (HEIGHT / 2.0F);
|
|
|
|
switch (adjacent_direction_) {
|
|
case Room::Border::TOP:
|
|
if (CENTER_Y < PlayArea::TOP) {
|
|
return {adjacent_room_->getTileCollider(), 0.0F, static_cast<float>(PlayArea::HEIGHT)};
|
|
}
|
|
break;
|
|
case Room::Border::BOTTOM:
|
|
if (CENTER_Y > PlayArea::BOTTOM) {
|
|
return {adjacent_room_->getTileCollider(), 0.0F, -static_cast<float>(PlayArea::HEIGHT)};
|
|
}
|
|
break;
|
|
case Room::Border::LEFT:
|
|
if (CENTER_X < PlayArea::LEFT) {
|
|
return {adjacent_room_->getTileCollider(), static_cast<float>(PlayArea::WIDTH), 0.0F};
|
|
}
|
|
break;
|
|
case Room::Border::RIGHT:
|
|
if (CENTER_X > PlayArea::RIGHT) {
|
|
return {adjacent_room_->getTileCollider(), -static_cast<float>(PlayArea::WIDTH), 0.0F};
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return {room_->getTileCollider(), 0.0F, 0.0F};
|
|
}
|
|
|
|
void Player::switchBorders() {
|
|
switch (border_) {
|
|
case Room::Border::TOP:
|
|
y_ += PlayArea::HEIGHT;
|
|
last_grounded_position_ = static_cast<int>(y_);
|
|
break;
|
|
case Room::Border::BOTTOM:
|
|
y_ -= PlayArea::HEIGHT;
|
|
last_grounded_position_ = static_cast<int>(y_);
|
|
break;
|
|
case Room::Border::RIGHT:
|
|
x_ -= PlayArea::WIDTH;
|
|
break;
|
|
case Room::Border::LEFT:
|
|
x_ += PlayArea::WIDTH;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
border_ = Room::Border::NONE;
|
|
syncSpriteAndCollider();
|
|
}
|
|
|
|
// ============================================================================
|
|
// Geometría y renderizado
|
|
// ============================================================================
|
|
|
|
void Player::syncSpriteAndCollider() {
|
|
placeSprite();
|
|
collider_box_ = getRect();
|
|
}
|
|
|
|
void Player::placeSprite() {
|
|
sprite_->setPos(x_, y_);
|
|
}
|
|
|
|
void Player::animate(float delta_time) { // NOLINT(readability-make-member-function-const)
|
|
if (state_ == State::ON_AIR) {
|
|
sprite_->setCurrentAnimation("jump");
|
|
} else if (vx_ != 0) {
|
|
sprite_->setCurrentAnimation("default");
|
|
sprite_->update(delta_time);
|
|
} else {
|
|
sprite_->setCurrentAnimation("stand");
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// Color y skin
|
|
// ============================================================================
|
|
|
|
void Player::setColor(Uint8 color) {
|
|
if (color != 0) {
|
|
color_ = color;
|
|
return;
|
|
}
|
|
if (Options::game.player_color >= 0) {
|
|
color_ = static_cast<Uint8>(Options::game.player_color);
|
|
} else {
|
|
color_ = 14;
|
|
}
|
|
if (room_ != nullptr && color_ == room_->getBGColor()) {
|
|
color_ = (room_->getBGColor() != 14) ? 14 : 1;
|
|
}
|
|
}
|
|
|
|
auto Player::skinToAnimationPath(const std::string& skin_name) -> std::string {
|
|
if (skin_name == "default") { return "player.yaml"; }
|
|
return skin_name + ".yaml";
|
|
}
|
|
|
|
void Player::setSkin(const std::string& skin_name) {
|
|
const auto FLIP = sprite_->getFlip();
|
|
initSprite(skinToAnimationPath(skin_name));
|
|
sprite_->setFlip(FLIP);
|
|
}
|
|
|
|
// ============================================================================
|
|
// Inicialización
|
|
// ============================================================================
|
|
|
|
void Player::initSprite(const std::string& animations_path) { // NOLINT(readability-convert-member-functions-to-static)
|
|
const auto& animation_data = Resource::Cache::get()->getAnimationData(animations_path);
|
|
sprite_ = std::make_unique<AnimatedSprite>(animation_data);
|
|
sprite_->setWidth(WIDTH);
|
|
sprite_->setHeight(HEIGHT);
|
|
sprite_->setCurrentAnimation("default");
|
|
}
|
|
|
|
void Player::initSounds() { // NOLINT(readability-convert-member-functions-to-static)
|
|
jump_sound_ = Resource::Cache::get()->getSound("jump.wav");
|
|
land_sound_ = Resource::Cache::get()->getSound("land.wav");
|
|
}
|
|
|
|
void Player::applySpawnValues(const SpawnData& spawn) {
|
|
x_ = spawn.x;
|
|
y_ = spawn.y;
|
|
vx_ = spawn.vx;
|
|
vy_ = spawn.vy;
|
|
last_grounded_position_ = spawn.last_grounded_position;
|
|
state_ = spawn.state;
|
|
sprite_->setFlip(spawn.flip);
|
|
}
|
|
|
|
// ============================================================================
|
|
// Utilidades
|
|
// ============================================================================
|
|
|
|
void Player::markAsDead() {
|
|
is_alive_ = (Options::cheats.invincible == Options::Cheat::State::ENABLED);
|
|
}
|
|
|
|
#ifdef _DEBUG
|
|
void Player::setDebugPosition(float x, float y) {
|
|
x_ = x;
|
|
y_ = y;
|
|
syncSpriteAndCollider();
|
|
}
|
|
|
|
void Player::finalizeDebugTeleport() {
|
|
vx_ = 0.0F;
|
|
vy_ = 0.0F;
|
|
last_grounded_position_ = static_cast<int>(y_);
|
|
slope_tile_x_ = 0;
|
|
slope_tile_y_ = 0;
|
|
slope_type_ = TileCollider::Tile::EMPTY;
|
|
transitionToState(State::ON_GROUND);
|
|
syncSpriteAndCollider();
|
|
}
|
|
#endif
|