forked from jaildesigner-jailgames/jaildoctors_dilemma
682 lines
16 KiB
C++
682 lines
16 KiB
C++
#include "player.h"
|
|
#include <fstream>
|
|
#include <sstream>
|
|
|
|
// Constructor
|
|
Player::Player(player_t ini, std::string tileset, std::string animation, SDL_Renderer *renderer, Asset *asset, Input *input, Room *room, Debug *debug)
|
|
{
|
|
// Obten punteros a objetos
|
|
this->asset = asset;
|
|
this->renderer = renderer;
|
|
this->input = input;
|
|
this->room = room;
|
|
this->debug = debug;
|
|
|
|
// Crea objetos
|
|
texture = new LTexture(renderer, asset->get(tileset));
|
|
sprite = new AnimatedSprite(texture, renderer, animation);
|
|
|
|
// Inicializa variables
|
|
color = stringToColor("white");
|
|
onBorder = false;
|
|
border = BORDER_TOP;
|
|
invincible = false;
|
|
|
|
jump_ini = ini.jump_ini;
|
|
state = ini.state;
|
|
prevState = state;
|
|
|
|
x = ini.x;
|
|
y = ini.y;
|
|
vx = ini.vx;
|
|
vy = ini.vy;
|
|
w = 8;
|
|
h = 16;
|
|
maxVY = 1.2f;
|
|
|
|
sprite->setPosX(ini.x);
|
|
sprite->setPosY(ini.y);
|
|
sprite->setWidth(8);
|
|
sprite->setHeight(16);
|
|
|
|
sprite->setFlip(ini.flip);
|
|
sprite->setCurrentAnimation("walk");
|
|
sprite->animate();
|
|
|
|
lastPosition = getRect();
|
|
colliderBox = getRect();
|
|
const SDL_Point p = {0, 0};
|
|
colliderPoints.insert(colliderPoints.end(), {p, p, p, p, p, p, p, p});
|
|
underFeet.insert(underFeet.end(), {p, p});
|
|
feet.insert(feet.end(), {p, p});
|
|
}
|
|
|
|
// Destructor
|
|
Player::~Player()
|
|
{
|
|
delete texture;
|
|
delete sprite;
|
|
}
|
|
|
|
// Pinta el jugador en pantalla
|
|
void Player::render()
|
|
{
|
|
sprite->getTexture()->setColor(color.r, color.g, color.b);
|
|
sprite->render();
|
|
if (debug->getEnabled())
|
|
{
|
|
SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
|
|
SDL_RenderDrawPoint(renderer, underFeet[0].x, underFeet[0].y);
|
|
SDL_RenderDrawPoint(renderer, underFeet[1].x, underFeet[1].y);
|
|
}
|
|
}
|
|
|
|
// Actualiza las variables del objeto
|
|
void Player::update()
|
|
{
|
|
checkInput(); // Comprueba las entradas y modifica variables
|
|
move(); // Recalcula la posición del jugador
|
|
animate(); // Establece la animación del jugador
|
|
checkBorders(); // Comprueba si está situado en alguno de los cuatro bordes de la habitación
|
|
checkJumpEnd(); // Comprueba si ha finalizado el salto al alcanzar la altura de inicio
|
|
}
|
|
|
|
// Comprueba las entradas y modifica variables
|
|
void Player::checkInput()
|
|
{
|
|
// Solo comprueba las entradas de dirección cuando está dsobre una superficie
|
|
if (state != s_standing)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (input->checkInput(INPUT_LEFT, REPEAT_TRUE))
|
|
{
|
|
vx = -0.6f;
|
|
sprite->setFlip(SDL_FLIP_HORIZONTAL);
|
|
}
|
|
|
|
else if (input->checkInput(INPUT_RIGHT, REPEAT_TRUE))
|
|
{
|
|
vx = 0.6f;
|
|
sprite->setFlip(SDL_FLIP_NONE);
|
|
}
|
|
|
|
else
|
|
{
|
|
vx = 0.0f;
|
|
}
|
|
|
|
if (input->checkInput(INPUT_UP, REPEAT_TRUE))
|
|
{
|
|
setState(s_jumping);
|
|
vy = -maxVY;
|
|
jump_ini = y;
|
|
}
|
|
}
|
|
|
|
// Indica si el jugador esta en uno de los cuatro bordes de la pantalla
|
|
bool Player::getOnBorder()
|
|
{
|
|
return onBorder;
|
|
}
|
|
|
|
// Indica en cual de los cuatro bordes se encuentra
|
|
int Player::getBorder()
|
|
{
|
|
return border;
|
|
}
|
|
|
|
// Comprueba si está situado en alguno de los cuatro bordes de la habitación
|
|
void Player::checkBorders()
|
|
{
|
|
if (x < PLAY_AREA_LEFT)
|
|
{
|
|
border = BORDER_LEFT;
|
|
onBorder = true;
|
|
}
|
|
else if (x > PLAY_AREA_RIGHT - w)
|
|
{
|
|
border = BORDER_RIGHT;
|
|
onBorder = true;
|
|
}
|
|
else if (y < PLAY_AREA_TOP)
|
|
{
|
|
border = BORDER_TOP;
|
|
onBorder = true;
|
|
}
|
|
else if (y > PLAY_AREA_BOTTOM - h)
|
|
{
|
|
border = BORDER_BOTTOM;
|
|
onBorder = true;
|
|
}
|
|
else
|
|
{
|
|
onBorder = false;
|
|
}
|
|
}
|
|
|
|
// Comprueba el estado del jugador
|
|
void Player::checkState()
|
|
{
|
|
// Actualiza las variables en función del estado
|
|
if (state == s_falling)
|
|
{
|
|
vx = 0.0f;
|
|
vy = maxVY;
|
|
}
|
|
|
|
else if (state == s_standing)
|
|
{
|
|
vy = 0.0f;
|
|
}
|
|
}
|
|
|
|
// Cambia al jugador de un borde al opuesto. Util para el cambio de pantalla
|
|
void Player::switchBorders()
|
|
{
|
|
if (border == BORDER_TOP)
|
|
{
|
|
y = PLAY_AREA_BOTTOM - h - 1;
|
|
jump_ini += 128;
|
|
}
|
|
else if (border == BORDER_BOTTOM)
|
|
{
|
|
y = PLAY_AREA_TOP + 1;
|
|
}
|
|
else if (border == BORDER_RIGHT)
|
|
{
|
|
x = PLAY_AREA_LEFT + 1;
|
|
}
|
|
if (border == BORDER_LEFT)
|
|
{
|
|
x = PLAY_AREA_RIGHT - w - 1;
|
|
}
|
|
|
|
onBorder = false;
|
|
}
|
|
|
|
// Aplica gravedad al jugador
|
|
void Player::applyGravity()
|
|
{
|
|
const float gf = 0.035f;
|
|
|
|
// La gravedad solo se aplica cuando el jugador esta saltando
|
|
// Nunca mientras cae o esta de pie
|
|
if (state == s_jumping)
|
|
{
|
|
vy += gf;
|
|
if (vy > maxVY)
|
|
{
|
|
vy = maxVY;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Obtiene el rectangulo que delimita al jugador
|
|
SDL_Rect Player::getRect()
|
|
{
|
|
return {(int)x, (int)y, w, h};
|
|
}
|
|
|
|
// Obtiene el rectangulo de colision del jugador
|
|
SDL_Rect &Player::getCollider()
|
|
{
|
|
colliderBox = getRect();
|
|
return colliderBox;
|
|
}
|
|
|
|
// Recalcula la posición del jugador y su animación
|
|
void Player::move()
|
|
{
|
|
lastPosition = {(int)x, (int)y}; // Guarda la posicion actual antes de modificarla
|
|
applyGravity(); // Aplica gravedad al jugador
|
|
checkState(); // Comprueba el estado del jugador
|
|
|
|
// Se mueve hacia la izquierda
|
|
if (vx < 0.0f)
|
|
{
|
|
// Crea el rectangulo de proyección en el eje X para ver si colisiona
|
|
SDL_Rect proj;
|
|
proj.x = (int)(x + vx);
|
|
proj.y = (int)y;
|
|
proj.h = h - 1;
|
|
proj.w = ceil(abs(vx)); // Para evitar que tenga un ancho de 0 pixels
|
|
|
|
// Comprueba la colisión
|
|
const int pos = room->checkRightSurfaces(&proj);
|
|
|
|
// Calcula la nueva posición
|
|
if (pos == -1)
|
|
{ // Si no hay colisión
|
|
x += vx;
|
|
}
|
|
else
|
|
{ // Si hay colisión lo mueve hasta donde no colisiona
|
|
x = pos + 1;
|
|
}
|
|
}
|
|
|
|
// Se mueve hacia la derecha
|
|
else if (vx > 0.0f)
|
|
{
|
|
// Crea el rectangulo de proyección en el eje X para ver si colisiona
|
|
SDL_Rect proj;
|
|
proj.x = (int)x + w;
|
|
proj.y = (int)y;
|
|
proj.h = h - 1;
|
|
proj.w = ceil(vx); // Para evitar que tenga un ancho de 0 pixels
|
|
|
|
// Comprueba la colisión
|
|
const int pos = room->checkLeftSurfaces(&proj);
|
|
|
|
// Calcula la nueva posición
|
|
if (pos == -1)
|
|
{ // Si no hay colisión
|
|
x += vx;
|
|
}
|
|
else
|
|
{ // Si hay colisión lo mueve hasta donde no colisiona
|
|
x = pos - w;
|
|
}
|
|
}
|
|
|
|
// Si ha salido del suelo, el jugador cae
|
|
if (state == s_standing && !isOnFloor())
|
|
{
|
|
setState(s_falling);
|
|
}
|
|
|
|
// Se mueve hacia arriba
|
|
if (vy < 0.0f)
|
|
{
|
|
// Crea el rectangulo de proyección en el eje X para ver si colisiona
|
|
SDL_Rect proj;
|
|
proj.x = (int)x;
|
|
proj.y = (int)(y + vy);
|
|
proj.h = ceil(abs(vy)); // Para evitar que tenga una altura de 0 pixels
|
|
proj.w = w - 1;
|
|
|
|
// Comprueba la colisión
|
|
const int pos = room->checkBottomSurfaces(&proj);
|
|
|
|
// Calcula la nueva posición
|
|
if (pos == -1)
|
|
{ // Si no hay colisión
|
|
y += vy;
|
|
}
|
|
else
|
|
{ // Si hay colisión lo mueve hasta donde no colisiona y entra en caída
|
|
y = pos + 1;
|
|
setState(s_falling);
|
|
}
|
|
}
|
|
|
|
// Se mueve hacia abajo
|
|
if (vy > 0.0f)
|
|
{
|
|
// Crea el rectangulo de proyección en el eje X para ver si colisiona
|
|
SDL_Rect proj;
|
|
proj.x = (int)x;
|
|
proj.y = (int)y + h;
|
|
proj.h = ceil(vy); // Para evitar que tenga una altura de 0 pixels
|
|
proj.w = w - 1;
|
|
|
|
// Comprueba la colisión
|
|
const int pos = room->checkTopSurfaces(&proj);
|
|
|
|
// Calcula la nueva posición
|
|
if (pos == -1)
|
|
{ // Si no hay colisión
|
|
y += vy;
|
|
}
|
|
else
|
|
{ // Si hay colisión lo mueve hasta donde no colisiona y pasa a estar sobre el suelo
|
|
y = pos - h;
|
|
setState(s_standing);
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------
|
|
|
|
// Calcula la nueva posición del jugador y compensa en caso de colisión
|
|
/*x += vx;
|
|
|
|
// Comprueba colisiones con muros
|
|
if (checkWalls())
|
|
{
|
|
// Recoloca
|
|
if (vx > 0.0f)
|
|
{
|
|
x = (int)x - ((int)x + w) % tileSize;
|
|
}
|
|
else
|
|
{
|
|
x = (int)x + tileSize - ((int)x % tileSize);
|
|
}
|
|
}
|
|
|
|
// Comprueba colisiones con rampas solo si esta quieto o cayendo
|
|
else if (vy >= 0.0f)
|
|
{
|
|
|
|
tile_e slope = checkSlopes();
|
|
tile_e slope2 = checkSlopes2();
|
|
debug->add("SLOPE = " + std::to_string(slope));
|
|
debug->add("SLOPE2 = " + std::to_string(slope2));
|
|
if (slope != t_empty)
|
|
{ // Cuesta hacia la derecha
|
|
if (slope == t_slope_r)
|
|
{ // Recoloca
|
|
y = -h + room->getSlopeHeight(feet[1], t_slope_r);
|
|
debug->add("feet[1] = " + std::to_string(feet[1].y));
|
|
}
|
|
|
|
// Cuesta hacia la izquierda
|
|
if (slope == t_slope_l)
|
|
{ // Recoloca
|
|
y = -h + room->getSlopeHeight(feet[0], t_slope_l);
|
|
debug->add("feet[0] = " + std::to_string(feet[0].y));
|
|
}
|
|
}
|
|
else if (slope2 != t_empty)
|
|
{ // Cuesta hacia la derecha
|
|
if (slope2 == t_slope_r)
|
|
{ // Recoloca
|
|
y = -h + room->getSlopeHeight(underFeet[1], t_slope_r);
|
|
debug->add("ufeet[1] = " + std::to_string(underFeet[1].y));
|
|
}
|
|
|
|
// Cuesta hacia la izquierda
|
|
if (slope2 == t_slope_l)
|
|
{ // Recoloca
|
|
y = -h + room->getSlopeHeight(underFeet[0], t_slope_l);
|
|
debug->add("ufeet[0] = " + std::to_string(underFeet[0].y));
|
|
}
|
|
}
|
|
}
|
|
|
|
y += vy;
|
|
if (checkWalls())
|
|
{
|
|
// Recoloca
|
|
if (vy > 0.0f)
|
|
{ // Bajando
|
|
y -= ((int)y + h) % tileSize;
|
|
setState(s_standing);
|
|
vy = 0.0f;
|
|
}
|
|
else
|
|
{ // Subiendo
|
|
y += tileSize - ((int)y % tileSize);
|
|
setState(s_falling);
|
|
vy = maxVY;
|
|
}
|
|
}
|
|
else
|
|
// Si no colisiona con los muros, haz comprobaciones extra
|
|
{
|
|
const int a = (lastPosition.y + h - 1) / tileSize;
|
|
const int b = ((int)y + h - 1) / tileSize;
|
|
const bool tile_change = a != b;
|
|
|
|
bool going_down = vy >= 0.0f;
|
|
bool tile_aligned = ((int)y + h) % tileSize == 0;
|
|
|
|
// Si está cayendo y hay cambio de tile o está justo sobre uno
|
|
if (going_down && (tile_aligned || tile_change))
|
|
{
|
|
// Comprueba si tiene uno de los pies sobre una superficie
|
|
if (isOnFloor())
|
|
{ // Y deja al jugador de pie
|
|
setState(s_standing);
|
|
vy = 0.0f;
|
|
|
|
// Si ademas ha habido un cambio de tile recoloca al jugador
|
|
if (tile_change && !checkSlopes2())
|
|
{
|
|
y = ((int)y - ((int)y % tileSize));
|
|
}
|
|
}
|
|
|
|
// Si tiene ambos pies sobre el vacío y no está saltando
|
|
else if (state != s_jumping)
|
|
{
|
|
setState(s_falling);
|
|
vy = maxVY;
|
|
}
|
|
}
|
|
|
|
going_down = vy >= 0.0f;
|
|
tile_aligned = ((int)y + h) % tileSize == 0;
|
|
|
|
// Si simplemente está cayendo (sin mirar si hay cambio de tile o si está justo sobre uno)
|
|
if (going_down)
|
|
{
|
|
if (state != s_jumping)
|
|
{
|
|
setState(s_falling);
|
|
vy = maxVY;
|
|
}
|
|
|
|
// Si está alineado con el tile mira el suelo (para que no lo mire si está
|
|
// dentro de un tile atravesable y lo deje a medias)
|
|
if (tile_aligned)
|
|
{
|
|
if (isOnFloor())
|
|
{
|
|
setState(s_standing);
|
|
vy = 0.0f;
|
|
}
|
|
}
|
|
// EXPERIMENTAL
|
|
else if (checkSlopes())
|
|
{
|
|
setState(s_standing);
|
|
vy = 0.0f;
|
|
}
|
|
}
|
|
}*/
|
|
|
|
// Actualiza la posición del sprite
|
|
sprite->setPosX(x);
|
|
sprite->setPosY(y);
|
|
}
|
|
|
|
// Establece la animación del jugador
|
|
void Player::animate()
|
|
{
|
|
if (vx != 0)
|
|
{
|
|
sprite->animate();
|
|
}
|
|
}
|
|
|
|
// Comprueba si ha finalizado el salto al alcanzar la altura de inicio
|
|
void Player::checkJumpEnd()
|
|
{
|
|
if (state == s_jumping)
|
|
if (vy > 0)
|
|
if (y >= jump_ini)
|
|
{ // Si alcanza la altura de salto inicial, pasa al estado de caída
|
|
setState(s_falling);
|
|
vy = maxVY;
|
|
}
|
|
}
|
|
|
|
// Comprueba si el jugador tiene suelo debajo de los pies
|
|
bool Player::isOnFloor()
|
|
{
|
|
bool onFloor = false;
|
|
|
|
updateFeet();
|
|
|
|
for (auto f : underFeet)
|
|
{
|
|
onFloor |= room->checkTopSurfaces(&f);
|
|
}
|
|
|
|
if (onFloor)
|
|
{
|
|
debug->add("ONFLOOR");
|
|
}
|
|
|
|
return onFloor;
|
|
}
|
|
|
|
// Comprueba que el jugador no atraviese ninguna pared
|
|
bool Player::checkWalls()
|
|
{
|
|
// Actualiza los puntos de colisión
|
|
updateColliderPoints();
|
|
|
|
// Comprueba si ha colisionado con un muro
|
|
bool wall = false;
|
|
|
|
for (auto c : colliderPoints)
|
|
{
|
|
wall |= (room->getTile(c) == t_wall);
|
|
}
|
|
|
|
return wall;
|
|
}
|
|
|
|
// Comprueba si el jugador está en una rampa
|
|
tile_e Player::checkSlopes()
|
|
{
|
|
// Actualiza los puntos de colisión
|
|
updateFeet();
|
|
|
|
bool slope_l = false;
|
|
bool slope_r = false;
|
|
|
|
// Comprueba si ha colisionado con una rampa
|
|
for (auto f : feet)
|
|
{
|
|
slope_l |= (room->getTile(f) == t_slope_l);
|
|
slope_r |= (room->getTile(f) == t_slope_r);
|
|
}
|
|
|
|
if (slope_l)
|
|
{
|
|
return t_slope_l;
|
|
}
|
|
|
|
if (slope_r)
|
|
{
|
|
return t_slope_r;
|
|
}
|
|
|
|
return t_empty;
|
|
}
|
|
|
|
// Comprueba si el jugador está en una rampa
|
|
tile_e Player::checkSlopes2()
|
|
{
|
|
// Actualiza los puntos de colisión
|
|
updateFeet();
|
|
|
|
bool slope_l = false;
|
|
bool slope_r = false;
|
|
bool wall = false;
|
|
bool passable = false;
|
|
|
|
// Comprueba si ha colisionado con una rampa
|
|
for (auto f : underFeet)
|
|
{
|
|
slope_l |= (room->getTile(f) == t_slope_l);
|
|
slope_r |= (room->getTile(f) == t_slope_r);
|
|
wall |= (room->getTile(f) == t_wall);
|
|
passable |= (room->getTile(f) == t_passable);
|
|
}
|
|
|
|
if (wall || passable)
|
|
{
|
|
return t_empty;
|
|
}
|
|
|
|
if (slope_l)
|
|
{
|
|
return t_slope_l;
|
|
}
|
|
|
|
if (slope_r)
|
|
{
|
|
return t_slope_r;
|
|
}
|
|
|
|
return t_empty;
|
|
}
|
|
|
|
// Obtiene algunos parametros del jugador
|
|
player_t Player::getSpawnParams()
|
|
{
|
|
player_t params;
|
|
|
|
params.x = x;
|
|
params.y = y;
|
|
params.vx = vx;
|
|
params.vy = vy;
|
|
params.jump_ini = jump_ini;
|
|
params.state = state;
|
|
params.flip = sprite->getFlip();
|
|
|
|
return params;
|
|
}
|
|
|
|
// Recarga la textura
|
|
void Player::reLoadTexture()
|
|
{
|
|
texture->reLoad();
|
|
}
|
|
|
|
// Establece el valor de la variable
|
|
void Player::setRoom(Room *room)
|
|
{
|
|
this->room = room;
|
|
}
|
|
|
|
// Actualiza los puntos de colisión
|
|
void Player::updateColliderPoints()
|
|
{
|
|
const SDL_Rect rect = getRect();
|
|
colliderPoints[0] = {rect.x, rect.y};
|
|
colliderPoints[1] = {rect.x + 7, rect.y};
|
|
colliderPoints[2] = {rect.x + 7, rect.y + 7};
|
|
colliderPoints[3] = {rect.x, rect.y + 7};
|
|
colliderPoints[4] = {rect.x, rect.y + 8};
|
|
colliderPoints[5] = {rect.x + 7, rect.y + 8};
|
|
colliderPoints[6] = {rect.x + 7, rect.y + 15};
|
|
colliderPoints[7] = {rect.x, rect.y + 15};
|
|
}
|
|
|
|
// Actualiza los puntos de los pies
|
|
void Player::updateFeet()
|
|
{
|
|
const SDL_Point p = {(int)x, (int)y};
|
|
|
|
underFeet[0] = {p.x, p.y + h};
|
|
underFeet[1] = {p.x + 7, p.y + h};
|
|
|
|
feet[0] = {p.x, p.y + h - 1};
|
|
feet[1] = {p.x + 7, p.y + h - 1};
|
|
}
|
|
|
|
// Obtiene el valor de la variable
|
|
bool Player::getInvincible()
|
|
{
|
|
return invincible;
|
|
}
|
|
|
|
// Establece el valor de la variable
|
|
void Player::setInvincible(bool value)
|
|
{
|
|
invincible = value;
|
|
}
|
|
|
|
// Cambia el estado del jugador
|
|
void Player::setState(state_e value)
|
|
{
|
|
prevState = state;
|
|
state = value;
|
|
} |