diff --git a/Makefile b/Makefile index 9999d56..6b74d7b 100644 --- a/Makefile +++ b/Makefile @@ -26,7 +26,7 @@ tracy_build: meson compile -j 10 -C builddir test: build - ./builddir/runtests "[loot]" + ./builddir/runtests run: build test ifeq '$(OS)' 'Windows_NT' @@ -49,7 +49,7 @@ clean: meson compile --clean -C builddir debug_test: build - gdb --nx -x .gdbinit --ex run --args builddir/runtests -e "[loot]" + gdb --nx -x .gdbinit --ex run --args builddir/runtests -e win_installer: powershell 'start "C:\Program Files (x86)\solicus\InstallForge\bin\ifbuilderenvx86.exe" scripts\win_installer.ifp' diff --git a/constants.hpp b/constants.hpp index 46fb856..d78c94e 100644 --- a/constants.hpp +++ b/constants.hpp @@ -3,7 +3,7 @@ #include #include -constexpr const int INV_SLOTS=20; +constexpr const int INV_SLOTS=16; constexpr const int TEXTURE_WIDTH=256; constexpr const int TEXTURE_HEIGHT=256; constexpr const int RAY_VIEW_WIDTH=900; diff --git a/events.hpp b/events.hpp index f0bd2e0..5de8e2a 100644 --- a/events.hpp +++ b/events.hpp @@ -5,7 +5,8 @@ namespace Events { START, COMBAT, LOOT, DEATH, STAIRS_UP, STAIRS_DOWN, TRAP, COMBAT_START, NO_NEIGHBORS, HP_STATUS, ATTACK, BLOCK, EVADE, NEW_RITUAL, - UPDATE_SPRITE, ENEMY_SPAWN, NOOP, LOOT_CLOSE + UPDATE_SPRITE, ENEMY_SPAWN, NOOP, + LOOT_CLOSE, LOOT_SELECT, LOOT_PLACE }; struct Combat { diff --git a/gui/fsm.cpp b/gui/fsm.cpp index 513c100..6f1bd80 100644 --- a/gui/fsm.cpp +++ b/gui/fsm.cpp @@ -38,8 +38,16 @@ namespace gui { FSM_STATE(State, IN_COMBAT, ev); FSM_STATE(State, COMBAT_ROTATE, ev); FSM_STATE(State, NEXT_LEVEL, ev); - FSM_STATE(State, LOOTING, ev); FSM_STATE(State, END, ev); + FSM_STATE(State, LOOTING, ev, std::make_any(true)); + } + } + + void FSM::event(Event ev, std::any data) { + switch($state) { + FSM_STATE(State, LOOTING, ev, data); + default: + dbc::log(fmt::format("event given with data but event={} is not handled", int(ev))); } } @@ -110,7 +118,7 @@ namespace gui { } } - void FSM::LOOTING(Event ev) { + void FSM::LOOTING(Event ev, std::any data) { using enum Event; switch(ev) { @@ -118,6 +126,19 @@ namespace gui { $loot_ui.active = false; state(State::IDLE); break; + case LOOT_SELECT: { + int slot_id = std::any_cast(data); + + if(auto entity = $loot_ui.select_slot(slot_id)) { + fmt::println("LOOT SELECT slot={} has entity={}", slot_id, entity.value()); + $status_ui.select_slot(slot_id, entity.value()); + } + } break; + case LOOT_PLACE: { + std::string slot_name = std::any_cast(data); + int slot_id = $status_ui.place_slot(slot_name); + $loot_ui.remove_slot(slot_id); + } break; case TICK: // do nothing break; @@ -190,6 +211,9 @@ namespace gui { state(State::LOOTING); } break; + case LOOT_PLACE: + // ignored + break; default: dbc::sentinel("unhandled event in IDLE"); } @@ -439,6 +463,12 @@ namespace gui { // BUG: need to resolve GUI events vs. FSM events better event(Event::LOOT_OPEN); break; + case eGUI::LOOT_SELECT: + event(Event::LOOT_SELECT, data); + break; + case eGUI::LOOT_PLACE: + event(Event::LOOT_PLACE, data); + break; case eGUI::LOOT: { $map_ui.log(L"You picked up an item."); } break; diff --git a/gui/fsm.hpp b/gui/fsm.hpp index ca09627..43019e7 100644 --- a/gui/fsm.hpp +++ b/gui/fsm.hpp @@ -41,7 +41,9 @@ namespace gui { STOP_COMBAT = 12, STAIRS_DOWN = 13, LOOT_OPEN=14, - QUIT = 15 + LOOT_SELECT=15, + LOOT_PLACE=16, + QUIT = 17 }; class FSM : public DeadSimpleFSM { @@ -66,6 +68,7 @@ namespace gui { FSM(); void event(Event ev); + void event(Event ev, std::any data); void autowalk(); void start_autowalk(double rot_speed); @@ -78,7 +81,7 @@ namespace gui { void IN_COMBAT(Event ev); void COMBAT_ROTATE(Event ev); void NEXT_LEVEL(Event ev); - void LOOTING(Event ev); + void LOOTING(Event ev, std::any data); void END(Event ev); void try_move(int dir, bool strafe); diff --git a/gui/guecstra.cpp b/gui/guecstra.cpp index de974be..cbb1bf3 100644 --- a/gui/guecstra.cpp +++ b/gui/guecstra.cpp @@ -4,6 +4,8 @@ namespace guecs { Clickable make_action(DinkyECS::World& target, Events::GUI event) { return {[&, event](auto ent, auto data){ + // BUG: I think entityt here shifted and isn't part of the world anymore + // BUG: it's actually coming from the GUI so passing it here is wrong // remember that ent is passed in from the UI::mouse handler target.send(event, ent, data); }}; @@ -11,6 +13,8 @@ namespace guecs { Clickable make_action(DinkyECS::World& target, Events::GUI event, std::any data) { return {[&, event, data](auto ent, auto){ + // BUG: I think entityt here shifted and isn't part of the world anymore + // BUG: it's actually coming from the GUI so passing it here is wrong // remember that ent is passed in from the UI::mouse handler target.send(event, ent, data); }}; diff --git a/gui/loot_ui.cpp b/gui/loot_ui.cpp index 2e7594f..6498191 100644 --- a/gui/loot_ui.cpp +++ b/gui/loot_ui.cpp @@ -33,34 +33,57 @@ namespace gui { $gui.set(close, guecs::make_action(*$level.world, Events::GUI::LOOT_CLOSE)); + for(int i = 0; i < INV_SLOTS; i++) { + auto id = $gui.entity("item_", i); + $gui.set(id, {THEME.PADDING, + THEME.TRANSPARENT, THEME.LIGHT_MID }); + $gui.set(id, {0.4f, "ui_shader"}); + $gui.set(id, { + guecs::make_action(*$level.world, Events::GUI::LOOT_SELECT, {i}) + }); + } + $gui.init(); update(); } + std::optional LootUI::select_slot(int slot_id) { + if(size_t(slot_id) < contents.size()) { + return contents.at(slot_id); + } else { + return std::nullopt; + } + } + + void LootUI::remove_slot(int slot_id) { + dbc::check(size_t(slot_id) < contents.size(), + fmt::format("invalid slot id {} give, contents.size={}", + slot_id, contents.size())); + + contents.erase(contents.begin() + slot_id); + update(); + } + void LootUI::update() { - dbc::check(contents.size() < 16, "too many items in loot contents, must be < 16"); - for(int i = 0; i < 16; i++) { - auto id = $gui.entity("item_", i); - if($gui.has(id)) { - $gui.remove(id); - $gui.remove(id); - $gui.remove(id); + 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)); + + fmt::println("checking for sprite at {}", id); + if($gui.has(id)) { + fmt::println("REMOVING SPRITE {}", id); $gui.remove(id); + } else { + fmt::println("nothing at {}", id); } - } - for(size_t item_i = 0; item_i < contents.size(); item_i++) { - auto id = $gui.entity("item_", int(item_i)); - $gui.set_init(id, {THEME.PADDING, - THEME.TRANSPARENT, THEME.LIGHT_MID }); - $gui.set_init(id, {0.4f, "ui_shader"}); - $gui.set(id, { - [=](auto, auto) { fmt::println("clicked button"); } - }); - - auto item = contents.at(item_i); - auto& sprite = $level.world->get(item); - $gui.set_init(id, {sprite.name}); + if(i < contents.size()) { + auto item = contents.at(i); + auto& sprite = $level.world->get(item); + fmt::println("NEW SPRITE SPRITE {}", sprite.name); + $gui.set_init(id, {sprite.name}); + } } } diff --git a/gui/loot_ui.hpp b/gui/loot_ui.hpp index 4046afb..e329277 100644 --- a/gui/loot_ui.hpp +++ b/gui/loot_ui.hpp @@ -20,5 +20,7 @@ namespace gui { void render(sf::RenderWindow& window); void update_level(GameLevel &level); bool mouse(float x, float y, bool hover); + std::optional select_slot(int slot); + void remove_slot(int slot_id); }; } diff --git a/gui/status_ui.cpp b/gui/status_ui.cpp index fd85d34..e014bfb 100644 --- a/gui/status_ui.cpp +++ b/gui/status_ui.cpp @@ -3,6 +3,7 @@ #include #include "rand.hpp" #include +#include "gui/guecstra.hpp" namespace gui { using namespace guecs; @@ -19,13 +20,6 @@ namespace gui { "[hand_r|_|_ |hand_l]" "[ring_r|_|_ |ring_l]" "[pocket_r|armor_leg|pocket_l]"); - - size_t inv_id = 0; - for(auto [name, entity] : $gui.$name_ents) { - if(name.starts_with("inv_")) { - $slots[name] = inv_id++; - } - } } void StatusUI::init() { @@ -49,7 +43,7 @@ namespace gui { } else { $gui.set(button, {guecs::to_wstring(name)}); $gui.set(button, { - [this](auto ent, auto data){ select_slot(ent, data); } + guecs::make_action(*$level.world, Events::GUI::LOOT_PLACE, {name}) }); } } @@ -71,13 +65,6 @@ namespace gui { $ritual_ui.event(ritual::Event::TOGGLE); } - void StatusUI::select_slot(DinkyECS::Entity ent, any slot_name) { - (void)ent; - (void)slot_name; - dbc::log("REWRITE!"); - } - - /* WARNING: This is really not the greatest way to do this. */ void StatusUI::update() { dbc::log("REWRITE ME!"); } @@ -92,4 +79,22 @@ namespace gui { $level = level; init(); } + + void StatusUI::select_slot(int slot_id, DinkyECS::Entity entity) { + $selected_slot = slot_id; + $selected_entity = entity; + } + + int StatusUI::place_slot(const std::string &name) { + fmt::println("LOOT slot={}, entity={} PLACE into slot={}", + $selected_slot, $selected_entity, name); + + auto& sprite = $level.world->get($selected_entity); + auto gui_id = $gui.entity(name); + $gui.set_init(gui_id, {sprite.name}); + + $slots.insert_or_assign(name, $selected_entity); + + return $selected_slot; + } } diff --git a/gui/status_ui.hpp b/gui/status_ui.hpp index 79982e4..4f54bd4 100644 --- a/gui/status_ui.hpp +++ b/gui/status_ui.hpp @@ -10,17 +10,20 @@ namespace gui { class StatusUI { public: guecs::UI $gui; - std::map $slots; + std::unordered_map $slots; GameLevel $level; ritual::UI $ritual_ui; + int $selected_slot; + DinkyECS::Entity $selected_entity; StatusUI(GameLevel level); - void select_slot(DinkyECS::Entity ent, std::any data); void select_ritual(); void update_level(GameLevel &level); bool mouse(float x, float y, bool hover); void init(); void render(sf::RenderWindow &window); void update(); + void select_slot(int slot_id, DinkyECS::Entity entity); + int place_slot(const std::string &name); }; } diff --git a/tests/map.cpp b/tests/map.cpp index bd251da..5e2b1cb 100644 --- a/tests/map.cpp +++ b/tests/map.cpp @@ -15,6 +15,7 @@ json load_test_data(const string &fname) { } TEST_CASE("camera control", "[map]") { + components::init(); LevelManager levels; GameLevel level = levels.current(); auto &map = *level.map; @@ -32,6 +33,7 @@ TEST_CASE("camera control", "[map]") { } TEST_CASE("map placement test", "[map:placement]") { + components::init(); for(int i = 0; i < 20; i++) { LevelManager levels; GameLevel level = levels.current(); diff --git a/tests/matrix.cpp b/tests/matrix.cpp index ba672e1..6e5641e 100644 --- a/tests/matrix.cpp +++ b/tests/matrix.cpp @@ -256,6 +256,7 @@ TEST_CASE("prototype circle algorithm", "[matrix:circle]") { } TEST_CASE("viewport iterator", "[matrix:viewport]") { + components::init(); size_t width = Random::uniform(20, 22); size_t height = Random::uniform(21, 25); shared_ptr map = make_map(); @@ -279,6 +280,7 @@ TEST_CASE("viewport iterator", "[matrix:viewport]") { } TEST_CASE("random rectangle", "[matrix:rando_rect]") { + components::init(); for(int i = 0; i < 5; i++) { shared_ptr map = make_map(); map->invert_space(); @@ -303,6 +305,7 @@ TEST_CASE("random rectangle", "[matrix:rando_rect]") { } TEST_CASE("standard rectangle", "[matrix:rectangle]") { + components::init(); for(int i = 0; i < 5; i++) { shared_ptr map = make_map(); auto wall_copy = map->walls(); diff --git a/tests/textures.cpp b/tests/textures.cpp index aec4d8c..68391a4 100644 --- a/tests/textures.cpp +++ b/tests/textures.cpp @@ -7,6 +7,7 @@ using namespace fmt; TEST_CASE("test texture management", "[textures]") { + components::init(); textures::init(); auto spider = textures::get("hairy_spider"); REQUIRE(spider.sprite != nullptr);