#include "gui/guecstra.hpp" #include "gui/dnd_loot.hpp" namespace gui { DNDLoot::DNDLoot(StatusUI& status_ui, LootUI& loot_ui, sf::RenderWindow &window, routing::Router& router) : $status_ui(status_ui), $loot_ui(loot_ui), $window(window), $router(router) { event(Event::STARTED); } bool DNDLoot::event(Event ev, std::any data) { switch($state) { FSM_STATE(DNDState, START, ev); FSM_STATE(DNDState, LOOTING, ev, data); FSM_STATE(DNDState, LOOT_GRAB, ev, data); FSM_STATE(DNDState, INV_GRAB, ev, data); FSM_STATE(DNDState, ITEM_PICKUP, ev, data); FSM_STATE(DNDState, INV_PICKUP, ev, data); FSM_STATE(DNDState, END, ev, data); default: dbc::log(fmt::format("event received with data but state={} is not handled", int($state))); } return !in_state(DNDState::END); } void DNDLoot::START(Event ev) { dbc::check(ev == Event::STARTED, "START not given a STARTED event."); END(Event::CLOSE); } void DNDLoot::LOOTING(Event ev, std::any data) { using enum Event; switch(ev) { case LOOT_OPEN: END(Event::CLOSE); break; case LOOT_SELECT: $grab_source = start_grab($loot_ui.$gui, data); if($grab_source) state(DNDState::LOOT_GRAB); break; case INV_SELECT: $grab_source = start_grab($status_ui.$gui, data); if($grab_source) state(DNDState::INV_GRAB); break; default: state(DNDState::LOOTING); } } void DNDLoot::LOOT_GRAB(Event ev, std::any data) { using enum Event; switch(ev) { case LOOT_OPEN: END(Event::CLOSE); break; case LOOT_SELECT: $grab_source = start_grab($loot_ui.$gui, data); if($grab_source) state(DNDState::LOOTING); break; case INV_SELECT: if(commit_drop($loot_ui.$gui, $status_ui.$gui, $grab_source, data)) { state(DNDState::LOOTING); } break; default: handle_mouse(ev, $loot_ui.$gui); } } void DNDLoot::INV_GRAB(Event ev, std::any data) { using enum Event; switch(ev) { case LOOT_OPEN: END(Event::CLOSE); break; case LOOT_SELECT: if(commit_drop($status_ui.$gui, $loot_ui.$gui, $grab_source, data)) { state(DNDState::LOOTING); } break; case INV_SELECT: $grab_source = start_grab($status_ui.$gui, data); state(DNDState::LOOTING); break; default: handle_mouse(ev, $status_ui.$gui); } } void DNDLoot::INV_PICKUP(Event ev, std::any data) { using enum Event; switch(ev) { case AIM_CLICK: { bool worked = throw_on_floor(); dbc::check(worked, "Need to fix this, should be able to abort."); END(Event::CLOSE); } break; case INV_SELECT: // BUG: should I do a bool here and not transition? commit_move($status_ui.$gui, $grab_source, data); END(Event::CLOSE); break; default: handle_mouse(ev, $status_ui.$gui); } } void DNDLoot::ITEM_PICKUP(Event ev, std::any data) { using enum Event; switch(ev) { case INV_SELECT: if(commit_drop($loot_ui.$gui, $status_ui.$gui, $grab_source, data)) { END(Event::CLOSE); } break; case LOOT_ITEM: break; default: handle_mouse(ev, $loot_ui.$gui); } } void DNDLoot::END(Event ev, std::any data) { using enum Event; switch(ev) { case LOOT_ITEM: { // NOTE: if > 1 items, go to LOOT_OPEN instead auto gui_id = $loot_ui.$gui.entity("item_0"); $grab_source = start_grab($loot_ui.$gui, gui_id); if($grab_source) { auto& source = $loot_ui.$gui.get(*$grab_source); $grab_sprite = source.sprite; // call this once to properly position the sprite handle_mouse(Event::MOUSE_MOVE, $loot_ui.$gui); state(DNDState::ITEM_PICKUP); } } break; case INV_SELECT: { $grab_source = start_grab($status_ui.$gui, data); if($grab_source) { auto& source = $status_ui.$gui.get(*$grab_source); $grab_sprite = source.sprite; state(DNDState::INV_PICKUP); } else { dbc::log("inv slot empty"); } } break; case LOOT_OPEN: open(); state(DNDState::LOOTING); break; case CLOSE: // called the first time transitioning to END close(); state(DNDState::END); break; case TICK: // ignored break; default: dbc::sentinel(fmt::format("invalid event: {}", int(ev))); } } void DNDLoot::handle_mouse(Event ev, guecs::UI& gui) { using enum Event; switch(ev) { case MOUSE_DRAG: case MOUSE_MOVE: { if($grab_source) { auto& source = gui.get(*$grab_source); source.move($window.mapPixelToCoords($router.position)); } } break; default: break; // ignored } } void DNDLoot::open() { $grab_source = std::nullopt; $grab_sprite = nullptr; $loot_ui.active = true; } void DNDLoot::close() { $grab_source = std::nullopt; $grab_sprite = nullptr; $loot_ui.active = false; } void DNDLoot::render() { if($grab_source && $grab_sprite) { $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); fmt::println("!!!!!!!!!source_id = {} target_id = {}", *source_id, target_id); dbc::check(target.has(target_id), "gui does not have a DropTarget at that slot"); dbc::check(source.has(*source_id), "gui does not have a GrabSource at that slot"); 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; } } void DNDLoot::commit_move(guecs::UI& gui, std::optional source_id, std::any data) { auto& grab = gui.get(*source_id); grab.commit(); auto drop_id = std::any_cast(data); auto& drop = gui.get(drop_id); drop.commit(grab.world_entity); // BUG: if the drop fails then need to put the grab back? // How to confirm the drop will work before doing it? // Or, maybe save the commit? } /* * Dropping on the ground is only possible from the * status_ui for now. */ bool DNDLoot::throw_on_floor() { dbc::check($grab_source != std::nullopt, "attempt to commit_drop but no grab_source set"); auto& grab = $status_ui.$gui.get(*$grab_source); grab.commit(); return $status_ui.drop_item(grab.world_entity); } }