#include "game.h" // Constructor Game::Game(SDL_Renderer *renderer, Screen *screen, Asset *asset, Input *input) { // Inicia variables currentRoom = "01.room"; spawnPoint = {2 * 8, 12 * 8, 0, 0, 0, STATUS_STANDING, SDL_FLIP_NONE}; debug = false; // Copia los punteros this->renderer = renderer; this->asset = asset; this->screen = screen; this->input = input; // Crea los objetos itemTracker = new ItemTracker(); room = new Room(asset->get(currentRoom), renderer, asset, itemTracker); player = new Player(spawnPoint, asset->get("player01.png"), asset->get("player01.ani"), renderer, asset, input, room); eventHandler = new SDL_Event(); text = new Text(asset->get("smb2.png"), asset->get("smb2.txt"), renderer); // Inicializa variables ticks = 0; ticksSpeed = 15; section.name = SECTION_PROG_GAME; section.subsection = SUBSECTION_GAME_PLAY; } Game::~Game() { // Borra las referencias a los punteros renderer = nullptr; asset = nullptr; input = nullptr; // Libera la memoria de los objetos delete itemTracker; itemTracker = nullptr; delete room; room = nullptr; delete player; player = nullptr; delete eventHandler; eventHandler = nullptr; delete text; text = nullptr; } // Bucle para el juego section_t Game::run() { while (section.name == SECTION_PROG_GAME) { // Sección juego jugando if (section.subsection == SUBSECTION_GAME_PLAY) { update(); render(); } } return section; } // 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() - ticks > ticksSpeed) { // Actualiza el contador de ticks ticks = SDL_GetTicks(); // Comprueba los eventos que hay en la cola while (SDL_PollEvent(eventHandler) != 0) { // Evento de salida de la aplicación if (eventHandler->type == SDL_QUIT) { section.name = SECTION_PROG_QUIT; break; } } room->update(); player->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::render() { // Prepara para dibujar el frame screen->start(); screen->clean(room->getBGColor()); room->renderMap(); room->renderEnemies(); room->renderItems(); player->render(); // 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(renderer, color.r, color.g, color.b, 0xFF); SDL_RenderFillRect(renderer, &rect); text->writeCentered(GAMECANVAS_CENTER_X, 16 * 8, room->getName()); // Debug info if (debug) { std::string text; text = "status: " + std::to_string(player->status); this->text->write(0, 17 * 8, text); text = "foot: " + std::to_string((int)player->getLeftFoot().y); this->text->write(0, 18 * 8, text); const int a = (player->lastPosition.y + 16) / 8; const int b = player->getLeftFoot().y / 8; text = "tile: " + std::to_string(a) + " - " + std::to_string(b); this->text->write(0, 19 * 8, text); const bool collision = checkPlayerAndEnemies(); text = "collision: " + std::to_string(collision); this->text->write(0, 20 * 8, text); } // Actualiza la pantalla screen->blit(); } // Comprueba la entrada void Game::checkInput() { /* if (input->checkInput(INPUT_UP, REPEAT_FALSE)) changeRoom(room->getRoomUp()); if (input->checkInput(INPUT_DOWN, REPEAT_FALSE)) changeRoom(room->getRoomDown()); if (input->checkInput(INPUT_LEFT, REPEAT_FALSE)) changeRoom(room->getRoomLeft()); if (input->checkInput(INPUT_RIGHT, REPEAT_FALSE)) changeRoom(room->getRoomRight()); */ if (input->checkInput(INPUT_BUTTON_2, REPEAT_FALSE)) debug = !debug; } // 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 (asset->get(file) != "") { // Elimina la habitación actual delete room; room = nullptr; // Crea un objeto habitación nuevo a partir del fichero room = new Room(asset->get(file), renderer, asset, itemTracker); success = true; } return success; } // Comprueba si el jugador esta en el borde de la pantalla void Game::checkPlayerOnBorder() { if (player->getOnBorder()) { const std::string room_name = room->getRoom(player->getBorder()); if (changeRoom(room_name)) { player->switchBorders(); currentRoom = room_name; spawnPoint = player->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 = (player->lastPosition.y + 16) / 8; const int b = player->getLeftFoot().y / 8; const bool tile_change = a != b; const bool is_not_going_up = player->getVelY() >= 0; const bool is_tile_aligned = player->getLeftFoot().y % 8 == 0; if (((is_not_going_up) && (is_tile_aligned)) || ((is_not_going_up) && (tile_change))) { bool test = false; test |= (room->getTile(player->getLeftFoot()) == TILE_SOLID); test |= (room->getTile(player->getRightFoot()) == TILE_SOLID); test |= (room->getTile(player->getLeftFoot()) == TILE_TRAVESSABLE); test |= (room->getTile(player->getRightFoot()) == TILE_TRAVESSABLE); // Tiene uno de los pies sobre una superficie if (test) { player->setStatus(STATUS_STANDING); // Si ha habido un cambio de tile, hay que recolocarlo if (tile_change) { int offset = (int)player->sprite->getPosY() % 8; player->sprite->setPosY((int)player->sprite->getPosY() - offset); } } // Tiene ambos pies sobre el vacío else if (player->getStatus() != STATUS_JUMPING) { player->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 = player->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 |= (room->getTile(p1) == TILE_SOLID); wall |= (room->getTile(p2) == TILE_SOLID); wall |= (room->getTile(p3) == TILE_SOLID); wall |= (room->getTile(p4) == TILE_SOLID); wall |= (room->getTile(p5) == TILE_SOLID); wall |= (room->getTile(p6) == TILE_SOLID); wall |= (room->getTile(p7) == TILE_SOLID); wall |= (room->getTile(p8) == TILE_SOLID); if (wall) { // Si hay colisión, deshace el movimiento y lo pone en modo caída player->undoLastMove(); player->setStatus(STATUS_FALLING); } // Comprueba si ha colisionado con un tile de los que matan al jugador bool death = false; death |= (room->getTile(p1) == TILE_KILL); death |= (room->getTile(p2) == TILE_KILL); death |= (room->getTile(p3) == TILE_KILL); death |= (room->getTile(p4) == TILE_KILL); death |= (room->getTile(p5) == TILE_KILL); death |= (room->getTile(p6) == TILE_KILL); death |= (room->getTile(p7) == TILE_KILL); death |= (room->getTile(p8) == TILE_KILL); if (death) { killPlayer(); } } // Comprueba las colisiones del jugador con los enemigos bool Game::checkPlayerAndEnemies() { const bool death = room->enemyCollision(player->getCollider()); if (death) { killPlayer(); } return death; } // Comprueba las colisiones del jugador con los objetos void Game::checkPlayerAndItems() { room->itemCollision(player->getCollider()); } // Mata al jugador void Game::killPlayer() { // Destruye la habitacion y el jugador delete room; delete player; // Crea la nueva habitación y el nuevo jugador room = new Room(asset->get(currentRoom), renderer, asset, itemTracker); player = new Player(spawnPoint, asset->get("player01.png"), asset->get("player01.ani"), renderer, asset, input, room); }