From c14efee9eabd3080f56e289c0522695d6fd04315 Mon Sep 17 00:00:00 2001 From: "Zed A. Shaw" Date: Fri, 24 Jan 2025 06:22:43 -0500 Subject: [PATCH] First step in refactoring to allow for multiple levels. Next is to clean up the APIs and sort out how things will be notified that they have to switch levels. --- gui.cpp | 52 +++++++++++++++++----------------- gui.hpp | 9 +++--- levelmanager.cpp | 11 +++++++- levelmanager.hpp | 4 ++- main.cpp | 22 ++------------- systems.cpp | 64 ++++++++++++++++++++++++++---------------- systems.hpp | 19 ++++++------- tests/gui.cpp | 12 +------- tests/levelmanager.cpp | 3 +- 9 files changed, 100 insertions(+), 96 deletions(-) diff --git a/gui.cpp b/gui.cpp index 75b38da..945b001 100644 --- a/gui.cpp +++ b/gui.cpp @@ -193,13 +193,11 @@ void MapViewUI::resize_canvas() { $canvas = Canvas(width * 2, height * 4); } -GUI::GUI(DinkyECS::World &world, Map& game_map) : - $world(world), - $game_map(game_map), - $status_ui(world), - $lights(game_map.width(), game_map.height()), - $map_view($world, $lights, $game_map), - $inventory_ui(world), +GUI::GUI() : + $level($level_manager.current()), + $status_ui(*$level.world), + $map_view(*$level.world, *$level.lights, *$level.map), + $inventory_ui(*$level.world), $sounds("./assets") { // this needs a config file soon @@ -219,8 +217,7 @@ void GUI::resize_map(int new_size) { } void GUI::save_world() { - $status_ui.log("Game saved!"); - save::to_file("./savefile.world", $world, $game_map); + $status_ui.log("SAVING BUSTED!"); } void GUI::create_renderer() { @@ -236,14 +233,16 @@ void GUI::create_renderer() { } void GUI::handle_world_events() { + auto& world = *$level.world; using eGUI = Events::GUI; - while($world.has_event()) { - auto [evt, entity, data] = $world.recv(); + + while(world.has_event()) { + auto [evt, entity, data] = world.recv(); switch(evt) { case eGUI::COMBAT: { auto &damage = std::any_cast(data); - auto enemy_combat = $world.get(entity); + auto enemy_combat = world.get(entity); if(damage.enemy_did > 0) { $status_ui.log(format("Enemy HIT YOU for {} damage!", damage.enemy_did)); @@ -265,10 +264,10 @@ void GUI::handle_world_events() { break; case eGUI::DEATH: { // auto &dead_data = std::any_cast(data); - auto player = $world.get_the(); + auto player = world.get_the(); dbc::check(player.entity == entity, "received death event for something not the player"); - auto player_combat = $world.get(entity); + auto player_combat = world.get(entity); if(player_combat.dead) { toggle_modal(&$death_ui, $player_died); } @@ -305,10 +304,12 @@ void GUI::shutdown() { } bool GUI::game_ui_events() { + auto& world = *$level.world; using KB = sf::Keyboard; - auto player = $world.get_the(); + + auto player = world.get_the(); int map_font_size = $renderer.font_size(); - auto& player_motion = $world.get(player.entity); + auto& player_motion = world.get(player.entity); bool event_happened = false; if(KB::isKeyPressed(KB::Left)) { @@ -328,12 +329,12 @@ bool GUI::game_ui_events() { } else if(KB::isKeyPressed(KB::Hyphen)) { resize_map(map_font_size - 10); } else if(KB::isKeyPressed(KB::L)) { - auto &debug = $world.get_the(); + auto &debug = world.get_the(); debug.LIGHT = !debug.LIGHT; } else if(KB::isKeyPressed(KB::I)) { toggle_modal(&$inventory_ui, $inventory_open); } else if(KB::isKeyPressed(KB::P)) { - auto &debug = $world.get_the(); + auto &debug = world.get_the(); debug.PATHS = !debug.PATHS; } else if(KB::isKeyPressed(KB::S)) { save_world(); @@ -422,12 +423,11 @@ void GUI::draw_paused() { } void GUI::run_systems() { - auto player = $world.get_the(); - System::motion($world, $game_map); - System::enemy_pathing($world, $game_map, player); - System::lighting($world, $game_map, $lights); - System::collision($world, player); - System::death($world); + System::motion($level); + System::enemy_pathing($level); + System::lighting($level); + System::collision($level); + System::death($level); } void GUI::shake() { @@ -481,7 +481,9 @@ void GUI::render_scene() { } int GUI::main(bool run_once) { - $world.set_the({}); + auto &world = *$level.world; + + world.set_the({}); create_renderer(); run_systems(); diff --git a/gui.hpp b/gui.hpp index 4ffc04e..eae91d8 100644 --- a/gui.hpp +++ b/gui.hpp @@ -19,6 +19,7 @@ #include "render.hpp" #include "panel.hpp" #include "lights.hpp" +#include "levelmanager.hpp" using std::string; using ftxui::Canvas, ftxui::Component, ftxui::Screen, ftxui::Button; @@ -122,10 +123,9 @@ class MapViewUI : public Panel { }; class GUI { - DinkyECS::World& $world; - Map& $game_map; + LevelManager $level_manager; + GameLevel &$level; StatusUI $status_ui; - LightRender $lights; MapViewUI $map_view; InventoryUI $inventory_ui; DeathUI $death_ui; @@ -142,7 +142,8 @@ class GUI { std::vector $active_panels; public: - GUI(DinkyECS::World& world, Map& game_map); + GUI(); + // disable copying GUI(GUI &gui) = delete; diff --git a/levelmanager.cpp b/levelmanager.cpp index 85a3a65..1f21f6c 100644 --- a/levelmanager.cpp +++ b/levelmanager.cpp @@ -2,13 +2,16 @@ #include "worldbuilder.hpp" #include "constants.hpp" #include "save.hpp" +#include "systems.hpp" using lighting::LightRender; using std::shared_ptr, std::make_shared; LevelManager::LevelManager() { + create_level(); } + size_t LevelManager::create_level() { auto world = make_shared(); save::load_configs(*world); @@ -19,8 +22,14 @@ size_t LevelManager::create_level() { size_t index = $levels.size(); + auto collider = make_shared(); + // not sure if this is still needed + world->set_the(*collider); + System::init_positions(*world, *collider); + $levels.emplace_back(index, map, world, - make_shared(map->width(), map->height())); + make_shared(map->width(), map->height()), + collider); dbc::check(index == $levels.size() - 1, "Level index is not the same as $levels.size() - 1, off by one error"); return index; diff --git a/levelmanager.hpp b/levelmanager.hpp index 461a981..89a47e3 100644 --- a/levelmanager.hpp +++ b/levelmanager.hpp @@ -5,6 +5,7 @@ #include "map.hpp" #include #include +#include "spatialmap.hpp" struct GameLevel { @@ -12,6 +13,7 @@ struct GameLevel { std::shared_ptr map; std::shared_ptr world; std::shared_ptr lights; + std::shared_ptr collision; }; class LevelManager { @@ -25,6 +27,6 @@ class LevelManager { GameLevel &next(); GameLevel &previous(); GameLevel ¤t(); - size_t current_index(); + size_t current_index() { return $current_level; } GameLevel &get(size_t index); }; diff --git a/main.cpp b/main.cpp index c63e042..06817d0 100644 --- a/main.cpp +++ b/main.cpp @@ -10,6 +10,7 @@ #include "save.hpp" #include "lights.hpp" #include "worldbuilder.hpp" +#include "levelmanager.hpp" #include "ftxui/screen/terminal.hpp" // for SetColorSupport, Color, TrueColor #include #include @@ -20,25 +21,8 @@ using namespace components; using lighting::LightSource; namespace fs = std::filesystem; -int main(int argc, char *argv[]) { - DinkyECS::World world; - Map game_map(GAME_MAP_X, GAME_MAP_Y); - save::load_configs(world); - - if(argc == 2) { - fmt::println("Loading save file {}", argv[1]); - fs::path save_path{argv[1]}; - save::from_file(save_path, world, game_map); - } else { - WorldBuilder builder(game_map); - builder.generate(world); - } - - SpatialMap collider; - world.set_the(collider); - System::init_positions(world); - - GUI gui(world, game_map); +int main() { + GUI gui; return gui.main(); } diff --git a/systems.cpp b/systems.cpp index c91a825..5269f2a 100644 --- a/systems.cpp +++ b/systems.cpp @@ -16,25 +16,33 @@ using namespace components; using ftxui::Color; using lighting::LightSource; -void System::lighting(DinkyECS::World &world, Map &game_map, LightRender &light) { +void System::lighting(GameLevel &level) { + auto &light = *level.lights; + auto &world = *level.world; + auto &map = *level.map; + light.reset_light(); world.query([&](const auto &ent[[maybe_unused]], auto &position) { light.set_light_target(position.location); }); - light.path_light(game_map.walls()); + light.path_light(map.walls()); world.query([&](const auto &ent[[maybe_unused]], auto &position, auto &lightsource) { light.render_light(lightsource, position.location); }); } -void System::enemy_pathing(DinkyECS::World &world, Map &game_map, Player &player) { +void System::enemy_pathing(GameLevel &level) { + auto &world = *level.world; + auto &map = *level.map; + auto player = world.get_the(); + // TODO: this will be on each enemy not a global thing const auto &player_position = world.get(player.entity); - game_map.set_target(player_position.location); - game_map.make_paths(); + map.set_target(player_position.location); + map.make_paths(); world.query([&](const auto &ent, auto &position, auto &motion) { if(ent != player.entity) { @@ -42,18 +50,16 @@ void System::enemy_pathing(DinkyECS::World &world, Map &game_map, Player &player const auto &config = world.get(ent); Point out = position.location; // copy - if(game_map.distance(out) < config.hearing_distance) { - game_map.neighbors(out, motion.random); + if(map.distance(out) < config.hearing_distance) { + map.neighbors(out, motion.random); motion = { int(out.x - position.location.x), int(out.y - position.location.y)}; } } }); - game_map.clear_target(player_position.location); + map.clear_target(player_position.location); } -void System::init_positions(DinkyECS::World &world) { - auto &collider = world.get_the(); - +void System::init_positions(DinkyECS::World &world, SpatialMap &collider) { // BUG: instead of separate things maybe just one // BUG: Collision component that references what is collide world.query([&](const auto &ent, auto &pos) { @@ -85,25 +91,28 @@ inline void move_entity(SpatialMap &collider, Map &game_map, Position &position, position.location = move_to; } -void System::motion(DinkyECS::World &world, Map &game_map) { - auto &collider = world.get_the(); +void System::motion(GameLevel &level) { + auto &map = *level.map; + auto &world = *level.world; + auto &collider = *level.collision; world.query([&](const auto &ent, auto &position, auto &motion) { // don't process entities that don't move if(motion.dx != 0 || motion.dy != 0) { - move_entity(collider, game_map, position, motion, ent); + move_entity(collider, map, position, motion, ent); } }); } -void System::death(DinkyECS::World &world) { +void System::death(GameLevel &level) { + auto &world = *level.world; + auto &collider = *level.collision; + auto player = world.get_the(); + // BUG: this is where entities can die on top of // BUG: eachother and overlap their corpse // BUG: maybe that can be allowed and looting just shows // BUG: all dead things there? - auto &collider = world.get_the(); - auto player = world.get_the(); - world.query([&](const auto &ent, auto &position, auto &combat) { // bring out yer dead if(combat.hp <= 0 && !combat.dead) { @@ -121,8 +130,11 @@ void System::death(DinkyECS::World &world) { }); } -void System::collision(DinkyECS::World &world, Player &player) { - auto& collider = world.get_the(); +void System::collision(GameLevel &level) { + auto &collider = *level.collision; + auto &world = *level.world; + auto player = world.get_the(); + const auto& player_position = world.get(player.entity); auto& player_combat = world.get(player.entity); @@ -185,15 +197,19 @@ void System::collision(DinkyECS::World &world, Player &player) { } } -void System::draw_entities(DinkyECS::World &world, Map &game_map, const Matrix &lighting, ftxui::Canvas &canvas, const Point &cam_orig, size_t view_x, size_t view_y) { - auto &tiles = game_map.tiles(); +/* + * This one is called inside the MapViewUI very often so + * just avoide GameMap unlike the others. + */ +void System::draw_entities(DinkyECS::World &world, Map &map, const Matrix &lights, ftxui::Canvas &canvas, const Point &cam_orig, size_t view_x, size_t view_y) { + auto &tiles = map.tiles(); world.query([&](auto &ent[[maybe_unused]], auto &pos, auto &tile) { if(pos.location.x >= cam_orig.x && pos.location.x <= cam_orig.x + view_x && pos.location.y >= cam_orig.y && pos.location.y <= cam_orig.y + view_y) { - Point loc = game_map.map_to_camera(pos.location, cam_orig); + Point loc = map.map_to_camera(pos.location, cam_orig); - float light_value = lighting[pos.location.y][pos.location.x] * PERCENT; + float light_value = lights[pos.location.y][pos.location.x] * PERCENT; const TileCell& cell = tiles.at(pos.location.x, pos.location.y); // the 2 and 4 are from ftxui::Canvas since it does a kind of "subpixel" drawing diff --git a/systems.hpp b/systems.hpp index 4f7c72e..227850b 100644 --- a/systems.hpp +++ b/systems.hpp @@ -1,21 +1,20 @@ #pragma once -#include "dinkyecs.hpp" -#include "map.hpp" #include "components.hpp" +#include "levelmanager.hpp" #include namespace System { using namespace components; - using namespace lighting; - void lighting(DinkyECS::World &world, Map &game_map, LightRender &light); - void motion(DinkyECS::World &world, Map &game_map); - void collision(DinkyECS::World &world, Player &player); - void death(DinkyECS::World &world); - void enemy_pathing(DinkyECS::World &world, Map &game_map, Player &player); - void draw_entities(DinkyECS::World &world, Map &game_map, const Matrix &lighting, ftxui::Canvas &canvas, const Point &cam_orig, size_t view_x, size_t view_y); - void init_positions(DinkyECS::World &world); + void lighting(GameLevel &level); + void motion(GameLevel &level); + void collision(GameLevel &level); + void death(GameLevel &level); + void enemy_pathing(GameLevel &level); + + void init_positions(DinkyECS::World &world, SpatialMap &collider); void pickup(DinkyECS::World &world, DinkyECS::Entity actor, DinkyECS::Entity item); void device(DinkyECS::World &world, DinkyECS::Entity actor, DinkyECS::Entity item); + void draw_entities(DinkyECS::World &world, Map &map, const Matrix &lights, ftxui::Canvas &canvas, const Point &cam_orig, size_t view_x, size_t view_y); } diff --git a/tests/gui.cpp b/tests/gui.cpp index 823770e..1b09d6a 100644 --- a/tests/gui.cpp +++ b/tests/gui.cpp @@ -14,16 +14,6 @@ using namespace components; using std::string; TEST_CASE("load a basic gui run but don't loop", "[gui]") { - DinkyECS::World world; - save::load_configs(world); - Map game_map(40, 40); - WorldBuilder builder(game_map); - builder.generate(world); - - SpatialMap collider; - world.set_the(collider); - System::init_positions(world); - - GUI gui(world, game_map); + GUI gui; gui.main(true); // runs once } diff --git a/tests/levelmanager.cpp b/tests/levelmanager.cpp index 5305f32..d8a6b8d 100644 --- a/tests/levelmanager.cpp +++ b/tests/levelmanager.cpp @@ -15,7 +15,8 @@ using std::string; TEST_CASE("basic level manager test", "[levelmanager]") { LevelManager lm; - size_t level1 = lm.create_level(); + // starts off with one already but I need to change that + size_t level1 = lm.current_index(); size_t level2 = lm.create_level(); auto& test1_level = lm.get(level1);