Files
aee-arcade/source/game/scenes/game_scene.cpp
T
JailDesigner 505e819af1 HC-06: rutes d'assets de GameScene a Defaults::Pyramid + Config
Afegits `Defaults::Pyramid::{MAP_YAML, PLAYER_SPRITE, PLAYER_YAML,
MUSIC_TRACK}` a utils/defaults.hpp. `GameScene` exposa `Config` i
`defaultConfig()`; el ctor accepta Config (default = Defaults::Pyramid)
i usa els camps enlloc dels literals. `Player` passa a rebre el anim_key
per constructor en lloc de cablejar "player.yaml". Cache key = basename
(convenció preexistent). Cada piràmide futura serà un Config distint.
2026-04-20 17:52:53 +02:00

131 lines
5.4 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "game/scenes/game_scene.hpp"
#include <SDL3/SDL.h>
#include <algorithm>
#include <cstddef>
#include <filesystem>
#include <iterator>
#include <utility>
#include <vector>
#include "core/audio/audio.hpp"
#include "core/input/global_inputs.hpp"
#include "core/rendering/notifier.hpp"
#include "core/rendering/render_info.hpp"
#include "core/rendering/screen.hpp"
#include "core/resources/resource_cache.hpp"
#include "game/entities/player.hpp"
#include "game/map/map.hpp"
#include "game/options.hpp"
#include "game/ui/ui_palette.hpp"
#include "utils/defaults.hpp"
#include "utils/defines.hpp"
namespace {
// La geometria ha de quedar inicialitzada abans que Tilemap parsejar el
// mateix YAML i abans que el ctor de Player consulte Map::playfield_*.
// Construïm el Tilemap via un helper que primer carrega la geometria
// i després retorna el Tilemap per valor (move-construït).
auto makeTilemap(const std::string& yaml_path) -> Tilemap {
Map::loadFrom(yaml_path);
return Tilemap(yaml_path);
}
} // namespace
auto GameScene::defaultConfig() -> Config {
return {
.map_yaml = Defaults::Pyramid::MAP_YAML,
.player_sprite = Defaults::Pyramid::PLAYER_SPRITE,
.player_yaml = Defaults::Pyramid::PLAYER_YAML,
.music_track = Defaults::Pyramid::MUSIC_TRACK,
};
}
GameScene::GameScene(Config config)
: config_(std::move(config)),
tilemap_(makeTilemap(config_.map_yaml)) {
// Convenció: el Resource::Cache indexa per basename del fitxer, perquè
// la mateixa clau l'usa el parseador d'animació (YAML > tileSetFile).
const auto SPRITE_KEY = std::filesystem::path(config_.player_sprite).filename().string();
const auto ANIM_KEY = std::filesystem::path(config_.player_yaml).filename().string();
Resource::Cache::get().loadSurface(SPRITE_KEY, config_.player_sprite);
Resource::Cache::get().loadAnimation(ANIM_KEY, config_.player_yaml);
// Geometria de col·lisió del jugador (caixa 16×16, base 12): carregada
// del mateix YAML que l'animació. Ha d'estar feta abans d'instanciar
// Player perquè el spawn consulta `collision_*`.
Player::loadFrom(config_.player_yaml);
// Jugador 0: fletxes del teclat (Defaults::KeysGame). Instanciar-ne un
// segon només requereix afegir una altra entrada amb uns Controls
// distints (p.ex. WASD).
const Player::Controls CONTROLS_P1{
.up = Defaults::KeysGame::UP,
.down = Defaults::KeysGame::DOWN,
.left = Defaults::KeysGame::LEFT,
.right = Defaults::KeysGame::RIGHT,
};
// Spawn centrat al passadís exterior superior: la caixa de col·lisió
// queda dins del passadís vertical del mig (entre columnes 1 i 2) i
// centrada en Y al passadís exterior superior. Així no naix dins de
// cap sarcòfag.
constexpr auto CORRIDOR_CENTER_X = static_cast<float>(GameCanvas::CENTER_X);
const auto CORRIDOR_CENTER_Y =
static_cast<float>(Map::playfield_y) + (static_cast<float>(Map::outer_corridor) / 2.0F);
const auto SPAWN_X = CORRIDOR_CENTER_X - (static_cast<float>(Player::sprite_w) / 2.0F);
const auto SPAWN_Y = CORRIDOR_CENTER_Y - static_cast<float>(Player::collision_y_offset) - (static_cast<float>(Player::collision_size) / 2.0F);
players_.emplace_back(std::make_unique<Player>(ANIM_KEY, SPAWN_X, SPAWN_Y, CONTROLS_P1));
// Música de fons en bucle infinit. El track ja està al Resource::Cache
// (preload al Director), així que playMusic només mou el punter cap a
// jail_audio i obri l'stream.
if (Audio::get() != nullptr) {
Audio::get()->playMusic(config_.music_track, -1);
}
}
GameScene::~GameScene() = default;
void GameScene::update(int delta_ms) {
GlobalInputs::update(delta_ms);
if (GlobalInputs::wantsQuit()) { wants_quit_ = true; }
const float DT = static_cast<float>(delta_ms) / 1000.0F;
for (auto& player : players_) { player->update(DT); }
RenderInfo::get().update(delta_ms, Options::buildRenderInfoFrame(UiColor::RENDER_INFO_TEXT, UiColor::RENDER_INFO_SHADOW));
Notifier::get().update(delta_ms);
}
void GameScene::render() {
const auto BG_ARGB = Resource::Cache::get().getPalette("ui")[UiColor::CANVAS_BG];
Screen::get().fillRect(0, 0, GameCanvas::WIDTH, GameCanvas::HEIGHT, BG_ARGB);
// Mapa visual: textura prerenderitzada del tilemap. Els sarcòfags
// queden "foradats" (cel·les transparents) perquè passaran a ser
// actors independents; Map::collides encara els bloqueja per a les
// col·lisions del jugador.
tilemap_.render();
// Ordenem punters a jugadors per Y dels peus (ascendent = més al fons
// primer). Usem un vector auxiliar per no tocar l'ordre d'entrada de
// players_, que és el d'assignació de controls.
std::vector<Player*> sorted;
sorted.reserve(players_.size());
std::ranges::transform(players_, std::back_inserter(sorted), [](const auto& p) { return p.get(); });
std::ranges::sort(sorted, [](const Player* a, const Player* b) {
return a->getFeetY() < b->getFeetY();
});
// De moment els sarcòfags no es pinten (seran actors independents);
// dibuixem tots els jugadors sobre el tilemap ja ordenats per Y. Quan
// els sarcs siguen sprites, tornarem a interleave per al Z-order.
for (auto* player : sorted) { player->render(); }
Notifier::get().render();
RenderInfo::get().render();
Screen::get().present();
}