#include "player.h" #include #include // 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; jumpIni = ini.jumpIni; 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}); jumpSound.push_back(JA_LoadSound(asset->get("jump1.wav").c_str())); jumpSound.push_back(JA_LoadSound(asset->get("jump2.wav").c_str())); jumpSound.push_back(JA_LoadSound(asset->get("jump3.wav").c_str())); jumpSound.push_back(JA_LoadSound(asset->get("jump4.wav").c_str())); jumpSound.push_back(JA_LoadSound(asset->get("jump5.wav").c_str())); jumpSound.push_back(JA_LoadSound(asset->get("jump6.wav").c_str())); jumpSound.push_back(JA_LoadSound(asset->get("jump7.wav").c_str())); jumpSound.push_back(JA_LoadSound(asset->get("jump8.wav").c_str())); jumpSound.push_back(JA_LoadSound(asset->get("jump9.wav").c_str())); jumpSound.push_back(JA_LoadSound(asset->get("jump10.wav").c_str())); jumpSound.push_back(JA_LoadSound(asset->get("jump11.wav").c_str())); jumpSound.push_back(JA_LoadSound(asset->get("jump12.wav").c_str())); jumpSound.push_back(JA_LoadSound(asset->get("jump13.wav").c_str())); jumpSound.push_back(JA_LoadSound(asset->get("jump14.wav").c_str())); jumpSound.push_back(JA_LoadSound(asset->get("jump15.wav").c_str())); jumpSound.push_back(JA_LoadSound(asset->get("jump16.wav").c_str())); jumpSound.push_back(JA_LoadSound(asset->get("jump17.wav").c_str())); jumpSound.push_back(JA_LoadSound(asset->get("jump18.wav").c_str())); jumpSound.push_back(JA_LoadSound(asset->get("jump19.wav").c_str())); jumpSound.push_back(JA_LoadSound(asset->get("jump20.wav").c_str())); jumpSound.push_back(JA_LoadSound(asset->get("jump21.wav").c_str())); jumpSound.push_back(JA_LoadSound(asset->get("jump22.wav").c_str())); jumpSound.push_back(JA_LoadSound(asset->get("jump23.wav").c_str())); jumpSound.push_back(JA_LoadSound(asset->get("jump24.wav").c_str())); fallSound.push_back(JA_LoadSound(asset->get("jump11.wav").c_str())); fallSound.push_back(JA_LoadSound(asset->get("jump12.wav").c_str())); fallSound.push_back(JA_LoadSound(asset->get("jump13.wav").c_str())); fallSound.push_back(JA_LoadSound(asset->get("jump14.wav").c_str())); fallSound.push_back(JA_LoadSound(asset->get("jump15.wav").c_str())); fallSound.push_back(JA_LoadSound(asset->get("jump16.wav").c_str())); fallSound.push_back(JA_LoadSound(asset->get("jump17.wav").c_str())); fallSound.push_back(JA_LoadSound(asset->get("jump18.wav").c_str())); fallSound.push_back(JA_LoadSound(asset->get("jump19.wav").c_str())); fallSound.push_back(JA_LoadSound(asset->get("jump20.wav").c_str())); fallSound.push_back(JA_LoadSound(asset->get("jump21.wav").c_str())); fallSound.push_back(JA_LoadSound(asset->get("jump22.wav").c_str())); fallSound.push_back(JA_LoadSound(asset->get("jump23.wav").c_str())); fallSound.push_back(JA_LoadSound(asset->get("jump24.wav").c_str())); r = {0, 0, 0, 0}; jumpCounter = 0; fallCounter = 0; } // Destructor Player::~Player() { delete texture; delete sprite; for (auto s : jumpSound) { JA_DeleteSound(s); } } // Pinta el jugador en pantalla void Player::render() { sprite->getTexture()->setColor(color.r, color.g, color.b); sprite->render(); if (debug->getEnabled()) { // Pinta los underfeet SDL_SetRenderDrawColor(renderer, 255, 0, 255, 255); SDL_RenderDrawPoint(renderer, underFeet[0].x, underFeet[0].y); SDL_RenderDrawPoint(renderer, underFeet[1].x, underFeet[1].y); // Pinta rectangulo del jugador SDL_SetRenderDrawColor(renderer, 0, 255, 0, 192); SDL_Rect rect = getRect(); SDL_RenderFillRect(renderer, &rect); // Pinta el rectangulo de movimiento SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255); SDL_RenderFillRect(renderer, &r); debug->add("RECT: " + std::to_string(r.x) + "," + std::to_string(r.y) + "," + std::to_string(r.w) + "," + std::to_string(r.h)); } } // 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; jumpIni = y; jumpCounter = 0; } } // 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; fallCounter++; playFallSound(); } else if (state == s_standing) { vy = 0.0f; jumpCounter = 0; fallCounter = 0; } else if (state == s_jumping) { jumpCounter++; playJumpSound(); } } // 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; jumpIni += 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; proj.w = ceil(abs(vx)); // Para evitar que tenga un ancho de 0 pixels r = proj; // Comprueba la colisión con las superficies 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; } // Si ha tocado alguna rampa mientras camina (sin saltar), asciende if (state != s_jumping) { v_line_t leftSide = {(int)x, (int)y + h - 2, (int)y + h - 1}; const int ly = room->checkLeftSlopes(&leftSide); if (ly > -1) { y = ly - h; // setState(s_standing); } } // Si está bajando la rampa, recoloca al jugador if (isOnDownSlope()) { y += 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; proj.w = ceil(vx); // Para evitar que tenga un ancho de 0 pixels r = proj; // 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 tocado alguna rampa mientras camina (sin saltar), asciende if (state != s_jumping) { v_line_t rightSide = {(int)x + w - 1, (int)y + h - 2, (int)y + h - 1}; const int ry = room->checkRightSlopes(&rightSide); if (ry > -1) { y = ry - h; // setState(s_standing); } } // Si está bajando la rampa, recoloca al jugador if (isOnDownSlope()) { y += 1; } } // 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 Y 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; r = proj; // 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 else if (vy > 0.0f) { // Crea el rectangulo de proyección en el eje Y 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; r = proj; // Comprueba la colisión con los muros 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); } } // 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 >= jumpIni) { // Si alcanza la altura de salto inicial, pasa al estado de caída setState(s_falling); vy = maxVY; jumpCounter = 0; } } } } // Calcula y reproduce el sonido de salto void Player::playJumpSound() { if (jumpCounter % 4 == 0) { JA_PlaySound(jumpSound[jumpCounter / 4]); } debug->add("JUMP: " + std::to_string(jumpCounter / 4)); } // Calcula y reproduce el sonido de caer void Player::playFallSound() { if (fallCounter % 4 == 0) { JA_PlaySound(fallSound[std::min((fallCounter / 4), (int)fallSound.size() - 1)]); } debug->add("FALL: " + std::to_string(fallCounter / 4)); } // Comprueba si el jugador tiene suelo debajo de los pies bool Player::isOnFloor() { bool onFloor = false; updateFeet(); // Comprueba las superficies for (auto f : underFeet) { onFloor |= room->checkTopSurfaces(&f); } // Comprueba las rampas onFloor |= room->checkLeftSlopes(&underFeet[0]); onFloor |= room->checkRightSlopes(&underFeet[1]); if (onFloor) { debug->add("ONFLOOR"); } return onFloor; } // Comprueba si el jugador está sobre una rampa hacia abajo bool Player::isOnDownSlope() { bool onSlope = false; updateFeet(); // Cuando el jugador baja una escalera, se queda volando // Hay que mirar otro pixel más por debajo underFeet[0].y += 1; underFeet[1].y += 1; // Comprueba las rampas onSlope |= room->checkLeftSlopes(&underFeet[0]); onSlope |= room->checkRightSlopes(&underFeet[1]); if (onSlope) { debug->add("ONSLOPE"); } return onSlope; } // 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; } // 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.jumpIni = jumpIni; 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; }