#include "game.h" // Constructor Game::Game(SDL_Window *window, SDL_Renderer *renderer, Asset *asset, Lang *lang, Input *input) { // Inicia variables mCurrentRoom = "02.room"; mSpawnPoint = {2 * BLOCK, 12 * BLOCK, 0, 0, 0, STATUS_STANDING, SDL_FLIP_NONE}; mDebug = false; // Copia los punteros mRenderer = renderer; mAsset = asset; mLang = lang; mInput = input; // Crea los objetos mScreen = new Screen(window, renderer); mItemTracker = new Item_tracker(); mRoom = new Room(mAsset->get(mCurrentRoom), mRenderer, mAsset, mItemTracker); mPlayer = new Player(mSpawnPoint, mAsset->get("player01.png"), mRenderer, mAsset, mInput, mRoom); mEventHandler = new SDL_Event(); mTextureText = new LTexture(); mText = new Text(mAsset->get("smb2.txt"), mTextureText, renderer); mFade = new Fade(renderer); } Game::~Game() { // Borra las referencias a los punteros mRenderer = nullptr; mAsset = nullptr; mLang = nullptr; mInput = nullptr; // Libera la memoria de los objetos delete mScreen; mScreen = nullptr; delete mItemTracker; mItemTracker = nullptr; delete mRoom; mRoom = nullptr; delete mPlayer; mPlayer = nullptr; delete mEventHandler; mEventHandler = nullptr; mTextureText->unload(); delete mTextureText; mTextureText = nullptr; delete mText; mText = nullptr; delete mFade; mFade = nullptr; } // Inicializa las variables necesarias para la sección 'Game' void Game::init() { // Carga los recursos loadMedia(); mTicks = 0; mTicksSpeed = 15; mSection.name = SECTION_PROG_GAME; mSection.subsection = SECTION_GAME_PLAY; } // Carga los recursos necesarios para la sección 'Game' bool Game::loadMedia() { bool success = true; // Texturas success &= loadTextureFromFile(mTextureText, mAsset->get("smb2.png"), mRenderer); return success; } // Bucle para el juego section_t Game::run() { init(); while (mSection.name == SECTION_PROG_GAME) { // Sección juego jugando if (mSection.subsection == SECTION_GAME_PLAY) { update(); draw(); } } return mSection; } // Actualiza el juego, las variables, comprueba la entrada, etc. void Game::update() { // Comprueba que la diferencia de ticks sea mayor a la velocidad del juego if (SDL_GetTicks() - mTicks > mTicksSpeed) { // Actualiza el contador de ticks mTicks = SDL_GetTicks(); // Comprueba los eventos que hay en la cola while (SDL_PollEvent(mEventHandler) != 0) { // Evento de salida de la aplicación if (mEventHandler->type == SDL_QUIT) { mSection.name = SECTION_PROG_QUIT; break; } } mRoom->update(); mPlayer->update(); checkPlayerAndWalls(); // Debe ir detras del player update, por si se ha metido en algun muro checkPlayerOnBorder(); checkPlayerOnFloor(); checkPlayerAndItems(); checkPlayerAndEnemies(); checkInput(); } } // Pinta los objetos en pantalla void Game::draw() { // Prepara para dibujar el frame mScreen->start(); mScreen->clean(mRoom->getBGColor()); mRoom->drawMap(); mRoom->drawEnemies(); mRoom->drawItems(); mPlayer->draw(); // Texto en el centro de la pantalla SDL_Rect rect = {0, 16 * 8, PLAY_AREA_RIGHT, 8}; color_t color = stringToColor("light_black"); SDL_SetRenderDrawColor(mRenderer, color.r, color.g, color.b, 0xFF); SDL_RenderFillRect(mRenderer, &rect); mText->writeCentered(GAMECANVAS_CENTER_X, 16 * 8, mRoom->getName()); // Debug info if (mDebug) { std::string text; text = "status: " + std::to_string(mPlayer->status); mText->write(0, 17 * 8, text); text = "foot: " + std::to_string((int)mPlayer->getLeftFoot().y); mText->write(0, 18 * 8, text); const int a = (mPlayer->lastPosition.y + 16) / 8; const int b = mPlayer->getLeftFoot().y / 8; text = "tile: " + std::to_string(a) + " - " + std::to_string(b); mText->write(0, 19 * 8, text); const bool collision = checkPlayerAndEnemies(); text = "collision: " + std::to_string(collision); mText->write(0, 20 * 8, text); } // Actualiza la pantalla mScreen->blit(); } // Comprueba la entrada void Game::checkInput() { /* if (mInput->checkInput(INPUT_UP, REPEAT_FALSE)) changeRoom(mRoom->getRoomUp()); if (mInput->checkInput(INPUT_DOWN, REPEAT_FALSE)) changeRoom(mRoom->getRoomDown()); if (mInput->checkInput(INPUT_LEFT, REPEAT_FALSE)) changeRoom(mRoom->getRoomLeft()); if (mInput->checkInput(INPUT_RIGHT, REPEAT_FALSE)) changeRoom(mRoom->getRoomRight()); */ if (mInput->checkInput(INPUT_BUTTON_2, REPEAT_FALSE)) mDebug = !mDebug; } // Cambia de habitación bool Game::changeRoom(std::string file) { bool success = false; // En las habitaciones los limites tienen la cadena del fichero o un 0 en caso de no limitar con nada if (file != "0") // Verifica que exista el fichero que se va a cargar if (mAsset->get(file) != "") { // Elimina la habitación actual delete mRoom; mRoom = nullptr; // Crea un objeto habitación nuevo a partir del fichero mRoom = new Room(mAsset->get(file), mRenderer, mAsset, mItemTracker); success = true; } return success; } // Comprueba si el jugador esta en el borde de la pantalla void Game::checkPlayerOnBorder() { if (mPlayer->getOnBorder()) { const std::string room_name = mRoom->getRoom(mPlayer->getBorder()); if (changeRoom(room_name)) { mPlayer->switchBorders(); mCurrentRoom = room_name; mSpawnPoint = mPlayer->getSpawnParams(); } } } // Comprueba si el jugador esta sobre el suelo void Game::checkPlayerOnFloor() { // Comprueba si tiene suelo bajo los pies solo cuando no hay velocidad de subida // y solo cuando el pie este encima de un bloque, es decir, en multiplos de 8 // *** HAY UN POSIBLE PROBLEMA y es que caiga muy rapido y viaje a mas de un pixel de velocidad, // con lo que se saltaria la comprobación // *** POSIBLE SOLUCION. Comprobar si el tile actual del pie es diferente al tile del pie previo. // Esto indica que se ha saltado la comprobacion cada 8 pixeles. // En este caso habría que recolocar al jugador en el sitio // *** PARECE RESUELTO const int a = (mPlayer->lastPosition.y + 16) / 8; const int b = mPlayer->getLeftFoot().y / 8; const bool tile_change = a != b; const bool is_not_going_up = mPlayer->getVelY() >= 0; const bool is_tile_aligned = mPlayer->getLeftFoot().y % 8 == 0; if (((is_not_going_up) && (is_tile_aligned)) || ((is_not_going_up) && (tile_change))) { bool test = false; test |= (mRoom->getTile(mPlayer->getLeftFoot()) == TILE_SOLID); test |= (mRoom->getTile(mPlayer->getRightFoot()) == TILE_SOLID); test |= (mRoom->getTile(mPlayer->getLeftFoot()) == TILE_TRAVESSABLE); test |= (mRoom->getTile(mPlayer->getRightFoot()) == TILE_TRAVESSABLE); // Tiene uno de los pies sobre una superficie if (test) { mPlayer->setStatus(STATUS_STANDING); // Si ha habido un cambio de tile, hay que recolocarlo if (tile_change) { int offset = (int)mPlayer->sprite->getPosY() % 8; mPlayer->sprite->setPosY((int)mPlayer->sprite->getPosY() - offset); } } // Tiene ambos pies sobre el vacío else if (mPlayer->getStatus() != STATUS_JUMPING) { mPlayer->setStatus(STATUS_FALLING); } } } // Comprueba que el jugador no atraviese ninguna pared void Game::checkPlayerAndWalls() { // Obtiene los ocho puntos de colisión del jugador const SDL_Rect rect = mPlayer->getRect(); const SDL_Point p1 = {rect.x, rect.y}; const SDL_Point p2 = {rect.x + 7, rect.y}; const SDL_Point p3 = {rect.x + 7, rect.y + 7}; const SDL_Point p4 = {rect.x, rect.y + 7}; const SDL_Point p5 = {rect.x, rect.y + 8}; const SDL_Point p6 = {rect.x + 7, rect.y + 8}; const SDL_Point p7 = {rect.x + 7, rect.y + 15}; const SDL_Point p8 = {rect.x, rect.y + 15}; // Comprueba si ha colisionado con un muro bool wall = false; wall |= (mRoom->getTile(p1) == TILE_SOLID); wall |= (mRoom->getTile(p2) == TILE_SOLID); wall |= (mRoom->getTile(p3) == TILE_SOLID); wall |= (mRoom->getTile(p4) == TILE_SOLID); wall |= (mRoom->getTile(p5) == TILE_SOLID); wall |= (mRoom->getTile(p6) == TILE_SOLID); wall |= (mRoom->getTile(p7) == TILE_SOLID); wall |= (mRoom->getTile(p8) == TILE_SOLID); if (wall) { // Si hay colisión, deshace el movimiento y lo pone en modo caída mPlayer->undoLastMove(); mPlayer->setStatus(STATUS_FALLING); } // Comprueba si ha colisionado con un tile de los que matan al jugador bool death = false; death |= (mRoom->getTile(p1) == TILE_KILL); death |= (mRoom->getTile(p2) == TILE_KILL); death |= (mRoom->getTile(p3) == TILE_KILL); death |= (mRoom->getTile(p4) == TILE_KILL); death |= (mRoom->getTile(p5) == TILE_KILL); death |= (mRoom->getTile(p6) == TILE_KILL); death |= (mRoom->getTile(p7) == TILE_KILL); death |= (mRoom->getTile(p8) == TILE_KILL); if (death) { killPlayer(); } } // Comprueba las colisiones del jugador con los enemigos bool Game::checkPlayerAndEnemies() { const bool death = mRoom->enemyCollision(mPlayer->getCollider()); if (death) { killPlayer(); } return death; } // Comprueba las colisiones del jugador con los objetos void Game::checkPlayerAndItems() { mRoom->itemCollision(mPlayer->getCollider()); } // Mata al jugador void Game::killPlayer() { // Destruye la habitacion y el jugador delete mRoom; delete mPlayer; // Crea la nueva habitación y el nuevo jugador mRoom = new Room(mAsset->get(mCurrentRoom), mRenderer, mAsset, mItemTracker); mPlayer = new Player(mSpawnPoint, mAsset->get("player01.png"), mRenderer, mAsset, mInput, mRoom); }