Basic loot UI mostly working. Each time you open there's a torch and you can place it visually on any slot on your character.
This commit is contained in:
parent
4b34de2109
commit
5c47a0151c
13 changed files with 123 additions and 46 deletions
4
Makefile
4
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'
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
#include <string>
|
||||
#include <array>
|
||||
|
||||
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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
34
gui/fsm.cpp
34
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<bool>(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<int>(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<std::string>(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;
|
||||
|
|
|
@ -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<State, Event> {
|
||||
|
@ -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);
|
||||
|
|
|
@ -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<Events::GUI>(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<Events::GUI>(event, ent, data);
|
||||
}};
|
||||
|
|
|
@ -33,34 +33,57 @@ namespace gui {
|
|||
$gui.set<guecs::Clickable>(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<guecs::Rectangle>(id, {THEME.PADDING,
|
||||
THEME.TRANSPARENT, THEME.LIGHT_MID });
|
||||
$gui.set<guecs::Effect>(id, {0.4f, "ui_shader"});
|
||||
$gui.set<guecs::Clickable>(id, {
|
||||
guecs::make_action(*$level.world, Events::GUI::LOOT_SELECT, {i})
|
||||
});
|
||||
}
|
||||
|
||||
$gui.init();
|
||||
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<guecs::Rectangle>(id)) {
|
||||
$gui.remove<guecs::Rectangle>(id);
|
||||
$gui.remove<guecs::Effect>(id);
|
||||
$gui.remove<guecs::Clickable>(id);
|
||||
$gui.remove<guecs::Sprite>(id);
|
||||
}
|
||||
std::optional<DinkyECS::Entity> LootUI::select_slot(int slot_id) {
|
||||
if(size_t(slot_id) < contents.size()) {
|
||||
return contents.at(slot_id);
|
||||
} else {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
for(size_t item_i = 0; item_i < contents.size(); item_i++) {
|
||||
auto id = $gui.entity("item_", int(item_i));
|
||||
$gui.set_init<guecs::Rectangle>(id, {THEME.PADDING,
|
||||
THEME.TRANSPARENT, THEME.LIGHT_MID });
|
||||
$gui.set_init<guecs::Effect>(id, {0.4f, "ui_shader"});
|
||||
$gui.set<guecs::Clickable>(id, {
|
||||
[=](auto, auto) { fmt::println("clicked button"); }
|
||||
});
|
||||
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()));
|
||||
|
||||
auto item = contents.at(item_i);
|
||||
auto& sprite = $level.world->get<components::Sprite>(item);
|
||||
$gui.set_init<guecs::Sprite>(id, {sprite.name});
|
||||
contents.erase(contents.begin() + slot_id);
|
||||
update();
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
fmt::println("checking for sprite at {}", id);
|
||||
if($gui.has<guecs::Sprite>(id)) {
|
||||
fmt::println("REMOVING SPRITE {}", id);
|
||||
$gui.remove<guecs::Sprite>(id);
|
||||
} else {
|
||||
fmt::println("nothing at {}", id);
|
||||
}
|
||||
|
||||
if(i < contents.size()) {
|
||||
auto item = contents.at(i);
|
||||
auto& sprite = $level.world->get<components::Sprite>(item);
|
||||
fmt::println("NEW SPRITE SPRITE {}", sprite.name);
|
||||
$gui.set_init<guecs::Sprite>(id, {sprite.name});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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<DinkyECS::Entity> select_slot(int slot);
|
||||
void remove_slot(int slot_id);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include <guecs/ui.hpp>
|
||||
#include "rand.hpp"
|
||||
#include <fmt/xchar.h>
|
||||
#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<Textual>(button, {guecs::to_wstring(name)});
|
||||
$gui.set<Clickable>(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<components::Sprite>($selected_entity);
|
||||
auto gui_id = $gui.entity(name);
|
||||
$gui.set_init<guecs::Sprite>(gui_id, {sprite.name});
|
||||
|
||||
$slots.insert_or_assign(name, $selected_entity);
|
||||
|
||||
return $selected_slot;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,17 +10,20 @@ namespace gui {
|
|||
class StatusUI {
|
||||
public:
|
||||
guecs::UI $gui;
|
||||
std::map<std::string, size_t> $slots;
|
||||
std::unordered_map<std::string, DinkyECS::Entity> $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);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -256,6 +256,7 @@ TEST_CASE("prototype circle algorithm", "[matrix:circle]") {
|
|||
}
|
||||
|
||||
TEST_CASE("viewport iterator", "[matrix:viewport]") {
|
||||
components::init();
|
||||
size_t width = Random::uniform<size_t>(20, 22);
|
||||
size_t height = Random::uniform<size_t>(21, 25);
|
||||
shared_ptr<Map> 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> 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> map = make_map();
|
||||
auto wall_copy = map->walls();
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue