From a0eff927b6acbf2bb4d0512ee3b4dfe2e29fad5c Mon Sep 17 00:00:00 2001 From: "Zed A. Shaw" Date: Fri, 20 Jun 2025 13:17:12 -0400 Subject: [PATCH] Big BIG refactor to make inventory use a model that's placed into the world, following a more sane MVC style. --- Makefile | 2 +- gui/dnd_loot.cpp | 50 ++++++++++++++++++++-------- gui/dnd_loot.hpp | 6 ++++ gui/fsm.cpp | 11 +----- gui/fsm.hpp | 1 - gui/loot_ui.cpp | 24 ++++++++------ gui/loot_ui.hpp | 9 +++-- gui/ritual_ui.cpp | 1 + gui/ritual_ui.hpp | 2 +- gui/status_ui.cpp | 81 ++++++++++++++++++++++++++------------------- gui/status_ui.hpp | 2 +- gui/uisystems.cpp | 32 ------------------ gui/uisystems.hpp | 10 ------ inventory.cpp | 65 ++++++++++++++++++++++++++++++++++++ inventory.hpp | 21 ++++++++++++ meson.build | 3 +- systems.cpp | 15 ++++++--- systems.hpp | 2 +- tests/inventory.cpp | 47 ++++++++++++++++++++++++++ tests/matrix.cpp | 1 + worldbuilder.cpp | 8 +++++ 21 files changed, 270 insertions(+), 123 deletions(-) delete mode 100644 gui/uisystems.cpp delete mode 100644 gui/uisystems.hpp create mode 100644 inventory.cpp create mode 100644 inventory.hpp create mode 100644 tests/inventory.cpp diff --git a/Makefile b/Makefile index 77b1ba6..3723a40 100644 --- a/Makefile +++ b/Makefile @@ -57,7 +57,7 @@ clean: meson compile --clean -C builddir debug_test: build - gdb --nx -x .gdbinit --ex run --args builddir/runtests -e + gdb --nx -x .gdbinit --ex run --args builddir/runtests -e "[matrix:viewport]" win_installer: powershell 'start "C:\Program Files (x86)\solicus\InstallForge\bin\ifbuilderenvx86.exe" scripts\win_installer.ifp' diff --git a/gui/dnd_loot.cpp b/gui/dnd_loot.cpp index f4ede8c..860a1c1 100644 --- a/gui/dnd_loot.cpp +++ b/gui/dnd_loot.cpp @@ -1,7 +1,5 @@ -#define FSM_DEBUG 1 #include "gui/guecstra.hpp" #include "gui/dnd_loot.hpp" -#include "gui/uisystems.hpp" namespace gui { @@ -43,11 +41,11 @@ namespace gui { END(Event::CLOSE); break; case LOOT_SELECT: - $grab_source = UISystem::loot_grab($loot_ui.$gui, data); + $grab_source = start_grab($loot_ui.$gui, data); if($grab_source) state(DNDState::LOOT_GRAB); break; case INV_SELECT: - $grab_source = UISystem::loot_grab($status_ui.$gui, data); + $grab_source = start_grab($status_ui.$gui, data); if($grab_source) state(DNDState::INV_GRAB); break; case MOUSE_DRAG_START: @@ -67,11 +65,11 @@ namespace gui { END(Event::CLOSE); break; case LOOT_SELECT: - $grab_source = UISystem::loot_grab($loot_ui.$gui, data); + $grab_source = start_grab($loot_ui.$gui, data); if($grab_source) state(DNDState::LOOTING); break; case INV_SELECT: - if(UISystem::loot_drop($loot_ui.$gui, + if(commit_drop($loot_ui.$gui, $status_ui.$gui, $grab_source, data)) { state(DNDState::LOOTING); @@ -90,14 +88,14 @@ namespace gui { END(Event::CLOSE); break; case LOOT_SELECT: - if(UISystem::loot_drop($status_ui.$gui, + if(commit_drop($status_ui.$gui, $loot_ui.$gui, $grab_source, data)) { state(DNDState::LOOTING); } break; case INV_SELECT: - $grab_source = UISystem::loot_grab($status_ui.$gui, data); + $grab_source = start_grab($status_ui.$gui, data); state(DNDState::LOOTING); break; default: @@ -128,8 +126,7 @@ namespace gui { switch(ev) { case INV_SELECT: - if(UISystem::loot_drop($loot_ui.$gui, - $status_ui.$gui, $grab_source, data)) + commit_drop($loot_ui.$gui, $status_ui.$gui, $grab_source, data); { END(Event::CLOSE); } @@ -149,7 +146,7 @@ namespace gui { case LOOT_ITEM: { // NOTE: if > 1 items, go to LOOT_OPEN instead auto gui_id = $loot_ui.$gui.entity("item_0"); - $grab_source = UISystem::loot_grab($loot_ui.$gui, gui_id); + $grab_source = start_grab($loot_ui.$gui, gui_id); if($grab_source) { auto& source = $loot_ui.$gui.get(*$grab_source); @@ -160,7 +157,7 @@ namespace gui { } } break; case INV_SELECT: { - $grab_source = UISystem::loot_grab($status_ui.$gui, data); + $grab_source = start_grab($status_ui.$gui, data); if($grab_source) { auto& source = $status_ui.$gui.get(*$grab_source); @@ -206,7 +203,6 @@ namespace gui { case MOUSE_DRAG: case MOUSE_MOVE: { if($grab_source) { - fmt::println("MOVING that thing"); auto& source = gui.get(*$grab_source); source.move($window.mapPixelToCoords($router.position)); } @@ -240,4 +236,32 @@ namespace gui { $window.draw(*$grab_sprite); } } + + std::optional DNDLoot::start_grab(guecs::UI& gui, std::any data) { + auto gui_id = std::any_cast(data); + + if(auto source = gui.get_if(gui_id)) { + source->grab(); + return gui_id; + } else { + return std::nullopt; + } + } + + bool DNDLoot::commit_drop(guecs::UI& source, guecs::UI& target, + std::optional source_id, std::any data) + { + if(!source_id) return false; + auto target_id = std::any_cast(data); + + auto& drop = target.get(target_id); + auto& grab = source.get(*source_id); + + if(drop.commit(grab.world_entity)) { + grab.commit(); + return true; + } else { + return false; + } + } } diff --git a/gui/dnd_loot.hpp b/gui/dnd_loot.hpp index 4e5ff99..b205436 100644 --- a/gui/dnd_loot.hpp +++ b/gui/dnd_loot.hpp @@ -38,11 +38,17 @@ namespace gui { void END(Event ev, std::any data={}); void ITEM_PICKUP(Event ev, std::any data); void INV_PICKUP(Event ev, std::any data); + void handle_mouse(Event ev, guecs::UI& gui); void mouse_action(bool hover); void render(); void open(); void close(); + + std::optional start_grab(guecs::UI& gui, std::any data); + bool commit_drop(guecs::UI& source, guecs::UI& target, + std::optional source_id, std::any data); + sf::Vector2f mouse_position(); }; } diff --git a/gui/fsm.cpp b/gui/fsm.cpp index 28356e8..2ea7494 100644 --- a/gui/fsm.cpp +++ b/gui/fsm.cpp @@ -6,7 +6,6 @@ #include "components.hpp" #include #include "systems.hpp" -#include "gui/uisystems.hpp" #include "gui/fsm_events.hpp" #include "events.hpp" #include "sound.hpp" @@ -69,7 +68,6 @@ namespace gui { run_systems(); - this_crap_must_die(); state(State::IDLE); } @@ -461,8 +459,7 @@ namespace gui { dbc::check(world.has(entity), "INVALID LOOT_ITEM, that entity has no InventoryItem"); dbc::log("@@@@ SENDING LOOT_ITEM"); - auto gui_id = $loot_ui.$gui.entity("item_0"); - $loot_ui.contents.insert_or_assign(gui_id, entity); + $loot_ui.contents.add("item_0", entity); $loot_ui.update(); event(Event::LOOT_ITEM); } break; @@ -517,10 +514,4 @@ namespace gui { run_systems(); } - - void FSM::this_crap_must_die() { - auto torch_id = System::spawn_item($level, "TORCH_BAD"); - auto hand_l = $status_ui.$gui.entity("hand_l"); - $status_ui.place_slot(hand_l, torch_id); - } } diff --git a/gui/fsm.hpp b/gui/fsm.hpp index 2ca27bb..6000820 100644 --- a/gui/fsm.hpp +++ b/gui/fsm.hpp @@ -79,6 +79,5 @@ namespace gui { void handle_world_events(); void next_level(); void debug_render(); - void this_crap_must_die(); }; } diff --git a/gui/loot_ui.cpp b/gui/loot_ui.cpp index f95dbbe..e691516 100644 --- a/gui/loot_ui.cpp +++ b/gui/loot_ui.cpp @@ -40,7 +40,10 @@ namespace gui { make_button("destroy", L"DESTROY", Events::GUI::LOOT_CLOSE); for(int i = 0; i < INV_SLOTS; i++) { - auto id = $gui.entity("item_", i); + auto name = fmt::format("item_{}", i); + auto id = $gui.entity(name); + $slot_to_name.insert_or_assign(id, name); + $gui.set(id, {THEME.PADDING, THEME.TRANSPARENT, THEME.LIGHT_MID }); $gui.set(id, {0.4f, "ui_shader"}); @@ -54,13 +57,12 @@ namespace gui { } void LootUI::update() { - dbc::check(contents.size() < INV_SLOTS, "too many items in loot contents, must be < 16"); - for(size_t i = 0; i < INV_SLOTS; i++) { auto id = $gui.entity("item_", int(i)); + auto& slot_name = $slot_to_name.at(id); - if(contents.contains(id)) { - auto item = contents.at(id); + if(contents.has(slot_name)) { + auto item = contents.get(slot_name); dbc::check($level.world->has(item), "item in inventory UI doesn't exist in world. New level?"); auto& sprite = $level.world->get(item); @@ -85,13 +87,16 @@ namespace gui { } void LootUI::remove_slot(DinkyECS::Entity slot_id) { - contents.erase(slot_id); + auto& name = $slot_to_name.at(slot_id); + contents.remove(name); update(); } - bool LootUI::place_slot(DinkyECS::Entity id, DinkyECS::Entity world_entity) { - if(contents.size() < INV_SLOTS && !contents.contains(id)) { - contents.try_emplace(id, world_entity); + bool LootUI::place_slot(guecs::Entity id, DinkyECS::Entity world_entity) { + auto& name = $slot_to_name.at(id); + + if(!contents.has(name)) { + contents.add(name, world_entity); update(); return true; } else { @@ -105,7 +110,6 @@ namespace gui { void LootUI::update_level(GameLevel &level) { $level = level; - contents.clear(); init(); } diff --git a/gui/loot_ui.hpp b/gui/loot_ui.hpp index 8762bbb..87652e5 100644 --- a/gui/loot_ui.hpp +++ b/gui/loot_ui.hpp @@ -5,6 +5,7 @@ #include #include #include "events.hpp" +#include "inventory.hpp" namespace gui { class LootUI { @@ -12,7 +13,9 @@ namespace gui { bool active = false; guecs::UI $gui; GameLevel $level; - std::unordered_map contents; + std::unordered_map $slot_to_name; + // NOTE: this should then become just an ECS id for a container + inventory::Model contents; LootUI(GameLevel level); void init(); @@ -22,7 +25,7 @@ namespace gui { bool mouse(float x, float y, bool hover); void make_button(const std::string &name, const std::wstring& label, Events::GUI event); - void remove_slot(DinkyECS::Entity slot_id); - bool place_slot(DinkyECS::Entity gui_id, DinkyECS::Entity world_entity); + void remove_slot(guecs::Entity slot_id); + bool place_slot(guecs::Entity gui_id, DinkyECS::Entity world_entity); }; } diff --git a/gui/ritual_ui.cpp b/gui/ritual_ui.cpp index 3eb300e..a788e42 100644 --- a/gui/ritual_ui.cpp +++ b/gui/ritual_ui.cpp @@ -47,6 +47,7 @@ namespace gui { $ritual_ui = textures::get("ritual_crafting_area"); $ritual_ui.sprite->setPosition($gui.get_position()); $ritual_ui.sprite->setTextureRect($ritual_closed_rect); + // BUG: why am I doing this twice? state(State::CLOSED); $ritual_anim = animation::load("ritual_blanket"); diff --git a/gui/ritual_ui.hpp b/gui/ritual_ui.hpp index 431617a..2f8aa63 100644 --- a/gui/ritual_ui.hpp +++ b/gui/ritual_ui.hpp @@ -27,7 +27,7 @@ namespace gui { }; struct SelectedItem { - DinkyECS::Entity slot_id; + guecs::Entity slot_id; DinkyECS::Entity item_id; }; diff --git a/gui/status_ui.cpp b/gui/status_ui.cpp index 1d82542..ce998c9 100644 --- a/gui/status_ui.cpp +++ b/gui/status_ui.cpp @@ -5,6 +5,7 @@ #include #include "gui/guecstra.hpp" #include "systems.hpp" +#include "inventory.hpp" namespace gui { using namespace guecs; @@ -27,28 +28,29 @@ namespace gui { $gui.set($gui.MAIN, {$gui.$parser}); for(auto& [name, cell] : $gui.cells()) { + auto gui_id = $gui.entity(name); + $slot_to_name.insert_or_assign(gui_id, name); + if(name == "character_view") { - auto char_view = $gui.entity(name); - $gui.set(char_view, {}); - $gui.set(char_view, {"armored_knight"}); + $gui.set(gui_id, {}); + $gui.set(gui_id, {"armored_knight"}); } else { - auto button = $gui.entity(name); - $gui.set(button, {}); - $gui.set(button, {make_any(name)}); + $gui.set(gui_id, {}); + $gui.set(gui_id, {make_any(name)}); if(name == "ritual_ui") { - $gui.set(button, { + $gui.set(gui_id, { [this](auto, auto){ select_ritual(); } }); - $gui.set(button, {"pickup"}); + $gui.set(gui_id, {"pickup"}); } else { - $gui.set(button, {guecs::to_wstring(name)}); - $gui.set(button, { - guecs::make_action($level, Events::GUI::INV_SELECT, {button}) + $gui.set(gui_id, {guecs::to_wstring(name)}); + $gui.set(gui_id, { + guecs::make_action($level, Events::GUI::INV_SELECT, {gui_id}) }); - $gui.set(button, { - .commit=[&, button](DinkyECS::Entity world_target) -> bool { - return place_slot(button, world_target); + $gui.set(gui_id, { + .commit=[&, gui_id](DinkyECS::Entity world_target) -> bool { + return place_slot(gui_id, world_target); } }); } @@ -57,6 +59,7 @@ namespace gui { $ritual_ui.event(ritual::Event::STARTED); $gui.init(); + update(); } bool StatusUI::mouse(float x, float y, bool hover) { @@ -72,7 +75,17 @@ namespace gui { } void StatusUI::update() { - dbc::log("REWRITE ME!"); + auto& inventory = $level.world->get_the(); + for(auto& [slot, world_entity] : inventory.by_slot) { + auto gui_id = $gui.entity(slot); + + auto& sprite = $level.world->get(world_entity); + $gui.set_init(gui_id, {sprite.name}); + guecs::GrabSource grabber{ world_entity, + [&, gui_id]() { return remove_slot(gui_id); }}; + grabber.setSprite($gui, gui_id); + $gui.set(gui_id, grabber); + } } void StatusUI::render(sf::RenderWindow &window) { @@ -87,30 +100,30 @@ namespace gui { } bool StatusUI::place_slot(guecs::Entity gui_id, DinkyECS::Entity world_entity) { - if($level.world->has(world_entity)) { - auto& sprite = $level.world->get(world_entity); - $gui.set_init(gui_id, {sprite.name}); - guecs::GrabSource grabber{ world_entity, - [&, gui_id]() { return remove_slot(gui_id); }}; - grabber.setSprite($gui, gui_id); - $gui.set(gui_id, grabber); - - contents.insert_or_assign(gui_id, world_entity); - return true; - } else { - return false; - } + auto& slot_name = $slot_to_name.at(gui_id); + auto& inventory = $level.world->get_the(); + inventory.add(slot_name, world_entity); + update(); + return true; } bool StatusUI::drop_item(DinkyECS::Entity item_id) { - return System::drop_item($level, item_id); + bool dropped = System::drop_item($level, item_id); + if(dropped) update(); + return dropped; } + // NOTE: do I need this or how does it relate to drop_item? void StatusUI::remove_slot(guecs::Entity slot_id) { - if(contents.contains(slot_id)) { - contents.erase(slot_id); - $gui.remove(slot_id); - $gui.remove(slot_id); - } + // NOTE: really the System should coordinate either dropping on the + // ground or moving from one container or another, so when loot_ui + // moves to use an ECS id to a container I can have the System + // do it. + auto& slot_name = $slot_to_name.at(slot_id); + auto& inventory = $level.world->get_the(); + inventory.remove(slot_name); + + $gui.remove(slot_id); + $gui.remove(slot_id); } } diff --git a/gui/status_ui.hpp b/gui/status_ui.hpp index a900ede..5830efe 100644 --- a/gui/status_ui.hpp +++ b/gui/status_ui.hpp @@ -12,7 +12,7 @@ namespace gui { public: guecs::UI $gui; GameLevel $level; - std::unordered_map contents; + std::unordered_map $slot_to_name; ritual::UI $ritual_ui; StatusUI(GameLevel level); diff --git a/gui/uisystems.cpp b/gui/uisystems.cpp deleted file mode 100644 index 780c20a..0000000 --- a/gui/uisystems.cpp +++ /dev/null @@ -1,32 +0,0 @@ -#include "gui/uisystems.hpp" -#include "gui/guecstra.hpp" - -namespace UISystem { - std::optional loot_grab(guecs::UI& gui, std::any data) { - auto gui_id = std::any_cast(data); - - if(auto source = gui.get_if(gui_id)) { - source->grab(); - return gui_id; - } else { - return std::nullopt; - } - } - - bool loot_drop(guecs::UI& source, guecs::UI& target, - std::optional source_id, std::any data) - { - if(!source_id) return false; - auto target_id = std::any_cast(data); - - auto& drop = target.get(target_id); - auto& grab = source.get(*source_id); - - if(drop.commit(grab.world_entity)) { - grab.commit(); - return true; - } else { - return false; - } - } -} diff --git a/gui/uisystems.hpp b/gui/uisystems.hpp deleted file mode 100644 index ebc695f..0000000 --- a/gui/uisystems.hpp +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once -#include -#include - -namespace UISystem { - std::optional loot_grab(guecs::UI& gui, std::any data); - - bool loot_drop(guecs::UI& source, guecs::UI& target, - std::optional source_id, std::any data); -} diff --git a/inventory.cpp b/inventory.cpp new file mode 100644 index 0000000..34db973 --- /dev/null +++ b/inventory.cpp @@ -0,0 +1,65 @@ +#include "inventory.hpp" + +namespace inventory { + void Model::add(const Slot &in_slot, DinkyECS::Entity ent) { + if(by_entity.contains(ent)) { + // doing it this way so we can get the offending entity, otherwise it + // crashes on the by_entity.at when this test _passes_ + dbc::sentinel(fmt::format("failed to add item to inventory, entity {} is already in the inventory slot {}", ent, by_entity.at(ent))); + } + + by_entity.insert_or_assign(ent, in_slot); + by_slot.insert_or_assign(in_slot, ent); + + invariant(); + } + + Slot& Model::get(DinkyECS::Entity ent) { + return by_entity.at(ent); + } + + DinkyECS::Entity Model::get(const Slot& slot) { + return by_slot.at(slot); + } + + bool Model::has(DinkyECS::Entity ent) { + return by_entity.contains(ent); + } + + bool Model::has(const Slot& slot) { + return by_slot.contains(slot); + } + + void Model::remove(const Slot& slot, DinkyECS::Entity ent) { + by_entity.erase(ent); + by_slot.erase(slot); + invariant(); + } + + void Model::remove(DinkyECS::Entity ent) { + auto& slot = by_entity.at(ent); + remove(slot, ent); + invariant(); + } + + void Model::remove(const Slot& slot) { + auto ent = by_slot.at(slot); + remove(slot, ent); + invariant(); + } + + void Model::invariant() { + dbc::check(by_slot.size() == by_entity.size(), "by_slot and by_entity have differing sizes"); + // std::unordered_map find_dupes; + + for(auto& [slot, ent] : by_slot) { + dbc::check(by_entity.at(ent) == slot, + fmt::format("mismatched slot {} in by_slot doesn't match entity {}", slot, ent)); + } + + for(auto& [ent, slot] : by_entity) { + dbc::check(by_slot.at(slot) == ent, + fmt::format("mismatched entity {} in by_entity doesn't match entity {}", ent, slot)); + } + } +} diff --git a/inventory.hpp b/inventory.hpp new file mode 100644 index 0000000..782cbf9 --- /dev/null +++ b/inventory.hpp @@ -0,0 +1,21 @@ +#include "dinkyecs.hpp" +#include + +namespace inventory { + using Slot = std::string; + + struct Model { + std::unordered_map by_slot; + std::unordered_map by_entity; + + void add(const Slot &in_slot, DinkyECS::Entity ent); + Slot& get(DinkyECS::Entity ent); + DinkyECS::Entity get(const Slot& slot); + bool has(DinkyECS::Entity ent); + bool has(const Slot& slot); + void remove(const Slot& slot, DinkyECS::Entity ent); + void remove(DinkyECS::Entity ent); + void remove(const Slot& slot); + void invariant(); + }; +} diff --git a/meson.build b/meson.build index 647c36d..c4b211d 100644 --- a/meson.build +++ b/meson.build @@ -106,7 +106,7 @@ sources = [ 'gui/overlay_ui.cpp', 'gui/ritual_ui.cpp', 'gui/status_ui.cpp', - 'gui/uisystems.cpp', + 'inventory.cpp', 'levelmanager.cpp', 'lights.cpp', 'map.cpp', @@ -139,6 +139,7 @@ executable('runtests', sources + [ 'tests/easings.cpp', 'tests/event_router.cpp', 'tests/fsm.cpp', + 'tests/inventory.cpp', 'tests/levelmanager.cpp', 'tests/lighting.cpp', 'tests/loot.cpp', diff --git a/systems.cpp b/systems.cpp index d02be0e..6d862a4 100644 --- a/systems.cpp +++ b/systems.cpp @@ -15,6 +15,7 @@ #include "battle.hpp" #include #include "shaders.hpp" +#include "inventory.hpp" using std::string; using namespace fmt; @@ -461,12 +462,12 @@ std::shared_ptr System::sprite_effect(GameLevel &level, DinkyECS::En } } -DinkyECS::Entity System::spawn_item(GameLevel& level, const std::string& name) { +DinkyECS::Entity System::spawn_item(DinkyECS::World& world, const std::string& name) { Config config("assets/items.json"); auto& item_config = config[name]; - auto item_id = level.world->entity(); - level.world->set(item_id, {1, item_config}); - components::configure_entity(*level.world, item_id, item_config["components"]); + auto item_id = world.entity(); + world.set(item_id, {1, item_config}); + components::configure_entity(world, item_id, item_config["components"]); return item_id; } @@ -478,13 +479,17 @@ bool System::drop_item(GameLevel& level, DinkyECS::Entity item) { auto player = world.get_the(); auto player_pos = world.get(player.entity); + auto& player_inv = world.get_the(); // doesn't compass already avoid walls? - for(matrix::box it{map.walls(), player_pos.location.x, player_pos.location.y, 1}; it.next();) { + for(matrix::box it{map.walls(), player_pos.location.x, player_pos.location.y, 1}; it.next();) + { Position pos{it.x, it.y}; if(map.can_move(pos.location) && !collision.occupied(pos.location)) { world.set(item, pos); collision.insert(pos.location, item); + // BUG: really there should be another system that handles loot->inv moves + if(player_inv.has(item)) player_inv.remove(item); level.world->send(Events::GUI::ENEMY_SPAWN, item, {}); return true; } diff --git a/systems.hpp b/systems.hpp index d071175..6f85ff4 100644 --- a/systems.hpp +++ b/systems.hpp @@ -18,7 +18,7 @@ namespace System { void device(DinkyECS::World &world, DinkyECS::Entity actor, DinkyECS::Entity item); void plan_motion(DinkyECS::World& world, Point move_to); std::wstring draw_map(GameLevel level, size_t view_x, size_t view_y, int compass_dir); - DinkyECS::Entity spawn_item(GameLevel& level, const std::string& name); + DinkyECS::Entity spawn_item(DinkyECS::World& world, const std::string& name); bool drop_item(GameLevel& level, DinkyECS::Entity item); void enemy_ai(GameLevel &level); diff --git a/tests/inventory.cpp b/tests/inventory.cpp new file mode 100644 index 0000000..ecdcca0 --- /dev/null +++ b/tests/inventory.cpp @@ -0,0 +1,47 @@ +#include +#include +#include +#include "inventory.hpp" + +using namespace fmt; + +TEST_CASE("base test", "[inventory]") { + inventory::Model inv; + DinkyECS::Entity test_ent = 1; + + inv.add("hand_l", test_ent); + inv.invariant(); + + auto& slot = inv.get(test_ent); + REQUIRE(slot == "hand_l"); + + auto ent = inv.get(slot); + REQUIRE(ent == test_ent); + + REQUIRE(inv.has(ent)); + REQUIRE(inv.has(slot)); + + // test base remove + inv.remove(slot, ent); + REQUIRE(!inv.has(slot)); + REQUIRE(!inv.has(ent)); + + // test remove just by slot + inv.add("hand_r", test_ent); + REQUIRE(inv.has("hand_r")); + REQUIRE(inv.has(test_ent)); + inv.invariant(); + + inv.remove("hand_r"); + REQUIRE(!inv.has("hand_r")); + REQUIRE(!inv.has(test_ent)); + + // test remove just by entity + inv.add("pocket_l", test_ent); + REQUIRE(inv.has("pocket_l")); + REQUIRE(inv.has(test_ent)); + inv.invariant(); + inv.remove(test_ent); + REQUIRE(!inv.has("pocket_l")); + REQUIRE(!inv.has(test_ent)); +} diff --git a/tests/matrix.cpp b/tests/matrix.cpp index 6e5641e..cfefaf5 100644 --- a/tests/matrix.cpp +++ b/tests/matrix.cpp @@ -14,6 +14,7 @@ using std::string; using matrix::Matrix; shared_ptr make_map() { + // BUG? I mean, it's a shared_ptr so it should keep it around but.... LevelManager levels; GameLevel level = levels.current(); return level.map; diff --git a/worldbuilder.cpp b/worldbuilder.cpp index dc8e013..7d235c4 100644 --- a/worldbuilder.cpp +++ b/worldbuilder.cpp @@ -6,6 +6,8 @@ #include "rituals.hpp" #include "maze.hpp" #include "textures.hpp" +#include "inventory.hpp" +#include "systems.hpp" using namespace fmt; using namespace components; @@ -172,6 +174,11 @@ void WorldBuilder::configure_starting_items(DinkyECS::World &world) { ritual::JunkItem name = el; blanket.add(name); }; + + auto torch_id = System::spawn_item(world, "TORCH_BAD"); + + auto &inventory = world.get_the(); + inventory.add("hand_r", torch_id); } void WorldBuilder::place_entities(DinkyECS::World &world) { @@ -202,6 +209,7 @@ void WorldBuilder::place_entities(DinkyECS::World &world) { world.set_the(player); world.set_the({}); world.set_the({}); + world.set_the({}); configure_starting_items(world); world.make_constant(player.entity); }