220 lines
6.1 KiB
C++
220 lines
6.1 KiB
C++
#pragma once
|
|
|
|
#include "Draw.hpp"
|
|
#include "Entities.hpp"
|
|
#include "Systems.hpp"
|
|
|
|
#include "raylib.h"
|
|
|
|
#include <memory>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
class SceneManager;
|
|
|
|
class Scene {
|
|
public:
|
|
explicit Scene(SceneManager &owner) : manager(owner) {}
|
|
virtual ~Scene() = default;
|
|
|
|
virtual void Enter() {}
|
|
virtual void Exit() {}
|
|
virtual void Update(float dt) = 0;
|
|
virtual void Draw() = 0;
|
|
|
|
protected:
|
|
SceneManager &manager;
|
|
};
|
|
|
|
/**
|
|
* Manages the current active scene, using an object oriented state machine approach.
|
|
*/
|
|
class SceneManager {
|
|
public:
|
|
/**
|
|
* @brief Changes the current scene, invoking `Exit` on the old scene and `Enter`
|
|
* on the new scene. The new scene is constructed in-place with the provided arguments.
|
|
*
|
|
* @tparam T The type of the new scene, must derive from `Scene`.
|
|
* @param args Arguments forwarded to the constructor of the new scene.
|
|
*/
|
|
template <typename T, typename... Args> void ChangeScene(Args &&...args);
|
|
|
|
/**
|
|
* @brief Changes to the provided next scene, invoking `Exit` on the old scene and
|
|
* `Enter` on the new scene.
|
|
*
|
|
* @param nextScene The new scene to switch to.
|
|
*/
|
|
void ChangeScene(std::unique_ptr<Scene> nextScene);
|
|
void Update(float dt);
|
|
void Draw();
|
|
|
|
private:
|
|
// use unique_ptr to enforce single ownership and ensure proper cleanup on
|
|
// scene change
|
|
std::unique_ptr<Scene> current;
|
|
};
|
|
|
|
class StartMenuScene;
|
|
class GameplayScene;
|
|
class DeathScene;
|
|
|
|
class StartMenuScene : public Scene {
|
|
public:
|
|
explicit StartMenuScene(SceneManager &owner) : Scene(owner) {}
|
|
void Update(float dt) override;
|
|
void Draw() override;
|
|
};
|
|
|
|
class GameplayScene : public Scene {
|
|
public:
|
|
explicit GameplayScene(SceneManager &owner) : Scene(owner) {}
|
|
void Enter() override;
|
|
void Update(float dt) override;
|
|
void Draw() override;
|
|
|
|
private:
|
|
std::vector<std::shared_ptr<Entity>> entities;
|
|
GameContext context;
|
|
bool wantsDeathScene = false;
|
|
int collectedCount = 0;
|
|
float meterValue = 60.0f;
|
|
};
|
|
|
|
class DeathScene : public Scene {
|
|
public:
|
|
explicit DeathScene(SceneManager &owner) : Scene(owner) {}
|
|
void Update(float dt) override;
|
|
void Draw() override;
|
|
};
|
|
|
|
inline void SceneManager::ChangeScene(std::unique_ptr<Scene> nextScene) {
|
|
if (current) {
|
|
current->Exit();
|
|
}
|
|
|
|
current = std::move(nextScene);
|
|
if (current) {
|
|
current->Enter();
|
|
}
|
|
}
|
|
|
|
inline void SceneManager::Update(float dt) {
|
|
if (current) {
|
|
current->Update(dt);
|
|
}
|
|
}
|
|
|
|
inline void SceneManager::Draw() {
|
|
if (current) {
|
|
current->Draw();
|
|
}
|
|
}
|
|
|
|
inline void StartMenuScene::Update(float) {
|
|
if (IsKeyPressed(KEY_ENTER) || IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) {
|
|
manager.ChangeScene<GameplayScene>();
|
|
}
|
|
}
|
|
|
|
inline void StartMenuScene::Draw() {
|
|
DrawSceneOutline();
|
|
DrawText("Gravity Surfing", 24, 28, 36, Color{230, 238, 255, 255});
|
|
DrawText("Click or press Enter to start", 24, 78, 22, Color{140, 198, 220, 255});
|
|
DrawText("Left mouse controls gravity well", 24, 130, 18, Color{170, 185, 205, 255});
|
|
DrawText("Collect stars and dodge hazards", 24, 156, 18, Color{170, 185, 205, 255});
|
|
}
|
|
|
|
inline void GameplayScene::Enter() {
|
|
entities.clear();
|
|
entities.reserve(20);
|
|
context = {};
|
|
wantsDeathScene = false;
|
|
collectedCount = 0;
|
|
meterValue = 60.0f;
|
|
|
|
context.onPlayerDeath = [this]() { wantsDeathScene = true; };
|
|
context.AddCollectiblePickedListener([this](Entity &) { ++collectedCount; });
|
|
context.AddMeterChangedListener([this](float, float newValue) { meterValue = newValue; });
|
|
|
|
entities.push_back(CreateWorld());
|
|
entities.push_back(CreateGravityWell());
|
|
entities.push_back(CreateProbe());
|
|
entities.push_back(CreateStar(900.0f, 120.0f));
|
|
entities.push_back(CreateAsteroid(1100.0f, 330.0f));
|
|
entities.push_back(CreateNullZone(1280.0f, 70.0f));
|
|
entities.push_back(CreateHUD());
|
|
|
|
if (auto meter = entities.back()->GetComponent<MeterComponent>()) {
|
|
meterValue = meter->get().value;
|
|
}
|
|
}
|
|
|
|
inline void GameplayScene::Update(float dt) {
|
|
UpdateAllSystems(entities, context, dt);
|
|
if (wantsDeathScene) {
|
|
manager.ChangeScene<DeathScene>();
|
|
}
|
|
}
|
|
|
|
inline void GameplayScene::Draw() {
|
|
DrawSceneOutline();
|
|
DrawText("Gravity Surfing", 14, 12, 20, Color{230, 238, 255, 255});
|
|
|
|
auto worldScroll = entities[0]->GetComponent<ScrollComponent>();
|
|
auto probeTransform = entities[2]->GetComponent<TransformComponent>();
|
|
|
|
if (worldScroll) {
|
|
DrawText(TextFormat("scrollX: %.2f", worldScroll->get().scrollX), 14, 40, 16,
|
|
Color{125, 225, 205, 255});
|
|
}
|
|
|
|
if (probeTransform) {
|
|
const auto &probe = probeTransform->get();
|
|
DrawText(TextFormat("probe: (%.1f, %.1f)", probe.x, probe.y), 14, 60, 16,
|
|
Color{235, 215, 125, 255});
|
|
}
|
|
|
|
DrawText(TextFormat("meter: %.1f", meterValue), 14, 80, 16, Color{120, 210, 190, 255});
|
|
DrawText(TextFormat("stars: %i", collectedCount), 14, 100, 16, Color{255, 223, 86, 255});
|
|
|
|
for (auto &entity : entities) {
|
|
if (!entity) {
|
|
continue;
|
|
}
|
|
|
|
auto collectible = entity->GetComponent<CollectibleComponent>();
|
|
if (collectible && collectible->get().collected) {
|
|
continue;
|
|
}
|
|
|
|
auto render = entity->GetComponent<RenderComponent>();
|
|
if (render) {
|
|
render->get().Draw();
|
|
}
|
|
}
|
|
}
|
|
|
|
inline void DeathScene::Update(float) {
|
|
if (IsKeyPressed(KEY_ENTER) || IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) {
|
|
manager.ChangeScene<GameplayScene>();
|
|
return;
|
|
}
|
|
|
|
if (IsKeyPressed(KEY_ESCAPE)) {
|
|
manager.ChangeScene<StartMenuScene>();
|
|
}
|
|
}
|
|
|
|
inline void DeathScene::Draw() {
|
|
DrawSceneOutline();
|
|
DrawText("PROBE LOST", 24, 28, 36, Color{255, 208, 208, 255});
|
|
DrawText("Press Enter or click to restart", 24, 84, 22, Color{255, 170, 170, 255});
|
|
DrawText("Press Esc for menu", 24, 116, 20, Color{200, 188, 188, 255});
|
|
}
|
|
|
|
template <typename T, typename... Args> inline void SceneManager::ChangeScene(Args &&...args) {
|
|
ChangeScene(std::make_unique<T>(*this, std::forward<Args>(args)...));
|
|
}
|