#include "game/scenes/game.hpp" #include #include // Para std::sqrt, std::min #include #include // Para vector #include "core/audio/audio.hpp" // Para Audio #include "core/input/global_inputs.hpp" // Para check #include "core/input/input.hpp" // Para Input, InputAction, Input::DO_NOT_ALLOW_REPEAT #include "core/rendering/screen.hpp" // Para Screen #include "core/rendering/surface.hpp" // Para Surface #include "core/rendering/text.hpp" // Para Text, Text::CENTER_FLAG, Text::COLOR_FLAG #include "core/resources/resource_cache.hpp" // Para ResourceRoom, Resource #include "core/resources/resource_list.hpp" // Para Asset #include "core/system/global_events.hpp" // Para check #include "game/gameplay/cheevos.hpp" // Para Cheevos #include "game/gameplay/item_tracker.hpp" // Para ItemTracker #include "game/gameplay/room.hpp" // Para Room, RoomData #include "game/gameplay/room_tracker.hpp" // Para RoomTracker #include "game/gameplay/scoreboard.hpp" // Para Scoreboard::Data, Scoreboard #include "game/gameplay/stats.hpp" // Para Stats #include "game/options.hpp" // Para Options, options, Cheat, SectionState #include "game/scene_manager.hpp" // Para SceneManager #include "game/ui/notifier.hpp" // Para Notifier, NotificationText, CHEEVO_NO... #include "utils/defines.hpp" // Para Tile::SIZE, PlayArea::HEIGHT, RoomBorder::BOTTOM #include "utils/utils.hpp" // Para PaletteColor, stringToColor #ifdef _DEBUG #include "core/system/debug.hpp" // Para Debug #endif // Constructor Game::Game(Mode mode) : board_(std::make_shared(0, 9, 0, true, 0, SDL_GetTicks(), Options::cheats.jail_is_open == Options::Cheat::State::ENABLED)), scoreboard_(std::make_shared(board_)), room_tracker_(std::make_shared()), stats_(std::make_shared(Resource::List::get()->get("stats.csv"), Resource::List::get()->get("stats_buffer.csv"))), mode_(mode), #ifdef _DEBUG current_room_("03.yaml"), spawn_data_(Player::SpawnData(25 * Tile::SIZE, 13 * Tile::SIZE, 0, 0, 0, Player::State::ON_GROUND, Flip::LEFT)) #else current_room_("03.yaml"), spawn_data_(Player::SpawnData(25 * Tile::SIZE, 13 * Tile::SIZE, 0, 0, 0, Player::State::ON_GROUND, Flip::LEFT)) #endif { // Crea objetos e inicializa variables ItemTracker::init(); demoInit(); room_ = std::make_shared(current_room_, board_); initPlayer(spawn_data_, room_); initStats(); total_items_ = getTotalItems(); createRoomNameTexture(); game_backbuffer_surface_ = std::make_shared(Options::game.width, Options::game.height); changeRoom(current_room_); Cheevos::get()->enable(!Options::cheats.enabled()); // Deshabilita los logros si hay trucos activados Cheevos::get()->clearUnobtainableState(); SceneManager::current = (mode_ == Mode::GAME) ? SceneManager::Scene::GAME : SceneManager::Scene::DEMO; SceneManager::options = SceneManager::Options::NONE; } Game::~Game() { ItemTracker::destroy(); } // Comprueba los eventos de la cola void Game::handleEvents() { SDL_Event event; while (SDL_PollEvent(&event)) { GlobalEvents::handle(event); #ifdef _DEBUG handleDebugEvents(event); #endif } } // Comprueba el teclado void Game::handleInput() { Input::get()->update(); // Inputs globales siempre funcionan if (Input::get()->checkAction(InputAction::TOGGLE_MUSIC, Input::DO_NOT_ALLOW_REPEAT)) { board_->music = !board_->music; board_->music ? Audio::get()->resumeMusic() : Audio::get()->pauseMusic(); Notifier::get()->show({"MUSIC " + std::string(board_->music ? "ENABLED" : "DISABLED")}); } // Durante fade/postfade, solo procesar inputs globales if (state_ != State::PLAYING) { GlobalInputs::handle(); return; } // Input de pausa solo en estado PLAYING if (Input::get()->checkAction(InputAction::PAUSE, Input::DO_NOT_ALLOW_REPEAT)) { togglePause(); Notifier::get()->show({std::string(paused_ ? "GAME PAUSED" : "GAME RUNNING")}); } GlobalInputs::handle(); } // Bucle para el juego void Game::run() { keepMusicPlaying(); if (!board_->music && mode_ == Mode::GAME) { Audio::get()->pauseMusic(); } while (SceneManager::current == SceneManager::Scene::GAME || SceneManager::current == SceneManager::Scene::DEMO) { update(); render(); } if (mode_ == Mode::GAME) { Audio::get()->stopMusic(); } } // Actualiza el juego, las variables, comprueba la entrada, etc. void Game::update() { const float DELTA_TIME = delta_timer_.tick(); handleEvents(); // Comprueba los eventos handleInput(); // Comprueba las entradas #ifdef _DEBUG Debug::get()->clear(); #endif // Dispatch por estado switch (state_) { case State::PLAYING: updatePlaying(DELTA_TIME); break; case State::BLACK_SCREEN: updateBlackScreen(DELTA_TIME); break; case State::GAME_OVER: updateGameOver(DELTA_TIME); break; case State::FADE_TO_ENDING: updateFadeToEnding(DELTA_TIME); break; case State::POST_FADE_ENDING: updatePostFadeEnding(DELTA_TIME); break; } Audio::update(); // Actualiza el objeto Audio Screen::get()->update(DELTA_TIME); // Actualiza el objeto Screen #ifdef _DEBUG updateDebugInfo(); #endif } // Actualiza el juego en estado PLAYING void Game::updatePlaying(float delta_time) { // Actualiza los objetos room_->update(delta_time); switch (mode_) { case Mode::GAME: #ifdef _DEBUG // Maneja el arrastre del jugador con el ratón (debug) handleDebugMouseDrag(delta_time); // Si estamos arrastrando, no ejecutar la física normal del jugador if (!debug_dragging_player_) { player_->update(delta_time); } #else player_->update(delta_time); #endif checkPlayerIsOnBorder(); checkPlayerAndItems(); checkPlayerAndEnemies(); checkIfPlayerIsAlive(); checkEndGame(); checkRestoringJail(delta_time); checkSomeCheevos(); break; case Mode::DEMO: demoCheckRoomChange(delta_time); break; } scoreboard_->update(delta_time); keepMusicPlaying(); } // Actualiza el juego en estado BLACK_SCREEN void Game::updateBlackScreen(float delta_time) { state_time_ += delta_time; // Si se acabaron las vidas Y pasó el threshold → GAME_OVER if (board_->lives < 0 && state_time_ > GAME_OVER_THRESHOLD) { transitionToState(State::GAME_OVER); return; } // Si pasó la duración completa → volver a PLAYING if (state_time_ > BLACK_SCREEN_DURATION) { // Despausar al salir player_->setPaused(false); room_->setPaused(false); Screen::get()->setBorderColor(room_->getBorderColor()); transitionToState(State::PLAYING); } } // Actualiza el juego en estado GAME_OVER void Game::updateGameOver(float delta_time) { // Pequeño delay antes de cambiar escena state_time_ += delta_time; if (state_time_ > 0.1F) { // 100ms de delay mínimo SceneManager::current = SceneManager::Scene::GAME_OVER; } } // Actualiza el juego en estado FADE_TO_ENDING void Game::updateFadeToEnding(float delta_time) { // Actualiza room, enemies, items (todo sigue funcionando) room_->update(delta_time); // NO actualizar player (congelar movimiento) // player_->update(delta_time); -- COMENTADO INTENCIONALMENTE // Actualiza scoreboard scoreboard_->update(delta_time); keepMusicPlaying(); // Aplica el fade progresivo al BACKBUFFER (no al renderer de pantalla) fade_accumulator_ += delta_time; if (fade_accumulator_ >= FADE_STEP_INTERVAL) { fade_accumulator_ = 0.0F; if (game_backbuffer_surface_->fadeSubPalette()) { // Fade completado, transicionar a POST_FADE transitionToState(State::POST_FADE_ENDING); } } } // Actualiza el juego en estado POST_FADE_ENDING void Game::updatePostFadeEnding(float delta_time) { // Pantalla negra estática, acumular tiempo state_time_ += delta_time; // Después del delay, cambiar a la escena de ending if (state_time_ >= POST_FADE_DELAY) { SceneManager::current = SceneManager::Scene::ENDING; } } // Cambia al estado especificado y resetea los timers void Game::transitionToState(State new_state) { // Lógica de ENTRADA según el nuevo estado if (new_state == State::BLACK_SCREEN) { // Respawn room y player room_ = std::make_shared(current_room_, board_); initPlayer(spawn_data_, room_); // Pausar ambos room_->setPaused(true); player_->setPaused(true); } state_ = new_state; state_time_ = 0.0F; fade_accumulator_ = 0.0F; } // Pinta los objetos en pantalla void Game::render() { // Dispatch por estado switch (state_) { case State::PLAYING: renderPlaying(); break; case State::BLACK_SCREEN: renderBlackScreen(); break; case State::GAME_OVER: renderGameOver(); break; case State::FADE_TO_ENDING: renderFadeToEnding(); break; case State::POST_FADE_ENDING: renderPostFadeEnding(); break; } } // Renderiza el juego en estado PLAYING (directo a pantalla) void Game::renderPlaying() { // Prepara para dibujar el frame Screen::get()->start(); // Dibuja los elementos del juego en orden room_->renderMap(); room_->renderEnemies(); room_->renderItems(); if (mode_ == Mode::GAME) { player_->render(); } renderRoomName(); scoreboard_->render(); #ifdef _DEBUG // Debug info renderDebugInfo(); #endif // Actualiza la pantalla Screen::get()->render(); } // Renderiza el juego en estado BLACK_SCREEN (pantalla negra) void Game::renderBlackScreen() { Screen::get()->start(); auto const COLOR = static_cast(PaletteColor::BLACK); Screen::get()->clearSurface(COLOR); Screen::get()->setBorderColor(COLOR); Screen::get()->render(); } // Renderiza el juego en estado GAME_OVER (pantalla negra) void Game::renderGameOver() { Screen::get()->start(); Screen::get()->clearSurface(static_cast(PaletteColor::BLACK)); Screen::get()->render(); } // Renderiza el juego en estado FADE_TO_ENDING (via backbuffer) void Game::renderFadeToEnding() { // 1. Guardar renderer actual auto previous_renderer = Screen::get()->getRendererSurface(); // 2. Cambiar target a backbuffer Screen::get()->setRendererSurface(game_backbuffer_surface_); // 3. Renderizar todo a backbuffer game_backbuffer_surface_->clear(static_cast(PaletteColor::BLACK)); room_->renderMap(); room_->renderEnemies(); room_->renderItems(); player_->render(); // Player congelado pero visible renderRoomName(); scoreboard_->render(); // 4. Restaurar renderer original Screen::get()->setRendererSurface(previous_renderer); // 5. Preparar pantalla y volcar backbuffer (fade YA aplicado en update) Screen::get()->start(); game_backbuffer_surface_->render(); Screen::get()->render(); } // Renderiza el juego en estado POST_FADE_ENDING (pantalla negra) void Game::renderPostFadeEnding() { Screen::get()->start(); Screen::get()->clearSurface(static_cast(PaletteColor::BLACK)); Screen::get()->render(); } #ifdef _DEBUG // Pasa la información de debug void Game::updateDebugInfo() { // Debug::get()->add("X = " + std::to_string(static_cast(player_->x_)) + ", Y = " + std::to_string(static_cast(player_->y_))); // Debug::get()->add("VX = " + std::to_string(player_->vx_).substr(0, 4) + ", VY = " + std::to_string(player_->vy_).substr(0, 4)); // Debug::get()->add("STATE = " + std::to_string(static_cast(player_->state_))); } // Pone la información de debug en pantalla void Game::renderDebugInfo() { if (!Debug::get()->isEnabled()) { return; } auto surface = Screen::get()->getRendererSurface(); // Borra el marcador SDL_FRect rect = {0, 18 * Tile::SIZE, PlayArea::WIDTH, GameCanvas::HEIGHT - PlayArea::HEIGHT}; surface->fillRect(&rect, static_cast(PaletteColor::BLACK)); // Pinta la rejilla /*for (int i = 0; i < PlayArea::BOTTOM; i += 8) { // Lineas horizontales surface->drawLine(0, i, PlayArea::RIGHT, i, static_cast(PaletteColor::BRIGHT_BLACK)); } for (int i = 0; i < PlayArea::RIGHT; i += 8) { // Lineas verticales surface->drawLine(i, 0, i, PlayArea::BOTTOM - 1, static_cast(PaletteColor::BRIGHT_BLACK)); }*/ // Pinta el texto Debug::get()->setPos({1, 18 * 8}); Debug::get()->render(); } // Comprueba los eventos void Game::handleDebugEvents(const SDL_Event& event) { if (event.type == SDL_EVENT_KEY_DOWN && static_cast(event.key.repeat) == 0) { switch (event.key.key) { case SDLK_F12: Debug::get()->toggleEnabled(); Notifier::get()->show({"DEBUG " + std::string(Debug::get()->isEnabled() ? "ENABLED" : "DISABLED")}); room_->redrawMap(); // Redibuja el tilemap para mostrar/ocultar líneas de colisión Options::cheats.invincible = static_cast(Debug::get()->isEnabled()); player_->setColor(); board_->music = !Debug::get()->isEnabled(); board_->music ? Audio::get()->resumeMusic() : Audio::get()->pauseMusic(); break; case SDLK_R: Resource::Cache::get()->reload(); break; case SDLK_W: changeRoom(room_->getRoom(Room::Border::TOP)); break; case SDLK_A: changeRoom(room_->getRoom(Room::Border::LEFT)); break; case SDLK_S: changeRoom(room_->getRoom(Room::Border::BOTTOM)); break; case SDLK_D: changeRoom(room_->getRoom(Room::Border::RIGHT)); break; case SDLK_7: Notifier::get()->show({"ACHIEVEMENT UNLOCKED!", "I LIKE MY MULTICOLOURED FRIENDS"}, Notifier::Style::CHEEVO, -1, false, "F7"); break; default: break; } } } // Maneja el arrastre del jugador con el ratón (debug) void Game::handleDebugMouseDrag(float delta_time) { // Solo funciona si Debug está habilitado if (!Debug::get()->isEnabled()) { return; } // Obtener estado del ratón (coordenadas de ventana física) float mouse_x = 0.0F; float mouse_y = 0.0F; SDL_MouseButtonFlags buttons = SDL_GetMouseState(&mouse_x, &mouse_y); // Convertir coordenadas de ventana a coordenadas lógicas del renderer float render_x = 0.0F; float render_y = 0.0F; SDL_RenderCoordinatesFromWindow(Screen::get()->getRenderer(), mouse_x, mouse_y, &render_x, &render_y); // Restar el offset del borde para obtener coordenadas del área de juego SDL_FRect dst_rect = Screen::get()->getGameSurfaceDstRect(); float game_x = render_x - dst_rect.x; float game_y = render_y - dst_rect.y; // Verificar si los botones están presionados bool left_button_pressed = (buttons & SDL_BUTTON_LMASK) != 0; bool right_button_pressed = (buttons & SDL_BUTTON_RMASK) != 0; // Botón derecho: teleport instantáneo a la posición del cursor if (right_button_pressed && !debug_dragging_player_) { player_->setDebugPosition(game_x, game_y); player_->finalizeDebugTeleport(); } else if (left_button_pressed) { // Obtener posición actual del jugador SDL_FRect player_rect = player_->getRect(); float player_x = player_rect.x; float player_y = player_rect.y; // Calcular distancia al objetivo float dx = game_x - player_x; float dy = game_y - player_y; float distance = std::sqrt(dx * dx + dy * dy); // Constantes de velocidad con ease-in (aceleración progresiva) constexpr float DRAG_SPEED_MIN = 30.0F; // Velocidad inicial (pixels/segundo) constexpr float DRAG_SPEED_MAX = 600.0F; // Velocidad máxima (pixels/segundo) constexpr float DRAG_ACCELERATION = 600.0F; // Aceleración (pixels/segundo²) // Incrementar velocidad con el tiempo (ease-in) if (!debug_dragging_player_) { debug_drag_speed_ = DRAG_SPEED_MIN; // Iniciar con velocidad mínima } debug_drag_speed_ = std::min(DRAG_SPEED_MAX, debug_drag_speed_ + DRAG_ACCELERATION * delta_time); if (distance > 1.0F) { // Calcular el movimiento con la velocidad actual float move_factor = std::min(1.0F, debug_drag_speed_ * delta_time / distance); float new_x = player_x + dx * move_factor; float new_y = player_y + dy * move_factor; // Mover el jugador hacia la posición del cursor player_->setDebugPosition(new_x, new_y); } debug_dragging_player_ = true; Debug::get()->add(std::string("X : " + std::to_string(static_cast(player_rect.x)))); Debug::get()->add(std::string("Y : " + std::to_string(static_cast(player_rect.y)))); } else if (debug_dragging_player_) { // Botón soltado después de arrastrar: finalizar teleport player_->finalizeDebugTeleport(); debug_dragging_player_ = false; debug_drag_speed_ = 0.0F; // Reset para el próximo arrastre } } #endif // Escribe el nombre de la pantalla void Game::renderRoomName() { // Dibuja la textura con el nombre de la habitación room_name_surface_->render(nullptr, &room_name_rect_); } // Cambia de habitación auto Game::changeRoom(const std::string& room_path) -> bool { // En las habitaciones los limites tienen la cadena del fichero o un 0 en caso de no limitar con nada if (room_path == "0") { return false; } // Verifica que exista el fichero que se va a cargar if (!Resource::List::get()->get(room_path).empty()) { // Crea un objeto habitación nuevo a partir del fichero room_ = std::make_shared(room_path, board_); // Pone el nombre de la habitación en la textura fillRoomNameTexture(); // Pone el color del marcador en función del color del borde de la habitación setScoreBoardColor(); if (room_tracker_->addRoom(room_path)) { // Incrementa el contador de habitaciones visitadas board_->rooms++; Options::stats.rooms = board_->rooms; // Actualiza las estadisticas stats_->addVisit(room_->getName()); } // Pasa la nueva habitación al jugador player_->setRoom(room_); // Cambia la habitación actual current_room_ = room_path; return true; } return false; } // Comprueba si el jugador esta en el borde de la pantalla void Game::checkPlayerIsOnBorder() { if (player_->isOnBorder()) { const auto BORDER = player_->getBorder(); const auto ROOM_NAME = room_->getRoom(BORDER); // Si puede cambiar de habitación, cambia if (changeRoom(ROOM_NAME)) { player_->switchBorders(); spawn_data_ = player_->getSpawnParams(); return; } // Si ha llegado al fondo y no hay habitación, muere if (BORDER == Room::Border::BOTTOM) { killPlayer(); return; } } } // Comprueba las colisiones del jugador con los enemigos auto Game::checkPlayerAndEnemies() -> bool { 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()); } // Comprueba si el jugador esta vivo void Game::checkIfPlayerIsAlive() { if (!player_->isAlive()) { killPlayer(); } } // Mata al jugador void Game::killPlayer() { if (Options::cheats.invincible == Options::Cheat::State::ENABLED) { return; } // Resta una vida al jugador if (Options::cheats.infinite_lives == Options::Cheat::State::DISABLED) { --board_->lives; } // Actualiza las estadisticas stats_->addDeath(room_->getName()); // Invalida el logro de pasarse el juego sin morir Cheevos::get()->setUnobtainable(11); // Sonido Audio::get()->playSound("death.wav", Audio::Group::GAME); // Transicionar al estado BLACK_SCREEN (el respawn ocurre en transitionToState) transitionToState(State::BLACK_SCREEN); } // Pone el color del marcador en función del color del borde de la habitación void Game::setScoreBoardColor() { // Obtiene el color del borde const Uint8 BORDER_COLOR = room_->getBorderColor(); const bool IS_BLACK = BORDER_COLOR == static_cast(PaletteColor::BLACK); const bool IS_BRIGHT_BLACK = BORDER_COLOR == static_cast(PaletteColor::BRIGHT_BLACK); // Si el color del borde es negro o negro brillante cambia el texto del marcador a blanco board_->color = IS_BLACK || IS_BRIGHT_BLACK ? static_cast(PaletteColor::WHITE) : BORDER_COLOR; } // Comprueba si ha finalizado el juego auto Game::checkEndGame() -> bool { const bool IS_ON_THE_ROOM = room_->getName() == "THE JAIL"; // Estar en la habitación que toca const bool HAVE_THE_ITEMS = board_->items >= int(total_items_ * 0.9F) || Options::cheats.jail_is_open == Options::Cheat::State::ENABLED; // Con mas del 90% de los items recogidos const bool IS_ON_THE_DOOR = player_->getRect().x <= 128; // Y en la ubicación que toca (En la puerta) if (HAVE_THE_ITEMS) { board_->jail_is_open = true; } if (HAVE_THE_ITEMS && IS_ON_THE_ROOM && IS_ON_THE_DOOR) { // Comprueba los logros de completar el juego checkEndGameCheevos(); // Iniciar transición de fade en vez de cambio inmediato de escena transitionToState(State::FADE_TO_ENDING); Audio::get()->fadeOutMusic(1000); // Fade out de música en 1 segundo return true; } return false; } // Obtiene la cantidad total de items que hay en el mapeado del juego auto Game::getTotalItems() -> int { int items = 0; auto rooms = Resource::Cache::get()->getRooms(); for (const auto& room : rooms) { items += room.room->items.size(); } return items; } // Pone el juego en pausa void Game::togglePause() { paused_ = !paused_; player_->setPaused(paused_); room_->setPaused(paused_); scoreboard_->setPaused(paused_); } // Da vidas al jugador cuando está en la Jail void Game::checkRestoringJail(float delta_time) { if (room_->getName() != "THE JAIL" || board_->lives == 9) { jail_restore_time_ = 0.0F; // Reset timer cuando no está en la Jail return; } if (!paused_) { jail_restore_time_ += delta_time; } // Incrementa el numero de vidas if (jail_restore_time_ >= JAIL_RESTORE_INTERVAL) { jail_restore_time_ -= JAIL_RESTORE_INTERVAL; // Mantiene el excedente para precisión board_->lives++; Audio::get()->playSound("death.wav", Audio::Group::GAME); // Invalida el logro de completar el juego sin entrar a la jail const bool HAVE_THE_ITEMS = board_->items >= int(total_items_ * 0.9F); if (!HAVE_THE_ITEMS) { Cheevos::get()->setUnobtainable(9); } } } // Inicializa el diccionario de las estadísticas void Game::initStats() { auto list = Resource::Cache::get()->getRooms(); for (const auto& room : list) { stats_->addDictionary(room.room->number, room.room->name); } stats_->init(); } // Crea la textura con el nombre de la habitación void Game::fillRoomNameTexture() { // Pone la textura como destino de renderizado auto previuos_renderer = Screen::get()->getRendererSurface(); Screen::get()->setRendererSurface(room_name_surface_); // Rellena la textura de color room_name_surface_->clear(stringToColor("white")); // Escribe el texto en la textura auto text = Resource::Cache::get()->getText("smb2"); text->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, GameCanvas::CENTER_X, text->getCharacterSize() / 2, room_->getName(), 1, room_->getBGColor()); // Deja el renderizador por defecto Screen::get()->setRendererSurface(previuos_renderer); } // Comprueba algunos logros void Game::checkSomeCheevos() { auto* cheevos = Cheevos::get(); // Logros sobre la cantidad de items if (board_->items == total_items_) { cheevos->unlock(4); cheevos->unlock(3); cheevos->unlock(2); cheevos->unlock(1); } else if (board_->items >= total_items_ * 0.75F) { cheevos->unlock(3); cheevos->unlock(2); cheevos->unlock(1); } else if (board_->items >= total_items_ * 0.5F) { cheevos->unlock(2); cheevos->unlock(1); } else if (board_->items >= total_items_ * 0.25F) { cheevos->unlock(1); } // Logros sobre las habitaciones visitadas if (board_->rooms >= 60) { cheevos->unlock(7); cheevos->unlock(6); cheevos->unlock(5); } else if (board_->rooms >= 40) { cheevos->unlock(6); cheevos->unlock(5); } else if (board_->rooms >= 20) { cheevos->unlock(5); } } // Comprueba los logros de completar el juego void Game::checkEndGameCheevos() { auto* cheevos = Cheevos::get(); // "Complete the game" cheevos->unlock(8); // "Complete the game without entering the jail" cheevos->unlock(9); // "Complete the game with all items" if (board_->items == total_items_) { cheevos->unlock(10); } // "Complete the game without dying" cheevos->unlock(11); // "Complete the game in under 30 minutes" if (scoreboard_->getMinutes() < 30) { cheevos->unlock(12); } } // Inicializa al jugador void Game::initPlayer(const Player::SpawnData& spawn_point, std::shared_ptr room) { std::string player_animations = Options::cheats.alternate_skin == Options::Cheat::State::ENABLED ? "player2.yaml" : "player.yaml"; const Player::Data PLAYER{.spawn_data = spawn_point, .animations_path = player_animations, .room = std::move(room)}; player_ = std::make_shared(PLAYER); } // Crea la textura para poner el nombre de la habitación void Game::createRoomNameTexture() { auto text = Resource::Cache::get()->getText("smb2"); room_name_surface_ = std::make_shared(Options::game.width, text->getCharacterSize() * 2); // Establece el destino de la textura room_name_rect_ = {.x = 0.0F, .y = PlayArea::HEIGHT, .w = Options::game.width, .h = text->getCharacterSize() * 2.0F}; } // Hace sonar la música void Game::keepMusicPlaying() { const std::string MUSIC_PATH = mode_ == Mode::GAME ? "game.ogg" : "title.ogg"; // Si la música no está sonando if (Audio::get()->getMusicState() == Audio::MusicState::STOPPED) { Audio::get()->playMusic(MUSIC_PATH); } } // DEMO MODE: Inicializa las variables para el modo demo void Game::demoInit() { if (mode_ == Mode::DEMO) { demo_ = DemoData(0.0F, 0, {"04.yaml", "54.yaml", "20.yaml", "09.yaml", "05.yaml", "11.yaml", "31.yaml", "44.yaml"}); current_room_ = demo_.rooms.front(); } } // DEMO MODE: Comprueba si se ha de cambiar de habitación void Game::demoCheckRoomChange(float delta_time) { if (mode_ == Mode::DEMO) { demo_.time_accumulator += delta_time; if (demo_.time_accumulator >= DEMO_ROOM_DURATION) { demo_.time_accumulator = 0.0F; demo_.room_index++; if (demo_.room_index == (int)demo_.rooms.size()) { SceneManager::current = SceneManager::Scene::LOGO; SceneManager::options = SceneManager::Options::LOGO_TO_TITLE; } else { changeRoom(demo_.rooms[demo_.room_index]); } } } }