#include "player.h" // Constructor Player::Player(SDL_Renderer *renderer, Asset *asset, Input *input, Map *map) { this->asset = asset; this->renderer = renderer; this->input = input; this->map = map; 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 LTexture(); loadTextureFromFile(texture, asset->get("player.png"), renderer); sprite = new AnimatedSprite(texture, renderer, asset->get("player.ani")); w = 16; h = 24; x = 3 * 16; y = 168; vx = 0; vy = 0; lastPosition = {(int)x, (int)y}; sprite->setRect({(int)x, (int)y, w, h}); sprite->setCurrentAnimation("stand"); sprite->setFlip(SDL_FLIP_NONE); jumpStrenght = 2.0f; gravity = 0.3f; accelX = 0.12f; maxVX = 1.5f; maxVY = 4.0f; state = s_standing; 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; diamonds = 0; } // 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() { checkInput(); move(); animate(); checkActors(); } // Dibuja el objeto void Player::render() { sprite->render(); } // Comprueba las entradas y modifica variables void Player::checkInput() { if (input->checkInput(INPUT_LEFT, REPEAT_TRUE)) { vx = std::max(vx -= accelX, -maxVX); sprite->setFlip(SDL_FLIP_HORIZONTAL); } else if (input->checkInput(INPUT_RIGHT, REPEAT_TRUE)) { vx = std::min(vx += accelX, maxVX); sprite->setFlip(SDL_FLIP_NONE); } else { if (vx > 0.0f) { vx = std::max(vx -= accelX, 0.0f); } else { vx = std::min(vx += accelX, 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 = std::max(jumpStrenght -= 0.4f, 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 = std::min(vy += gravity, 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 (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() { bool success = false; border = b_none; if (x < map->getPlayArea(b_left)) { border = b_left; success = true; } else if (x > map->getPlayArea(b_right) - w) { border = b_right; success = true; } else if (y < map->getPlayArea(b_top)) { border = b_top; success = true; } else if (y > map->getPlayArea(b_bottom) - h) { border = b_bottom; success = true; } return success; } // 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++; JA_PlaySound(sound_coin); map->getItem(index); map->deleteActor(index); } return index; }