#include "Scene.h" #include "Engine.h" #include "Input.h" #include "Textures.h" #include "Audio.h" #include "Render.h" #include "Window.h" #include "Log.h" #include "Entity.h" #include "EntityManager.h" #include "Player.h" #include "Map.h" #include "Item.h" #include "EnemySpider.h" #include "EnemyBat.h" #include "Physics.h" #include <pugixml.hpp>
Scene::Scene() { name = "scene"; }
Scene::~Scene() {}
bool Scene::Awake() { LOG("Loading Scene");
player = std::dynamic_pointer_cast<Player>(
Engine::GetInstance().entityManager->CreateEntity(EntityType::PLAYER));
coin = std::dynamic_pointer_cast<Item>(
Engine::GetInstance().entityManager->CreateEntity(EntityType::ITEM));
coin->position = coinInitialPos;
spider1 = std::dynamic_pointer_cast<EnemySpider>(
Engine::GetInstance().entityManager->CreateEntity(EntityType::ENEMY_GROUND));
spider1->position = Vector2D(800, 300);
spider2 = std::dynamic_pointer_cast<EnemySpider>(
Engine::GetInstance().entityManager->CreateEntity(EntityType::ENEMY_GROUND));
spider2->position = Vector2D(1500, 300);
bat1 = std::dynamic_pointer_cast<EnemyBat>(
Engine::GetInstance().entityManager->CreateEntity(EntityType::ENEMY_FLY));
bat1->position = Vector2D(1000, 200);
bat2 = std::dynamic_pointer_cast<EnemyBat>(
Engine::GetInstance().entityManager->CreateEntity(EntityType::ENEMY_FLY));
bat2->position = Vector2D(1700, 200);
return true;
}
bool Scene::Start() { Engine::GetInstance().audio->PlayMusic("Assets/Audio/Music/backgroundmusic.wav");
Engine::GetInstance().map->Load("Assets/Maps/", "Mapa5.tmx");
// Cargar textura de vidas
heartsTexture = Engine::GetInstance().textures->Load("Assets/Textures/Vidas.png");
if (heartsTexture)
{
Engine::GetInstance().textures->GetSize(heartsTexture, heartW, heartH);
}
else
{
LOG("Scene: could not load hearts texture Vidas.png");
}
return true;
}
bool Scene::PreUpdate() { return true; }
bool Scene::Update(float dt) { float camSpeed = 1.0f;
if (Engine::GetInstance().input->GetKey(SDL_SCANCODE_UP) == KEY_REPEAT)
Engine::GetInstance().render->camera.y -= (int)ceil(camSpeed * dt);
if (Engine::GetInstance().input->GetKey(SDL_SCANCODE_DOWN) == KEY_REPEAT)
Engine::GetInstance().render->camera.y += (int)ceil(camSpeed * dt);
if (Engine::GetInstance().input->GetKey(SDL_SCANCODE_LEFT) == KEY_REPEAT)
Engine::GetInstance().render->camera.x -= (int)ceil(camSpeed * dt);
if (Engine::GetInstance().input->GetKey(SDL_SCANCODE_RIGHT) == KEY_REPEAT)
Engine::GetInstance().render->camera.x += (int)ceil(camSpeed * dt);
// F1 - Cargar Nivel 1 (Mapa5.tmx)
if (Engine::GetInstance().input->GetKey(SDL_SCANCODE_F1) == KEY_DOWN)
{
LoadMap1();
}
// F2 - Cargar Nivel 2 (map2.tmx) - Solo player y música
if (Engine::GetInstance().input->GetKey(SDL_SCANCODE_F2) == KEY_DOWN)
{
LoadMap2();
}
// F5 - Guardar partida
if (Engine::GetInstance().input->GetKey(SDL_SCANCODE_F5) == KEY_DOWN)
{
SaveGame();
}
// F6 - Cargar partida
if (Engine::GetInstance().input->GetKey(SDL_SCANCODE_F6) == KEY_DOWN)
{
LoadGame();
}
return true;
}
bool Scene::PostUpdate() { if (Engine::GetInstance().input->GetKey(SDL_SCANCODE_ESCAPE) == KEY_DOWN) return false;
// HUD de vidas
if (heartsTexture && player)
{
int maxLives = player->GetMaxDeaths();
int usedLives = player->GetDeathCount();
int livesLeft = maxLives - usedLives;
if (livesLeft < 0) livesLeft = 0;
if (livesLeft > maxLives) livesLeft = maxLives;
int startX = 10;
int startY = 10;
int spacing = (heartW > 0 ? heartW + 4 : 40);
for (int i = 0; i < livesLeft; ++i)
{
int x = startX + i * spacing;
int y = startY;
Engine::GetInstance().render->DrawTexture(
heartsTexture,
x,
y,
nullptr,
0.0f
);
}
}
return true;
}
bool Scene::CleanUp() { LOG("Freeing scene");
if (heartsTexture)
{
Engine::GetInstance().textures->UnLoad(heartsTexture);
heartsTexture = nullptr;
}
return true;
}
void Scene::ResetLevel() { if (!coin || !coin->active) { coin = std::dynamic_pointer_cast( Engine::GetInstance().entityManager->CreateEntity(EntityType::ITEM));
coin->position = coinInitialPos;
coin->Start();
}
}
Vector2D Scene::GetPlayerPosition() const { if (player) return player->position; return Vector2D(0, 0); }
void Scene::ResetEnemies() { LOG("Resetting all enemies to initial positions");
if (spider1) spider1->ResetPosition();
if (spider2) spider2->ResetPosition();
if (bat1) bat1->ResetPosition();
if (bat2) bat2->ResetPosition();
}
// ==================================================================== // CAMBIO DE MAPAS // ====================================================================
void Scene::LoadMap1() { LOG("Loading Map 1 (Mapa5.tmx) - Full level with enemies");
// 1. Limpiar el mapa anterior completamente
Engine::GetInstance().map->CleanUp();
// 2. Limpiar física
Engine::GetInstance().physics->CleanUp();
Engine::GetInstance().physics->Start();
// 3. Cargar el nuevo mapa
Engine::GetInstance().map->Load("Assets/Maps/", "Mapa5.tmx");
// 4. Recrear el cuerpo físico del player
if (player)
{
player->CleanUp(); // Limpia el pbody anterior
player->Start(); // Crea un nuevo pbody
}
// 5. Recrear los cuerpos físicos de los enemigos y moneda
if (coin)
{
coin->CleanUp();
coin->Start();
coin->isPicked = false;
coin->position = coinInitialPos;
}
if (spider1) { spider1->CleanUp(); spider1->Start(); }
if (spider2) { spider2->CleanUp(); spider2->Start(); }
if (bat1) { bat1->CleanUp(); bat1->Start(); }
if (bat2) { bat2->CleanUp(); bat2->Start(); }
// 6. Resetear posiciones del jugador
if (player)
{
player->ResetToInitialSpawn();
Vector2D spawnPos = player->position;
float groundY = Engine::GetInstance().map->GetGroundYBelow(spawnPos.getX(), spawnPos.getY());
if (player->pbody)
{
player->pbody->SetPosition((int)spawnPos.getX(), (int)(groundY - player->texH / 2));
Engine::GetInstance().physics->SetLinearVelocity(player->pbody, 0.0f, 0.0f);
}
}
// 7. Resetear enemigos
ResetEnemies();
LOG("Map 1 loaded successfully");
}
void Scene::LoadMap2() { LOG("Loading Map 2 (map2.tmx) - Player only, no enemies");
// 1. Limpiar el mapa anterior completamente
Engine::GetInstance().map->CleanUp();
// 2. Limpiar física
Engine::GetInstance().physics->CleanUp();
Engine::GetInstance().physics->Start();
// 3. Cargar el nuevo mapa
Engine::GetInstance().map->Load("Assets/Maps/", "map2.tmx");
// 4. Recrear el cuerpo físico del player
if (player)
{
player->CleanUp(); // Limpia el pbody anterior
player->Start(); // Crea un nuevo pbody
}
// 5. Desactivar todos los enemigos y la moneda
if (coin) coin->Disable();
if (spider1) spider1->Disable();
if (spider2) spider2->Disable();
if (bat1) bat1->Disable();
if (bat2) bat2->Disable();
// 6. Solo resetear el player
if (player)
{
player->ResetToInitialSpawn();
Vector2D spawnPos = player->position;
float groundY = Engine::GetInstance().map->GetGroundYBelow(spawnPos.getX(), spawnPos.getY());
if (player->pbody)
{
player->pbody->SetPosition((int)spawnPos.getX(), (int)(groundY - player->texH / 2));
Engine::GetInstance().physics->SetLinearVelocity(player->pbody, 0.0f, 0.0f);
}
}
// La música ya está sonando, no hace falta cambiarla
LOG("Map 2 loaded - Only player active");
}
// ==================================================================== // SISTEMA SAVE/LOAD // ====================================================================
bool Scene::SaveGame() { LOG("Saving game state...");
pugi::xml_document saveDoc;
pugi::xml_node root = saveDoc.append_child("save");
// Guardar Player usando position (público)
if (player)
{
pugi::xml_node playerNode = root.append_child("player");
playerNode.append_attribute("x").set_value((int)player->position.getX());
playerNode.append_attribute("y").set_value((int)player->position.getY());
playerNode.append_attribute("deathCount").set_value(player->GetDeathCount());
LOG("Player saved at (%.1f, %.1f)", player->position.getX(), player->position.getY());
}
// Guardar Spider1 usando position (público)
if (spider1)
{
pugi::xml_node spiderNode = root.append_child("spider1");
spiderNode.append_attribute("x").set_value((int)spider1->position.getX());
spiderNode.append_attribute("y").set_value((int)spider1->position.getY());
LOG("Spider1 saved at (%.1f, %.1f)", spider1->position.getX(), spider1->position.getY());
}
// Guardar Spider2
if (spider2)
{
pugi::xml_node spiderNode = root.append_child("spider2");
spiderNode.append_attribute("x").set_value((int)spider2->position.getX());
spiderNode.append_attribute("y").set_value((int)spider2->position.getY());
LOG("Spider2 saved at (%.1f, %.1f)", spider2->position.getX(), spider2->position.getY());
}
// Guardar Bat1
if (bat1)
{
pugi::xml_node batNode = root.append_child("bat1");
batNode.append_attribute("x").set_value((int)bat1->position.getX());
batNode.append_attribute("y").set_value((int)bat1->position.getY());
LOG("Bat1 saved at (%.1f, %.1f)", bat1->position.getX(), bat1->position.getY());
}
// Guardar Bat2
if (bat2)
{
pugi::xml_node batNode = root.append_child("bat2");
batNode.append_attribute("x").set_value((int)bat2->position.getX());
batNode.append_attribute("y").set_value((int)bat2->position.getY());
LOG("Bat2 saved at (%.1f, %.1f)", bat2->position.getX(), bat2->position.getY());
}
// Guardar archivo
bool result = saveDoc.save_file("savegame.xml");
if (result)
{
LOG("Game saved successfully to savegame.xml");
}
else
{
LOG("ERROR: Failed to save game to savegame.xml");
}
return result;
}
bool Scene::LoadGame() { LOG("Loading game state...");
pugi::xml_document saveDoc;
pugi::xml_parse_result result = saveDoc.load_file("savegame.xml");
if (!result)
{
LOG("ERROR: Could not load savegame.xml: %s", result.description());
return false;
}
pugi::xml_node root = saveDoc.child("save");
if (!root)
{
LOG("ERROR: savegame.xml has no <save> root node");
return false;
}
// Cargar Player
pugi::xml_node playerNode = root.child("player");
if (playerNode && player && player->pbody)
{
int px = playerNode.attribute("x").as_int();
int py = playerNode.attribute("y").as_int();
player->pbody->SetPosition(px, py);
player->position.setX((float)px);
player->position.setY((float)py);
// Resetear velocidad
Engine::GetInstance().physics->SetLinearVelocity(player->pbody, 0.0f, 0.0f);
LOG("Player loaded at (%d, %d)", px, py);
}
// Cargar Spider1 - usando position y ResetPosition() modificado
pugi::xml_node spider1Node = root.child("spider1");
if (spider1Node && spider1)
{
int sx = spider1Node.attribute("x").as_int();
int sy = spider1Node.attribute("y").as_int();
// Actualizar initialPosition temporalmente para que ResetPosition use estos valores
Vector2D savedPos((float)sx, (float)sy);
spider1->initialPosition = savedPos;
spider1->ResetPosition();
LOG("Spider1 loaded at (%d, %d)", sx, sy);
}
// Cargar Spider2
pugi::xml_node spider2Node = root.child("spider2");
if (spider2Node && spider2)
{
int sx = spider2Node.attribute("x").as_int();
int sy = spider2Node.attribute("y").as_int();
Vector2D savedPos((float)sx, (float)sy);
spider2->initialPosition = savedPos;
spider2->ResetPosition();
LOG("Spider2 loaded at (%d, %d)", sx, sy);
}
// Cargar Bat1
pugi::xml_node bat1Node = root.child("bat1");
if (bat1Node && bat1)
{
int bx = bat1Node.attribute("x").as_int();
int by = bat1Node.attribute("y").as_int();
Vector2D savedPos((float)bx, (float)by);
bat1->initialPosition = savedPos;
bat1->ResetPosition();
LOG("Bat1 loaded at (%d, %d)", bx, by);
}
// Cargar Bat2
pugi::xml_node bat2Node = root.child("bat2");
if (bat2Node && bat2)
{
int bx = bat2Node.attribute("x").as_int();
int by = bat2Node.attribute("y").as_int();
Vector2D savedPos((float)bx, (float)by);
bat2->initialPosition = savedPos;
bat2->ResetPosition();
LOG("Bat2 loaded at (%d, %d)", bx, by);
}
LOG("Game loaded successfully from savegame.xml");
return true;
}
#pragma once
#include "Module.h" #include "Player.h" #include "Item.h" #include "Vector2D.h" #include
class EnemySpider; class EnemyBat;
class Scene : public Module { public: Scene(); virtual ~Scene(); bool Awake() override; bool Start() override; bool PreUpdate() override; bool Update(float dt) override; bool PostUpdate() override; bool CleanUp() override;
void ResetLevel();
void ResetEnemies();
Vector2D GetPlayerPosition() const;
// Sistema Save/Load
bool SaveGame();
bool LoadGame();
// Cambio de mapas
void LoadMap1(); // Carga Mapa5.tmx con todos los enemigos
void LoadMap2(); // Carga map2.tmx solo con player y música
private: std::shared_ptr player;
std::shared_ptr<Item> coin;
Vector2D coinInitialPos = Vector2D(1300, 375);
// Referencias a los enemigos
std::shared_ptr<EnemySpider> spider1;
std::shared_ptr<EnemySpider> spider2;
std::shared_ptr<EnemyBat> bat1;
std::shared_ptr<EnemyBat> bat2;
// HUD de vidas
SDL_Texture* heartsTexture = nullptr;
int heartW = 0;
int heartH = 0;
};