594 lines
13 KiB
C++
594 lines
13 KiB
C++
#include "player.h"
|
|
|
|
// Constructor
|
|
Player::Player(player_t player, SDL_Renderer *renderer, Asset *asset, Input *input, Map *map, Debug *debug, int *diamonds)
|
|
{
|
|
this->asset = asset;
|
|
this->renderer = renderer;
|
|
this->input = input;
|
|
this->map = map;
|
|
this->debug = debug;
|
|
this->diamonds = diamonds;
|
|
|
|
sound_jump = JA_LoadSound(asset->get("sound_player_jump.wav").c_str());
|
|
sound_death = JA_LoadSound(asset->get("sound_player_death.wav").c_str());
|
|
sound_coin = JA_LoadSound(asset->get("sound_player_coin.wav").c_str());
|
|
|
|
texture = new Texture(renderer, asset->get("player.png"));
|
|
sprite = new AnimatedSprite(texture, renderer, asset->get("player.ani"));
|
|
|
|
w = 16;
|
|
h = 24;
|
|
x = player.x;
|
|
y = player.y;
|
|
vx = player.vx;
|
|
vy = player.vy;
|
|
lastPosition = {(int)x, (int)y};
|
|
sprite->setRect({(int)x, (int)y, w, h});
|
|
sprite->setCurrentAnimation("stand");
|
|
sprite->setFlip(player.flip);
|
|
|
|
jumpStrenght = 2.0f;
|
|
gravity = 0.3f;
|
|
accelX = 0.12f;
|
|
maxVX = 1.5f;
|
|
maxVY = 4.0f;
|
|
|
|
state = player.state;
|
|
living = l_alive;
|
|
jumpPressed = false;
|
|
key.insert(key.end(), {0, 0, 0, 0, 0, 0});
|
|
const SDL_Point p = {0, 0};
|
|
collider.insert(collider.end(), {p, p, p, p, p, p, p, p, p, p, p, p});
|
|
underFeet.insert(underFeet.end(), {p, p, p});
|
|
hookedOnMovingPlatform = -1;
|
|
colliderBox = getRect();
|
|
}
|
|
|
|
// Destructor
|
|
Player::~Player()
|
|
{
|
|
JA_DeleteSound(sound_jump);
|
|
JA_DeleteSound(sound_death);
|
|
JA_DeleteSound(sound_coin);
|
|
|
|
texture->unload();
|
|
delete texture;
|
|
|
|
delete sprite;
|
|
}
|
|
|
|
// Actualiza todas las variables
|
|
void Player::update()
|
|
{
|
|
checkLivingState();
|
|
checkInput();
|
|
move();
|
|
animate();
|
|
checkActors();
|
|
colliderBox = getRect();
|
|
|
|
debug->add(std::to_string((int)sprite->getPosX()) + "," + std::to_string((int)sprite->getPosY()) + "," + std::to_string((int)sprite->getWidth()) + "," + std::to_string((int)sprite->getHeight()));
|
|
debug->add("VY " + std::to_string(vy) + " " + std::to_string(jumpStrenght));
|
|
debug->add("VX " + std::to_string(vx));
|
|
debug->add("jump_pressed " + std::to_string(jumpPressed));
|
|
debug->add("isOnFloor " + std::to_string(isOnFloor()));
|
|
debug->add("getTile(" + std::to_string(underFeet[0].x) + "," + std::to_string(underFeet[0].y) + ") = " + std::to_string(map->getTile(underFeet[0])));
|
|
debug->add("state " + std::to_string(state));
|
|
debug->add(map->getName() + " (" + map->getRoomFileName(b_top) + ", " + map->getRoomFileName(b_right) + ", " + map->getRoomFileName(b_bottom) + ", " + map->getRoomFileName(b_left) + ")");
|
|
debug->add("hookedOn = " + std::to_string(hookedOnMovingPlatform));
|
|
}
|
|
|
|
// Dibuja el objeto
|
|
void Player::render()
|
|
{
|
|
sprite->render();
|
|
}
|
|
|
|
// Comprueba las entradas y modifica variables
|
|
void Player::checkInput()
|
|
{
|
|
if (living == l_dying)
|
|
{ // Aplica fricción
|
|
if (vx > 0.0f)
|
|
{
|
|
vx -= (accelX / 3);
|
|
vx = std::max(vx, 0.0f);
|
|
}
|
|
else
|
|
{
|
|
vx += (accelX / 3);
|
|
vx = std::min(vx, 0.0f);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (input->checkInput(INPUT_LEFT, REPEAT_TRUE))
|
|
{
|
|
vx -= accelX;
|
|
vx = std::max(vx, -maxVX);
|
|
sprite->setFlip(SDL_FLIP_HORIZONTAL);
|
|
}
|
|
else if (input->checkInput(INPUT_RIGHT, REPEAT_TRUE))
|
|
{
|
|
vx += accelX;
|
|
vx = std::min(vx, maxVX);
|
|
sprite->setFlip(SDL_FLIP_NONE);
|
|
}
|
|
else
|
|
{ // Aplica fricción
|
|
if (vx > 0.0f)
|
|
{
|
|
vx -= accelX;
|
|
vx = std::max(vx, 0.0f);
|
|
}
|
|
else
|
|
{
|
|
vx += accelX;
|
|
vx = std::min(vx, 0.0f);
|
|
}
|
|
}
|
|
|
|
if (input->checkInput(INPUT_UP, REPEAT_TRUE))
|
|
{
|
|
if (state == s_standing)
|
|
{
|
|
if (!jumpPressed)
|
|
{
|
|
jumpStrenght = 2.0f;
|
|
vy -= jumpStrenght;
|
|
state = s_jumping;
|
|
isOn = f_none;
|
|
jumpPressed = true;
|
|
JA_PlaySound(sound_jump);
|
|
}
|
|
}
|
|
else if (state == s_jumping)
|
|
{
|
|
if (jumpPressed)
|
|
{
|
|
jumpStrenght -= 0.4f;
|
|
jumpStrenght = std::max(jumpStrenght, 0.0f);
|
|
vy -= jumpStrenght;
|
|
}
|
|
}
|
|
|
|
jumpPressed = true;
|
|
|
|
// Si salta sale de la plataforma movil
|
|
hookedOnMovingPlatform = -1;
|
|
}
|
|
else
|
|
{
|
|
jumpPressed = false;
|
|
if (state == s_jumping)
|
|
{
|
|
state = s_falling;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Aplica la gravedad
|
|
void Player::addGravity()
|
|
{
|
|
// *** Falta ver pq la gravedad empuja al muñeco hacia abajo en los tiles atravesables
|
|
if (state != s_standing)
|
|
{
|
|
vy += gravity;
|
|
vy = std::min(vy, maxVY);
|
|
}
|
|
}
|
|
|
|
// Actualiza los puntos de colisión
|
|
void Player::updateColliders()
|
|
{
|
|
const SDL_Point p = {(int)x, (int)y};
|
|
|
|
// Lado izquierdo
|
|
collider[0] = p;
|
|
collider[1] = {p.x, p.y + 7};
|
|
collider[2] = {p.x, p.y + 12};
|
|
collider[3] = {p.x, p.y + 18};
|
|
collider[4] = {p.x, p.y + 23};
|
|
|
|
// Lado derecho
|
|
collider[5] = {p.x + 15, p.y};
|
|
collider[6] = {p.x + 15, p.y + 7};
|
|
collider[7] = {p.x + 15, p.y + 12};
|
|
collider[8] = {p.x + 15, p.y + 18};
|
|
collider[9] = {p.x + 15, p.y + 23};
|
|
|
|
// Centro
|
|
collider[10] = {p.x + 7, p.y};
|
|
collider[11] = {p.x + 7, p.y + 23};
|
|
}
|
|
|
|
// 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};
|
|
underFeet[2] = {p.x + 15, p.y + h};
|
|
}
|
|
|
|
// Compruena las colisiones con el mapa
|
|
bool Player::checkMapCollisions()
|
|
{
|
|
bool collision = false;
|
|
|
|
updateColliders();
|
|
|
|
for (auto c : collider)
|
|
{
|
|
collision |= (map->getTile(c) == wall);
|
|
}
|
|
|
|
return collision;
|
|
}
|
|
|
|
// Mueve al jugador en función de la velocidad/desplazamiento
|
|
void Player::move()
|
|
{
|
|
const int tileSize = map->getTileSize();
|
|
lastPosition = {(int)x, (int)y};
|
|
addGravity();
|
|
|
|
// Mueve en el eje X
|
|
x += vx;
|
|
|
|
// Comprueba colisiones con muros
|
|
if (checkMapCollisions())
|
|
{
|
|
// Recoloca
|
|
if (vx > 0)
|
|
{
|
|
x -= ((int)x + w) % tileSize;
|
|
}
|
|
else
|
|
{
|
|
x += tileSize - ((int)x % tileSize);
|
|
}
|
|
|
|
vx = 0.0f;
|
|
}
|
|
|
|
// Mueve en el eje Y y comprueba colisiones con muros
|
|
y += vy;
|
|
if (checkMapCollisions())
|
|
{
|
|
// Recoloca
|
|
if (vy > 0.0f)
|
|
{
|
|
y -= ((int)y + h) % tileSize;
|
|
state = s_standing;
|
|
}
|
|
else
|
|
{
|
|
y += tileSize - ((int)y % tileSize);
|
|
state = s_falling;
|
|
}
|
|
vy = 0.0f;
|
|
}
|
|
|
|
// Si no hay colisiones con los muros en el eje Y, comprueba
|
|
// no haya atravesado el suelo de un tile atravesble
|
|
else
|
|
{
|
|
const int a = (lastPosition.y + h) / tileSize;
|
|
const int b = ((int)y + h) / tileSize;
|
|
const bool tile_change = a != b;
|
|
|
|
const bool going_down = vy >= 0.0f;
|
|
const bool tile_aligned = ((int)y + h) % tileSize == 0;
|
|
|
|
if (((going_down) && (tile_aligned)) || ((going_down) && (tile_change)))
|
|
{
|
|
// Tiene uno de los pies sobre una superficie
|
|
if (isOnFloor())
|
|
{
|
|
state = s_standing;
|
|
vy = 0.0f;
|
|
y -= ((int)y + h) % tileSize;
|
|
}
|
|
// Tiene ambos pies sobre el vacío
|
|
else
|
|
{
|
|
state = s_falling;
|
|
}
|
|
}
|
|
|
|
const bool going_down2 = vy >= 0.0f;
|
|
const bool tile_aligned2 = ((int)y + h) % tileSize == 0;
|
|
// Si está cayendo
|
|
if (going_down2)
|
|
{
|
|
state = s_falling;
|
|
// 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_aligned2)
|
|
{
|
|
if (isOnFloor())
|
|
{
|
|
state = s_standing;
|
|
}
|
|
}
|
|
|
|
// Si no esta enganchado a una plataforma
|
|
if (hookedOnMovingPlatform == -1)
|
|
{
|
|
// Si esta sobre una plataforma
|
|
if (isOnMovingPlatform())
|
|
{
|
|
// Detener la caída y alinearlo con la plataforma
|
|
state = s_standing;
|
|
vy = 0.0f;
|
|
y = map->getActorCollider(hookedOnMovingPlatform).y - h;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Si está enganchado a una plataforma movil
|
|
if (hookedOnMovingPlatform != -1)
|
|
{
|
|
// Dejarlo alineado con la plataforma
|
|
state = s_standing;
|
|
vy = 0.0f;
|
|
y = map->getActorCollider(hookedOnMovingPlatform).y - h;
|
|
x += map->getActorIncX(hookedOnMovingPlatform);
|
|
isOnMovingPlatform();
|
|
}
|
|
}
|
|
|
|
// Actualiza la posición del sprite
|
|
sprite->setPosX(x);
|
|
sprite->setPosY(y);
|
|
}
|
|
|
|
// Anima al jugador
|
|
void Player::animate()
|
|
{
|
|
if (living == l_dying)
|
|
{
|
|
sprite->setCurrentAnimation("death");
|
|
}
|
|
|
|
else if (state != s_standing)
|
|
{
|
|
sprite->setCurrentAnimation("jump");
|
|
}
|
|
else
|
|
{
|
|
if (abs(vx) < 0.50f)
|
|
{
|
|
sprite->setCurrentAnimation("stand");
|
|
}
|
|
else
|
|
{
|
|
sprite->setCurrentAnimation("walk");
|
|
}
|
|
}
|
|
|
|
sprite->animate();
|
|
}
|
|
|
|
// Comprueba si el jugador tiene suelo debajo de los pies
|
|
bool Player::isOnFloor()
|
|
{
|
|
if (isOn == f_platform)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool onFloor = false;
|
|
|
|
updateFeet();
|
|
|
|
for (auto f : underFeet)
|
|
{
|
|
onFloor |= ((map->getTile(f) == wall) || (map->getTile(f) == passable));
|
|
}
|
|
|
|
if (onFloor)
|
|
{
|
|
isOn = f_wall;
|
|
}
|
|
else
|
|
{
|
|
isOn = f_none;
|
|
}
|
|
|
|
return onFloor;
|
|
}
|
|
|
|
// Comprueba si el jugador tiene una plataforma movil bajo sus pies
|
|
bool Player::isOnMovingPlatform()
|
|
{
|
|
bool onMovingPlatform = false;
|
|
hookedOnMovingPlatform = -1;
|
|
|
|
// Si esta sobre el suelo, no puede estar tambien sobre una plataforma movil
|
|
if (isOn == f_wall)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
updateFeet();
|
|
|
|
for (auto f : underFeet)
|
|
{
|
|
onMovingPlatform |= (map->getActorName(map->actorCollision(f)) == a_moving_platform);
|
|
hookedOnMovingPlatform = std::max(hookedOnMovingPlatform, (map->actorCollision(f)));
|
|
}
|
|
|
|
if (!onMovingPlatform)
|
|
{
|
|
hookedOnMovingPlatform = -1;
|
|
isOn = f_none;
|
|
}
|
|
else
|
|
{
|
|
isOn = f_platform;
|
|
}
|
|
|
|
return onMovingPlatform;
|
|
}
|
|
|
|
// Comprueba si está situado en alguno de los cuatro bordes de la habitación
|
|
bool Player::isOnScreenBorder()
|
|
{
|
|
border = b_none;
|
|
|
|
if (x < map->getPlayArea(b_left))
|
|
{
|
|
border = b_left;
|
|
return true;
|
|
}
|
|
|
|
else if (x > map->getPlayArea(b_right) - w)
|
|
{
|
|
border = b_right;
|
|
return true;
|
|
}
|
|
|
|
else if (y < map->getPlayArea(b_top))
|
|
{
|
|
border = b_top;
|
|
return true;
|
|
}
|
|
|
|
else if (y > map->getPlayArea(b_bottom) - h)
|
|
{
|
|
border = b_bottom;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// Devuelve el valor de la variable
|
|
e_border Player::getBorder()
|
|
{
|
|
return border;
|
|
}
|
|
|
|
// Cambia al jugador de un borde al opuesto. Util para el cambio de pantalla
|
|
void Player::switchBorders()
|
|
{
|
|
switch (border)
|
|
{
|
|
case b_top:
|
|
y = map->getPlayArea(b_bottom) - h;
|
|
break;
|
|
|
|
case b_bottom:
|
|
y = map->getPlayArea(b_top);
|
|
break;
|
|
|
|
case b_right:
|
|
x = map->getPlayArea(b_left);
|
|
break;
|
|
|
|
case b_left:
|
|
x = map->getPlayArea(b_right) - w;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
border = b_none;
|
|
}
|
|
|
|
// Pasa la referencia del mapa
|
|
void Player::setMap(Map *map)
|
|
{
|
|
this->map = map;
|
|
}
|
|
|
|
// Comprueba las interacciones con los actores
|
|
int Player::checkActors()
|
|
{
|
|
SDL_Rect rect = sprite->getRect();
|
|
const int index = map->actorCollision(rect);
|
|
const int name = map->getActorName(index);
|
|
|
|
if (name == a_diamond)
|
|
{
|
|
*diamonds = *diamonds + 1;
|
|
JA_PlaySound(sound_coin);
|
|
map->getItem(index);
|
|
map->deleteActor(index);
|
|
}
|
|
|
|
return index;
|
|
}
|
|
|
|
// Recarga las texturas
|
|
void Player::reLoadTextures()
|
|
{
|
|
texture->reLoad();
|
|
}
|
|
|
|
// Devuelve el rectangulo que contiene al enemigo
|
|
SDL_Rect Player::getRect()
|
|
{
|
|
return sprite->getRect();
|
|
}
|
|
|
|
// Obtiene el rectangulo de colision del enemigo
|
|
SDL_Rect &Player::getCollider()
|
|
{
|
|
return colliderBox;
|
|
}
|
|
|
|
// Comprueba los estados de vida
|
|
void Player::checkLivingState()
|
|
{
|
|
switch (living)
|
|
{
|
|
case l_alive:
|
|
break;
|
|
|
|
case l_dying:
|
|
if (sprite->animationIsCompleted())
|
|
{
|
|
living = l_dead;
|
|
}
|
|
break;
|
|
|
|
case l_dead:
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Establece el estado de vida del jugador
|
|
void Player::setLivingState(e_living value)
|
|
{
|
|
living = value;
|
|
}
|
|
|
|
// Obtiene el estado de vida del jugador
|
|
e_living Player::getLivingState()
|
|
{
|
|
return living;
|
|
}
|
|
|
|
// 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.state = state;
|
|
params.flip = sprite->getFlip();
|
|
|
|
return params;
|
|
} |