Compare commits

...

38 Commits

Author SHA1 Message Date
Zed A. Shaw 6cbfcf993e Meson build was using the wrong sfml_main on windows. 12 hours ago
Zed A. Shaw 0eb245d113 Working on a better character view. 1 day ago
Zed A. Shaw d6e64dd06b The log is now moved to the map, but changing StatusUI caused a weird compiler error so need to remove logs from that separate. 1 day ago
Zed A. Shaw a2246d2b71 Move the map_view and mini_map into gui as well. 1 day ago
Zed A. Shaw bed5ce22d2 Move gui_fsm to fsm but this causes some problems. 2 days ago
Zed A. Shaw cc44c9d37a Move the guecstra stuff into the gui. 2 days ago
Zed A. Shaw dfc6aa08e9 Loot UI is now mostly formed, just need to get loot into it and make it work. 3 days ago
Zed A. Shaw 8545b8cf1d Simple Loot UI started. 3 days ago
Zed A. Shaw 8a3046e141 The colors and other theme elements can be configured in assets/config.json 4 days ago
Zed A. Shaw 74a8599977 Fully converted to using the lel-guecs library externally now. 4 days ago
Zed A. Shaw 7c90eb6da1 GUECS now doesn't have the facts feature from DinkyECS and instead you refer to the whole area with gui.MAIN. This is an entity that's at 0 and represents the whole grid. Background is placed there. 2 weeks ago
Zed A. Shaw abea6da2e0 More GUECS cleanup before releasing. Still need to sort out events and reduce the amount of stuff that GUECS needs. 2 weeks ago
Zed A. Shaw 1780a758b3 Initial GUECS refactor is done, it's now on its own with no other dependencies, but now I need to trim it down to do only what it needs. 2 weeks ago
Zed A. Shaw 20176cf54a GUECS refactor part 1. 2 weeks ago
Zed A. Shaw 4e7f837240 Cleanup of GUECS and the textures manager. 2 weeks ago
Zed A. Shaw 438bd8ab8a Finally upgraded the strings to const& since I'm about to pull guecs out and distribute it. Oh well, the joke's finally over. 2 weeks ago
Zed A. Shaw 82ce3cb6be Autowalker now knows how to craft its first weapon and open the map. 2 weeks ago
Zed A. Shaw 70d27b9a95 GUECS now has a click_on function so you can programatically click on buttons for testing. 2 weeks ago
Zed A. Shaw 78ba83e916 Move the map opened detect out to the class. 2 weeks ago
Zed A. Shaw c4ed26184b Autowalker now opens the map at first to test that it works. 2 weeks ago
Zed A. Shaw edf10c976a Cleaned up the ritual UI some more and solved a few more bugs, then brought in a quick 'dubious combination' image. 2 weeks ago
Zed A. Shaw bac552c3d7 Ritual ui now does the combination correctly. 2 weeks ago
Zed A. Shaw dab0e092e6 RitualUI is mostly working, but need to make the consumption of items work in the UI. 2 weeks ago
Zed A. Shaw 6269d10807 The ritual UI is now a lot better using a FSM to control everything. Probably one more session to work out the remaining functionality. 2 weeks ago
Zed A. Shaw 8a1f42c0f1 RitualUI is now ritual::UI and uses a FSM to coordinate its activities. 2 weeks ago
Zed A. Shaw d1bd6b7c45 Mostly does everything I need, minus the ritual description. That'll have to probably be a separate generator. Next, rewrite this crap. 2 weeks ago
Zed A. Shaw 2ceab51c40 A really shitty ritual crafting UI is working but needs a big reshape. 3 weeks ago
Zed A. Shaw 14619558fa Better UI for the ritual crafting that almost works, but need to get the selected items to move down. Might need some state machine love soon. 3 weeks ago
Zed A. Shaw 9d55b2954a The rituals can now craft from items taken from dead enemies and they go into the blanket right away. 3 weeks ago
Zed A. Shaw 1a9e068d02 Junk items are now transfered to your blanket so you can use them in crafting. No UI for that though. 3 weeks ago
Zed A. Shaw bc557652ba The player now has some starting items to craft a first weapon, and it is craftable in the UI. 3 weeks ago
Zed A. Shaw c8a8d2b1af You can now craft a single ritual from the blanket. 3 weeks ago
Zed A. Shaw 292711f91f Prep for the actually making ritual crafting work. 3 weeks ago
Zed A. Shaw ad1d08ca96 There's now an hp status indicator 'doll' when you click on it your host (character) will tell you how they're doing for HP. 3 weeks ago
Zed A. Shaw dac9b1b3de rcrnstn found a way to make the shaders work under MESA by forcing the version number to 120 and no default params in functions. 3 weeks ago
Zed A. Shaw 1ab5fa4291 Fix from ORBLISH suggestion to stop any component that's not hover/clicked on in the mouse handler. 3 weeks ago
Zed A. Shaw 4c03fe1ed3 Make the raycaster 'highlight' things you point at. 3 weeks ago
Zed A. Shaw bc31750d9c Fix from ORBLISH suggestion to stop any component that's not hover/clicked on in the mouse handler. 3 weeks ago
  1. 1
      .gitignore
  2. 2
      Makefile
  3. 47
      assets/config.json
  4. 15
      assets/enemies.json
  5. BIN
      assets/gold_savior_oil-256.png
  6. 2
      assets/items.json
  7. BIN
      assets/peasant_girl_2-256.png
  8. 29
      assets/rituals.json
  9. BIN
      assets/rituals/dubious_combination-128.png
  10. BIN
      assets/rituals/dubious_combination-64.png
  11. 6
      assets/shaders/flame_trash.frag
  12. 4
      assets/shaders/lightning_attack.frag
  13. BIN
      assets/sounds/fireball_01.ogg
  14. BIN
      assets/sounds/hp_status_00.ogg
  15. BIN
      assets/sounds/hp_status_10.ogg
  16. BIN
      assets/sounds/hp_status_30.ogg
  17. BIN
      assets/sounds/hp_status_60.ogg
  18. BIN
      assets/sounds/hp_status_80.ogg
  19. 49
      autowalker.cpp
  20. 7
      autowalker.hpp
  21. 86
      backend.cpp
  22. 19
      backend.hpp
  23. 15
      color.hpp
  24. 1
      components.cpp
  25. 7
      components.hpp
  26. 12
      config.cpp
  27. 5
      config.hpp
  28. 9
      constants.hpp
  29. 2
      dbc.hpp
  30. 11
      dinkyecs.hpp
  31. 6
      events.hpp
  32. 317
      guecs.cpp
  33. 237
      guecs.hpp
  34. 4
      gui/boss_fight_ui.cpp
  35. 2
      gui/boss_fight_ui.hpp
  36. 47
      gui/combat_ui.cpp
  37. 6
      gui/combat_ui.hpp
  38. 4
      gui/debug_ui.cpp
  39. 2
      gui/debug_ui.hpp
  40. 65
      gui/fsm.cpp
  41. 24
      gui/fsm.hpp
  42. 18
      gui/guecstra.cpp
  43. 8
      gui/guecstra.hpp
  44. 65
      gui/loot_ui.cpp
  45. 22
      gui/loot_ui.hpp
  46. 23
      gui/main_ui.cpp
  47. 8
      gui/main_ui.hpp
  48. 42
      gui/map_view.cpp
  49. 7
      gui/map_view.hpp
  50. 0
      gui/mini_map.cpp
  51. 2
      gui/mini_map.hpp
  52. 3
      gui/overlay_ui.cpp
  53. 4
      gui/overlay_ui.hpp
  54. 252
      gui/ritual_ui.cpp
  55. 69
      gui/ritual_ui.hpp
  56. 56
      gui/status_ui.cpp
  57. 9
      gui/status_ui.hpp
  58. 117
      lel.cpp
  59. 55
      lel.hpp
  60. 261
      lel_parser.cpp
  61. 66
      lel_parser.rl
  62. 26
      levelmanager.cpp
  63. 4
      levelmanager.hpp
  64. 9
      main.cpp
  65. 36
      meson.build
  66. 2
      raycaster.cpp
  67. 156
      ritual_ui.cpp
  68. 38
      ritual_ui.hpp
  69. 119
      rituals.cpp
  70. 80
      rituals.hpp
  71. 0
      simplefsm.hpp
  72. 64
      systems.cpp
  73. 2
      systems.hpp
  74. 2
      tests/battle.cpp
  75. 2
      tests/fsm.cpp
  76. 33
      tests/guecs.cpp
  77. 52
      tests/lel.cpp
  78. 165
      tests/rituals.cpp
  79. 6
      textures.cpp
  80. 5
      textures.hpp
  81. 1
      tilemap.cpp
  82. 14
      worldbuilder.cpp
  83. 3
      worldbuilder.hpp
  84. 9
      wraps/lel-guecs.wrap

1
.gitignore vendored

@ -27,4 +27,5 @@ backup
*.dll *.dll
*.world *.world
coverage coverage
coverage/*
.venv .venv

@ -10,7 +10,7 @@ endif
%.cpp : %.rl %.cpp : %.rl
ragel -o $@ $< ragel -o $@ $<
build: lel_parser.cpp build:
meson compile -j 10 -C builddir meson compile -j 10 -C builddir
release_build: release_build:

@ -22,11 +22,16 @@
"ui_hover": "assets/sounds/ui_hover.ogg", "ui_hover": "assets/sounds/ui_hover.ogg",
"punch_cartoony": "assets/sounds/punch_cartoony.ogg", "punch_cartoony": "assets/sounds/punch_cartoony.ogg",
"electric_shock_01": "assets/sounds/electric_shock_01.ogg", "electric_shock_01": "assets/sounds/electric_shock_01.ogg",
"fireball_01": "assets/sounds/fireball_01.ogg" "fireball_01": "assets/sounds/fireball_01.ogg",
"hp_status_80": "assets/sounds/hp_status_80.ogg",
"hp_status_60": "assets/sounds/hp_status_60.ogg",
"hp_status_30": "assets/sounds/hp_status_30.ogg",
"hp_status_10": "assets/sounds/hp_status_10.ogg",
"hp_status_00": "assets/sounds/hp_status_00.ogg"
}, },
"sprites": { "sprites": {
"gold_savior": "gold_savior":
{"path": "assets/gold_savior-256.png", {"path": "assets/gold_savior_oil-256.png",
"frame_width": 256, "frame_width": 256,
"frame_height": 256 "frame_height": 256
}, },
@ -81,7 +86,7 @@
"frame_height": 256 "frame_height": 256
}, },
"peasant_girl": "peasant_girl":
{"path": "assets/undead_peasant-256.png", {"path": "assets/peasant_girl_2-256.png",
"frame_width": 256, "frame_width": 256,
"frame_height": 256 "frame_height": 256
}, },
@ -304,6 +309,16 @@
{"path": "assets/rituals/stone_doll_cursed-128.png", {"path": "assets/rituals/stone_doll_cursed-128.png",
"frame_width": 128, "frame_width": 128,
"frame_height": 128 "frame_height": 128
},
"dubious_combination-64":
{"path": "assets/rituals/dubious_combination-64.png",
"frame_width": 64,
"frame_height": 64
},
"dubious_combination-128":
{"path": "assets/rituals/dubious_combination-128.png",
"frame_width": 128,
"frame_height": 128
} }
}, },
"worldgen": { "worldgen": {
@ -324,9 +339,25 @@
"W": 8592, "W": 8592,
"NW": 8598 "NW": 8598
}, },
"test_rituals": [ "theme": {
{ "has_spikes": true, "active": true }, "black": [0, 0, 0, 255],
{ "has_magick": true, "active": true }, "dark_dark": [10, 10, 10, 255],
{ "has_magick": true, "shiny_bauble": true, "active": true } "dark_mid": [30, 30, 30, 255],
] "dark_light": [60, 60, 60, 255],
"mid": [100, 100, 100, 255],
"light_dark": [150, 150, 150, 255],
"light_mid": [200, 200, 200, 255],
"light_light": [230, 230, 230, 255],
"white": [255, 255, 255, 255],
"padding": 3,
"border_px": 1,
"text_size": 20,
"label_size": 20,
"fill_color": "dark_mid",
"text_color": "light_light",
"bg_color": "mid",
"border_color": "dark_dark",
"bg_color_dark": "black",
"font_file_name": "assets/text.otf"
}
} }

@ -11,6 +11,21 @@
{"_type": "LightSource", "strength": 45, "radius": 2.0} {"_type": "LightSource", "strength": 45, "radius": 2.0}
] ]
}, },
"GOLD_SAVIOR": {
"components": [
{"_type": "Tile", "display": 2189,
"foreground": [131, 213, 238],
"background": [30, 20, 75]
},
{"_type": "Combat", "hp": 20, "max_hp": 20, "damage": 1, "dead": false},
{"_type": "Motion", "dx": 0, "dy": 0, "random": false},
{"_type": "EnemyConfig", "ai_script": "Enemy::actions", "ai_start_name": "Enemy::initial_state", "ai_goal_name": "Enemy::final_state"},
{"_type": "Personality", "hearing_distance": 5, "tough": true},
{"_type": "Animation", "easing": 1, "ease_rate": 0.2, "scale": 0.1, "simple": true, "frames": 10, "speed": 0.3, "stationary": false},
{"_type": "Sprite", "name": "gold_savior", "width": 256, "height": 256, "width": 256, "height": 256, "scale": 1.0},
{"_type": "Sound", "attack": "Sword_Hit_2", "death": "Humanoid_Death_1"}
]
},
"KNIGHT": { "KNIGHT": {
"components": [ "components": [
{"_type": "Tile", "display": 2189, {"_type": "Tile", "display": 2189,

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

@ -38,7 +38,6 @@
"foreground": [150, 100, 189], "foreground": [150, 100, 189],
"background": [150, 100, 189] "background": [150, 100, 189]
}, },
{"_type": "Loot", "amount": 10},
{"_type": "Sprite", "name": "barrel_small", "width": 256, "height": 256, "scale": 1.0}, {"_type": "Sprite", "name": "barrel_small", "width": 256, "height": 256, "scale": 1.0},
{"_type": "Sound", "attack": "pickup", "death": "blank"} {"_type": "Sound", "attack": "pickup", "death": "blank"}
], ],
@ -84,7 +83,6 @@
"foreground": [32, 123, 164], "foreground": [32, 123, 164],
"background": [24, 205, 189] "background": [24, 205, 189]
}, },
{"_type": "Loot", "amount": 10},
{"_type": "Sprite", "name": "grave_stone", "width": 256, "height": 256, "scale": 1.0}, {"_type": "Sprite", "name": "grave_stone", "width": 256, "height": 256, "scale": 1.0},
{"_type": "Sound", "attack": "pickup", "death": "blank"} {"_type": "Sound", "attack": "pickup", "death": "blank"}
] ]

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

@ -169,5 +169,34 @@
"damage": 16, "damage": 16,
"probability": 1.0 "probability": 1.0
} }
},
"junk": {
"chess_pawn": {
"name": "chess_pawn",
"provides": ["cursed_item"]
},
"dirty_kerchief": {
"name": "dirty_kerchief",
"provides": ["has_magick"]
},
"mushroom": {
"name": "mushroom",
"provides": ["has_magick"]
},
"pocket_watch": {
"name": "pocket_watch",
"provides": ["shiny_bauble"]
},
"rusty_nails": {
"name": "rusty_nails",
"provides": ["has_spikes"]
},
"severed_finger": {
"name": "severed_finger",
"provides": ["cursed_item"]
} }
},
"starting_junk": [
"pocket_watch", "mushroom", "rusty_nails"
]
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

@ -1,8 +1,10 @@
#version 120
uniform vec2 u_resolution; uniform vec2 u_resolution;
uniform float u_time; uniform float u_time;
uniform sampler2D source; uniform sampler2D source;
uniform float u_mouse; uniform float u_mouse;
uniform float value = 0.2; uniform float value = 0.2;
uniform int octaves=8;
float random (in vec2 st) { float random (in vec2 st) {
return fract(sin(dot(st.xy, return fract(sin(dot(st.xy,
@ -26,7 +28,7 @@ float noise(in vec2 st) {
(d - b) * u.x * u.y; (d - b) * u.x * u.y;
} }
float fbm(in vec2 st, int octaves=8) { float fbm(in vec2 st) {
float v = 0.0; float v = 0.0;
float a = 0.5; float a = 0.5;
vec2 shift = vec2(100.0); vec2 shift = vec2(100.0);
@ -47,7 +49,7 @@ void main() {
vec3 color = vec3(0.0); vec3 color = vec3(0.0);
float speed = u_time * 10.0; float speed = u_time * 10.0;
float value = cos(u_time) * cos(u_time); float value = 0.8; // cos(u_time) * cos(u_time);
vec2 q = vec2(0.0); vec2 q = vec2(0.0);
q.x = fbm(st + 0.00 * speed); q.x = fbm(st + 0.00 * speed);

@ -1,8 +1,10 @@
#version 120
uniform vec2 u_resolution; uniform vec2 u_resolution;
uniform float u_time; uniform float u_time;
uniform sampler2D source; uniform sampler2D source;
uniform float u_mouse; uniform float u_mouse;
uniform float value = 0.2; uniform float value = 0.2;
uniform int octaves=8;
float random (in vec2 st) { float random (in vec2 st) {
return fract(sin(dot(st.xy, return fract(sin(dot(st.xy,
@ -26,7 +28,7 @@ float noise(in vec2 st) {
(d - b) * u.x * u.y; (d - b) * u.x * u.y;
} }
float fbm(in vec2 st, int octaves=8) { float fbm(in vec2 st) {
float v = 0.0; float v = 0.0;
float a = 0.5; float a = 0.5;
vec2 shift = vec2(100.0); vec2 shift = vec2(100.0);

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

@ -1,6 +1,7 @@
#include "autowalker.hpp" #include "autowalker.hpp"
#include "inventory.hpp" #include "inventory.hpp"
#include "ai_debug.hpp" #include "ai_debug.hpp"
#include "gui/ritual_ui.hpp"
template<typename Comp> template<typename Comp>
int number_left(gui::FSM& fsm) { int number_left(gui::FSM& fsm) {
@ -41,7 +42,7 @@ Pathing compute_paths(gui::FSM& fsm) {
} }
void Autowalker::log(std::wstring msg) { void Autowalker::log(std::wstring msg) {
fsm.$status_ui.log(msg); fsm.$map_ui.log(msg);
} }
void Autowalker::status(std::wstring msg) { void Autowalker::status(std::wstring msg) {
@ -60,11 +61,6 @@ Pathing Autowalker::path_to_items() {
return compute_paths<components::InventoryItem>(fsm); return compute_paths<components::InventoryItem>(fsm);
} }
Pathing Autowalker::path_to_devices() {
return compute_paths<components::Device>(fsm);
}
void Autowalker::handle_window_events() { void Autowalker::handle_window_events() {
fsm.$window.handleEvents( fsm.$window.handleEvents(
[&](const sf::Event::KeyPressed &) { [&](const sf::Event::KeyPressed &) {
@ -223,7 +219,6 @@ void Autowalker::handle_player_walk(ai::State& start, ai::State& goal) {
auto action = a_plan.script.front(); auto action = a_plan.script.front();
if(action.name == "find_enemy") { if(action.name == "find_enemy") {
// this is where to test if enemy found and update state
status(L"FINDING ENEMY"); status(L"FINDING ENEMY");
auto paths = path_to_enemies(); auto paths = path_to_enemies();
process_move(paths); process_move(paths);
@ -258,6 +253,41 @@ void Autowalker::handle_player_walk(ai::State& start, ai::State& goal) {
} }
} }
void Autowalker::craft_weapon() {
if(!weapon_crafted) {
auto& ritual_ui = fsm.$status_ui.$ritual_ui;
fsm.$status_ui.$gui.click_on("ritual_ui");
while(!ritual_ui.in_state(gui::ritual::State::OPENED)) {
send_event(gui::Event::TICK);
}
ritual_ui.$gui.click_on("inv_slot0");
send_event(gui::Event::TICK);
ritual_ui.$gui.click_on("inv_slot1");
send_event(gui::Event::TICK);
while(!ritual_ui.in_state(gui::ritual::State::CRAFTING)) {
send_event(gui::Event::TICK);
}
ritual_ui.$gui.click_on("result_image", true);
send_event(gui::Event::TICK);
ritual_ui.$gui.click_on("ritual_ui");
send_event(gui::Event::TICK);
weapon_crafted = true;
}
}
void Autowalker::open_map() {
if(map_opened_once) return;
if(!fsm.$map_open) {
send_event(gui::Event::MAP_OPEN);
map_opened_once = true;
}
}
void Autowalker::autowalk() { void Autowalker::autowalk() {
@ -267,6 +297,9 @@ void Autowalker::autowalk() {
return; return;
} }
craft_weapon();
open_map();
int move_attempts = 0; int move_attempts = 0;
auto start = ai::load_state("Host::initial_state"); auto start = ai::load_state("Host::initial_state");
@ -277,6 +310,8 @@ void Autowalker::autowalk() {
handle_boss_fight(); handle_boss_fight();
handle_player_walk(start, goal); handle_player_walk(start, goal);
if(map_opened_once && move_attempts > 20) send_event(gui::Event::MAP_OPEN);
move_attempts++; move_attempts++;
} while(move_attempts < 100 && fsm.autowalking); } while(move_attempts < 100 && fsm.autowalking);
} }

@ -1,7 +1,7 @@
#pragma once #pragma once
#include "ai.hpp" #include "ai.hpp"
#include "gui_fsm.hpp" #include "gui/fsm.hpp"
struct InventoryStats; struct InventoryStats;
@ -9,6 +9,8 @@ struct Autowalker {
int enemy_count = 0; int enemy_count = 0;
int item_count = 0; int item_count = 0;
int device_count = 0; int device_count = 0;
bool map_opened_once = false;
bool weapon_crafted = false;
gui::FSM& fsm; gui::FSM& fsm;
Autowalker(gui::FSM& fsm) Autowalker(gui::FSM& fsm)
@ -16,6 +18,8 @@ struct Autowalker {
void autowalk(); void autowalk();
void start_autowalk(); void start_autowalk();
void craft_weapon();
void open_map();
void handle_window_events(); void handle_window_events();
void handle_boss_fight(); void handle_boss_fight();
@ -38,5 +42,4 @@ struct Autowalker {
Pathing path_to_enemies(); Pathing path_to_enemies();
Pathing path_to_items(); Pathing path_to_items();
Pathing path_to_devices();
}; };

@ -0,0 +1,86 @@
#include "backend.hpp"
#include "shaders.hpp"
#include "sound.hpp"
#include "textures.hpp"
#include "config.hpp"
namespace sfml {
using namespace nlohmann;
guecs::SpriteTexture Backend::texture_get(const string& name) {
auto sp = textures::get(name);
return {sp.sprite, sp.texture};
}
Backend::Backend() {
sound::init();
shaders::init();
textures::init();
}
void Backend::sound_play(const string& name) {
sound::play(name);
}
void Backend::sound_stop(const string& name) {
sound::stop(name);
}
std::shared_ptr<sf::Shader> Backend::shader_get(const std::string& name) {
return shaders::get(name);
}
bool Backend::shader_updated() {
if(shaders::updated($shaders_version)) {
$shaders_version = shaders::version();
return true;
} else {
return false;
}
}
inline sf::Color to_color(json& config, const std::string& name) {
json& val = config[name];
if(val.type() == json::value_t::array) {
return sf::Color{val[0], val[1], val[2], val[3]};
} else if(val.type() == json::value_t::string) {
json& array = config[val];
return sf::Color{array[0], array[1], array[2], array[3]};
} else {
dbc::sentinel(fmt::format(
"theme config {} has invalid color setting,"
"either use an array of 4 ints or a string"
"referencing another config with 4 ints.", name));
}
}
guecs::Theme Backend::theme() {
auto config = Config("assets/config.json")["theme"];
guecs::Theme theme {
.BLACK=to_color(config, "black"),
.DARK_DARK=to_color(config, "dark_dark"),
.DARK_MID=to_color(config, "dark_mid"),
.DARK_LIGHT=to_color(config, "dark_light"),
.MID=to_color(config, "mid"),
.LIGHT_DARK=to_color(config, "light_dark"),
.LIGHT_MID=to_color(config, "light_mid"),
.LIGHT_LIGHT=to_color(config, "light_light"),
.WHITE=to_color(config, "white"),
.TRANSPARENT = sf::Color::Transparent
};
theme.PADDING = config["padding"];
theme.BORDER_PX = config["border_px"];
theme.TEXT_SIZE = config["text_size"];
theme.LABEL_SIZE = config["label_size"];
theme.FILL_COLOR = to_color(config, "fill_color");
theme.TEXT_COLOR = to_color(config, "text_color");
theme.BG_COLOR = to_color(config, "bg_color");
theme.BORDER_COLOR = to_color(config, "border_color");
theme.BG_COLOR_DARK = to_color(config, "bg_color_dark");
theme.FONT_FILE_NAME = Config::path_to(config["font_file_name"]).string();
return theme;
}
}

@ -0,0 +1,19 @@
#include "guecs/ui.hpp"
namespace sfml {
using std::string;
class Backend : public guecs::Backend {
int $shaders_version = 0;
public:
Backend();
guecs::SpriteTexture texture_get(const string& name);
void sound_play(const string& name);
void sound_stop(const string& name);
std::shared_ptr<sf::Shader> shader_get(const std::string& name);
bool shader_updated();
guecs::Theme theme();
};
}

@ -1,15 +0,0 @@
#pragma once
#include <SFML/Graphics/Color.hpp>
namespace ColorValue {
const sf::Color BLACK{0, 0, 0};
const sf::Color DARK_DARK{10, 10, 10};
const sf::Color DARK_MID{30, 30, 30};
const sf::Color DARK_LIGHT{60, 60, 60};
const sf::Color MID{100, 100, 100};
const sf::Color LIGHT_DARK{150, 150, 150};
const sf::Color LIGHT_MID{200, 200, 200};
const sf::Color LIGHT_LIGHT{230, 230, 230};
const sf::Color WHITE{255, 255, 255};
const sf::Color TRANSPARENT = sf::Color::Transparent;
}

@ -14,7 +14,6 @@ namespace components {
void configure(ComponentMap& component_map) { void configure(ComponentMap& component_map) {
components::enroll<BossFight>(component_map); components::enroll<BossFight>(component_map);
components::enroll<Combat>(component_map); components::enroll<Combat>(component_map);
components::enroll<Loot>(component_map);
components::enroll<Position>(component_map); components::enroll<Position>(component_map);
components::enroll<Weapon>(component_map); components::enroll<Weapon>(component_map);
components::enroll<Curative>(component_map); components::enroll<Curative>(component_map);

@ -12,8 +12,8 @@
#include "json_mods.hpp" #include "json_mods.hpp"
#include "goap.hpp" #include "goap.hpp"
namespace components { namespace components {
using std::string;
using namespace nlohmann; using namespace nlohmann;
struct SpriteEffect { struct SpriteEffect {
@ -31,10 +31,6 @@ namespace components {
bool random=false; bool random=false;
}; };
struct Loot {
int amount;
};
struct Tile { struct Tile {
wchar_t display; wchar_t display;
std::array<uint8_t, 3> foreground; std::array<uint8_t, 3> foreground;
@ -152,7 +148,6 @@ namespace components {
ENROLL_COMPONENT(Curative, hp); ENROLL_COMPONENT(Curative, hp);
ENROLL_COMPONENT(LightSource, strength, radius); ENROLL_COMPONENT(LightSource, strength, radius);
ENROLL_COMPONENT(Weapon, damage); ENROLL_COMPONENT(Weapon, damage);
ENROLL_COMPONENT(Loot, amount);
ENROLL_COMPONENT(Position, location.x, location.y); ENROLL_COMPONENT(Position, location.x, location.y);
ENROLL_COMPONENT(EnemyConfig, ai_script, ai_start_name, ai_goal_name); ENROLL_COMPONENT(EnemyConfig, ai_script, ai_start_name, ai_goal_name);
ENROLL_COMPONENT(Personality, hearing_distance, tough); ENROLL_COMPONENT(Personality, hearing_distance, tough);

@ -5,8 +5,10 @@
using nlohmann::json; using nlohmann::json;
using fmt::format; using fmt::format;
std::filesystem::path Config::BASE_DIR{"."};
Config::Config(const std::string src_path) : $src_path(src_path) { Config::Config(const std::string src_path) : $src_path(src_path) {
std::ifstream infile($src_path); std::ifstream infile(Config::path_to($src_path));
$config = json::parse(infile); $config = json::parse(infile);
} }
@ -33,3 +35,11 @@ std::vector<std::string> Config::keys() {
return the_fucking_keys; return the_fucking_keys;
} }
void Config::set_base_dir(const char *optarg) {
Config::BASE_DIR.assign(optarg);
}
std::filesystem::path Config::path_to(const std::string& path) {
return Config::BASE_DIR / path;
}

@ -2,8 +2,10 @@
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
#include <fstream> #include <fstream>
#include <codecvt> #include <codecvt>
#include <filesystem>
struct Config { struct Config {
static std::filesystem::path BASE_DIR;
nlohmann::json $config; nlohmann::json $config;
std::string $src_path; std::string $src_path;
@ -16,4 +18,7 @@ struct Config {
nlohmann::json &json() { return $config; }; nlohmann::json &json() { return $config; };
std::wstring wstring(const std::string main_key, const std::string sub_key); std::wstring wstring(const std::string main_key, const std::string sub_key);
std::vector<std::string> keys(); std::vector<std::string> keys();
static void set_base_dir(const char *optarg);
static std::filesystem::path path_to(const std::string& path);
}; };

@ -1,9 +1,9 @@
#pragma once #pragma once
#include <string> #include <string>
#include "color.hpp"
#include <array> #include <array>
constexpr const int INV_SLOTS=20;
constexpr const int TEXTURE_WIDTH=256; constexpr const int TEXTURE_WIDTH=256;
constexpr const int TEXTURE_HEIGHT=256; constexpr const int TEXTURE_HEIGHT=256;
constexpr const int RAY_VIEW_WIDTH=900; constexpr const int RAY_VIEW_WIDTH=900;
@ -23,13 +23,6 @@ constexpr const int FRAME_LIMIT=60;
constexpr const int NUM_SPRITES=1; constexpr const int NUM_SPRITES=1;
constexpr const int MAX_LOG_MESSAGES=17; constexpr const int MAX_LOG_MESSAGES=17;
constexpr const int GUECS_PADDING = 3;
constexpr const int GUECS_BORDER_PX = 1;
constexpr const int GUECS_FONT_SIZE = 30;
const sf::Color GUECS_FILL_COLOR = ColorValue::DARK_MID;
const sf::Color GUECS_TEXT_COLOR = ColorValue::LIGHT_LIGHT;
const sf::Color GUECS_BG_COLOR = ColorValue::MID;
const sf::Color GUECS_BORDER_COLOR = ColorValue::MID;
#ifdef NDEBUG #ifdef NDEBUG
constexpr const bool DEBUG_BUILD=false; constexpr const bool DEBUG_BUILD=false;

@ -5,9 +5,9 @@
#include <functional> #include <functional>
#include <source_location> #include <source_location>
namespace dbc {
using std::string; using std::string;
namespace dbc {
class Error { class Error {
public: public:
const string message; const string message;

@ -203,11 +203,14 @@ namespace DinkyECS
* return pointers (assuming optional can handle pointers) * return pointers (assuming optional can handle pointers)
*/ */
template <typename Comp> template <typename Comp>
std::optional<Comp> get_if(DinkyECS::Entity entity) { Comp* get_if(DinkyECS::Entity entity) {
if(has<Comp>(entity)) { EntityMap &map = entity_map_for<Comp>();
return std::make_optional<Comp>(get<Comp>(entity)); auto &storage = component_storage_for<Comp>();
if(map.contains(entity)) {
auto index = map.at(entity);
return &storage.data[index];
} else { } else {
return std::nullopt; return nullptr;
} }
} }
}; };

@ -3,9 +3,9 @@
namespace Events { namespace Events {
enum GUI { enum GUI {
START, COMBAT, LOOT, DEATH, STAIRS_UP, STAIRS_DOWN, TRAP, START, COMBAT, LOOT, DEATH, STAIRS_UP, STAIRS_DOWN, TRAP,
COMBAT_START, NO_NEIGHBORS, COMBAT_START, NO_NEIGHBORS, HP_STATUS,
ATTACK, BLOCK, EVADE, HEAL, ATTACK, BLOCK, EVADE, NEW_RITUAL,
UPDATE_SPRITE, ENEMY_SPAWN, NOOP UPDATE_SPRITE, ENEMY_SPAWN, NOOP, LOOT_CLOSE
}; };
struct Combat { struct Combat {

@ -1,317 +0,0 @@
#include "guecs.hpp"
#include "shaders.hpp"
#include "sound.hpp"
namespace guecs {
void Textual::init(lel::Cell &cell, shared_ptr<sf::Font> font_ptr) {
dbc::check(font_ptr != nullptr, "you failed to initialize this WideText");
if(font == nullptr) font = font_ptr;
if(text == nullptr) text = make_shared<sf::Text>(*font, content, size);
text->setFillColor(color);
if(centered) {
auto bounds = text->getLocalBounds();
auto text_cell = lel::center(bounds.size.x, bounds.size.y, cell);
// this stupid / 2 is because SFML renders from baseline rather than from the claimed bounding box
text->setPosition({float(text_cell.x), float(text_cell.y) - text_cell.h / 2});
} else {
text->setPosition({float(cell.x + padding * 2), float(cell.y + padding * 2)});
}
text->setCharacterSize(size);
}
void Textual::update(std::wstring& new_content) {
content = new_content;
text->setString(content);
}
void Sprite::init(lel::Cell &cell) {
auto sprite_texture = textures::get(name);
sprite = make_shared<sf::Sprite>(
*sprite_texture.texture,
sprite_texture.sprite->getTextureRect());
sprite->setPosition({
float(cell.x + padding),
float(cell.y + padding)});
auto bounds = sprite->getLocalBounds();
sprite->setScale({
float(cell.w - padding * 2) / bounds.size.x,
float(cell.h - padding * 2) / bounds.size.y});
}
void Rectangle::init(lel::Cell& cell) {
sf::Vector2f size{float(cell.w) - padding * 2, float(cell.h) - padding * 2};
if(shape == nullptr) shape = make_shared<sf::RectangleShape>(size);
shape->setPosition({float(cell.x + padding), float(cell.y + padding)});
shape->setFillColor(color);
shape->setOutlineColor(border_color);
shape->setOutlineThickness(border_px);
}
void Meter::init(lel::Cell& cell) {
bar.init(cell);
}
void Sound::play(bool hover) {
if(!hover) {
sound::play(on_click);
}
}
void Background::init() {
sf::Vector2f size{float(w), float(h)};
if(shape == nullptr) shape = make_shared<sf::RectangleShape>(size);
shape->setPosition({float(x), float(y)});
shape->setFillColor(color);
}
void Effect::init(lel::Cell &cell) {
$shader_version = shaders::version();
$shader = shaders::get(name);
$shader->setUniform("u_resolution", sf::Vector2f({float(cell.w), float(cell.h)}));
$clock = std::make_shared<sf::Clock>();
}
void Effect::step() {
sf::Time cur_time = $clock->getElapsedTime();
float u_time = cur_time.asSeconds();
if(u_time < $u_time_end) {
$shader->setUniform("u_duration", duration);
$shader->setUniform("u_time_end", $u_time_end);
$shader->setUniform("u_time", u_time);
} else {
$active = false;
}
}
void Effect::run() {
$active = true;
sf::Time u_time = $clock->getElapsedTime();
$u_time_end = u_time.asSeconds() + duration;
}
shared_ptr<sf::Shader> Effect::checkout_ptr() {
if(shaders::updated($shader_version)) {
$shader = shaders::get(name);
$shader_version = shaders::version();
}
return $shader;
}
UI::UI() {
$font = make_shared<sf::Font>(FONT_FILE_NAME);
}
void UI::position(int x, int y, int width, int height) {
$parser.position(x, y, width, height);
}
void UI::layout(std::string grid) {
$grid = grid;
bool good = $parser.parse($grid);
dbc::check(good, "LEL parsing failed.");
for(auto& [name, cell] : $parser.cells) {
auto ent = init_entity(name);
$world.set<lel::Cell>(ent, cell);
}
}
DinkyECS::Entity UI::init_entity(std::string name) {
auto entity = $world.entity();
// this lets you look up an entity by name
$name_ents.insert_or_assign(name, entity);
// this makes it easier to get the name during querying
$world.set<CellName>(entity, {name});
return entity;
}
DinkyECS::Entity UI::entity(std::string name) {
dbc::check($name_ents.contains(name),
fmt::format("GUECS entity {} does not exist. Forgot to init_entity?", name));
return $name_ents.at(name);
}
void UI::init() {
if($world.has_the<Background>()) {
auto& bg = $world.get_the<Background>();
bg.init();
}
$world.query<Background>([](auto, auto& bg) {
bg.init();
});
$world.query<lel::Cell, Rectangle>([](auto, auto& cell, auto& rect) {
rect.init(cell);
});
$world.query<lel::Cell, Effect>([](auto, auto& cell, auto& shader) {
shader.init(cell);
});
$world.query<Rectangle, Meter>([](auto, auto& bg, auto &) {
bg.shape->setFillColor(ColorValue::BLACK);
});
$world.query<lel::Cell, Meter>([](auto, auto &cell, auto& meter) {
meter.init(cell);
});
$world.query<lel::Cell, Textual>([this](auto, auto& cell, auto& text) {
text.init(cell, $font);
});
$world.query<lel::Cell, Label>([this](auto, auto& cell, auto& text) {
text.init(cell, $font);
});
$world.query<lel::Cell, Sprite>([&](auto, auto &cell, auto &sprite) {
sprite.init(cell);
});
}
void UI::debug_layout(sf::RenderWindow& window) {
$world.query<lel::Cell>([&](const auto, auto &cell) {
sf::RectangleShape rect{{float(cell.w), float(cell.h)}};
rect.setPosition({float(cell.x), float(cell.y)});
rect.setFillColor(sf::Color::Transparent);
rect.setOutlineColor(sf::Color::Red);
rect.setOutlineThickness(2.0f);
window.draw(rect);
});
}
void UI::render(sf::RenderWindow& window) {
if($world.has_the<Background>()) {
auto& bg = $world.get_the<Background>();
window.draw(*bg.shape);
}
$world.query<Effect>([&](auto, auto& shader) {
if(shader.$active) shader.step();
});
$world.query<Rectangle>([&](auto ent, auto& rect) {
render_helper(window, ent, true, rect.shape);
});
$world.query<lel::Cell, Meter>([&](auto ent, auto& cell, const auto &meter) {
float level = std::clamp(meter.percent, 0.0f, 1.0f) * float(cell.w);
// ZED: this 6 is a border width, make it a thing
meter.bar.shape->setSize({std::max(level, 0.0f), float(cell.h - 6)});
render_helper(window, ent, true, meter.bar.shape);
});
$world.query<Sprite>([&](auto ent, auto& sprite) {
render_helper(window, ent, false, sprite.sprite);
});
$world.query<Label>([&](auto ent, auto& text) {
render_helper(window, ent, false, text.text);
});
$world.query<Textual>([&](auto ent, auto& text) {
render_helper(window, ent, true, text.text);
});
}
bool UI::mouse(float x, float y, bool hover) {
int action_count = 0;
$world.query<lel::Cell, Clickable>([&](auto ent, auto& cell, auto &clicked) {
if((x >= cell.x && x <= cell.x + cell.w) &&
(y >= cell.y && y <= cell.y + cell.h))
{
do_if<Effect>(ent, [hover](auto& effect) {
effect.$shader->setUniform("hover", hover);
effect.run();
});
do_if<Sound>(ent, [hover](auto& sound) {
// here set that it played then only play once
sound.play(hover);
});
if(hover) return; // kinda gross
if(auto action_data = get_if<ActionData>(ent)) {
clicked.action(ent, action_data->data);
} else {
clicked.action(ent, {});
}
action_count++;
} else {
// via ORBLISHJ
// just reset the hover trigger for all that aren't hit
// then in the ^^ positive branch play it and set it played
}
});
return action_count > 0;
}
void UI::show_sprite(string region, string sprite_name) {
auto ent = entity(region);
if(!has<Sprite>(ent)) {
Sprite to_show{sprite_name};
auto& cell = cell_for(ent);
to_show.init(cell);
set<guecs::Sprite>(ent, to_show);
}
}
void UI::show_text(string region, wstring content) {
auto ent = entity(region);
if(auto text = get_if<Textual>(ent)) {
text->text->setString(content);
} else {
auto &cell = cell_for(ent);
Textual to_set{content, 20};
to_set.init(cell, $font);
to_set.text->setFillColor(ColorValue::LIGHT_MID);
set<Textual>(ent, to_set);
}
}
void UI::show_label(string region, wstring content) {
auto ent = entity(region);
if(auto text = get_if<Label>(ent)) {
text->text->setString(content);
} else {
auto &cell = cell_for(ent);
Label to_set{content, 20};
to_set.init(cell, $font);
to_set.text->setFillColor(ColorValue::LIGHT_MID);
set<Label>(ent, to_set);
}
}
Clickable make_action(DinkyECS::World& target, Events::GUI event) {
return {[&, event](auto ent, auto data){
// remember that ent is passed in from the UI::mouse handler
target.send<Events::GUI>(event, ent, data);
}};
}
Clickable make_action(DinkyECS::World& target, Events::GUI event, std::any data) {
return {[&, event, data](auto ent, auto){
// remember that ent is passed in from the UI::mouse handler
target.send<Events::GUI>(event, ent, data);
}};
}
}

@ -1,237 +0,0 @@
#pragma once
#include "color.hpp"
#include "dinkyecs.hpp"
#include "lel.hpp"
#include <string>
#include <memory>
#include <SFML/Graphics.hpp>
#include "textures.hpp"
#include <functional>
#include "events.hpp"
#include "constants.hpp"
#include "components.hpp"
#include <any>
#include "shaders.hpp"
namespace guecs {
using std::shared_ptr, std::make_shared, std::wstring, std::string;
struct Textual {
std::wstring content;
unsigned int size = GUECS_FONT_SIZE;
sf::Color color = GUECS_TEXT_COLOR;
int padding = GUECS_PADDING;
bool centered = false;
shared_ptr<sf::Font> font = nullptr;
shared_ptr<sf::Text> text = nullptr;
void init(lel::Cell &cell, shared_ptr<sf::Font> font_ptr);
void update(std::wstring& new_content);
};
struct Label : public Textual {
template<typename... Args>
Label(Args... args) : Textual(args...)
{
centered = true;
}
Label() {
centered = true;
};
};
struct Clickable {
/* This is actually called by UI::mouse and passed the entity ID of the
* button pressed so you can interact with it in the event handler.
*/
std::function<void(DinkyECS::Entity ent, std::any data)> action;
};
struct Sprite {
std::string name;
int padding = GUECS_PADDING;
std::shared_ptr<sf::Sprite> sprite = nullptr;
std::shared_ptr<sf::Texture> texture = nullptr;
void init(lel::Cell &cell);
};
struct Rectangle {
int padding = GUECS_PADDING;
sf::Color color = GUECS_FILL_COLOR;
sf::Color border_color = GUECS_BORDER_COLOR;
int border_px = GUECS_BORDER_PX;
shared_ptr<sf::RectangleShape> shape = nullptr;
void init(lel::Cell& cell);
};
struct Meter {
float percent = 1.0f;
Rectangle bar;
void init(lel::Cell& cell);
};
struct ActionData {
std::any data;
};
struct CellName {
std::string name;
};
struct Effect {
float duration = 0.1f;
std::string name{"ui_shader"};
float $u_time_end = 0.0;
bool $active = false;
std::shared_ptr<sf::Clock> $clock = nullptr;
std::shared_ptr<sf::Shader> $shader = nullptr;
int $shader_version = 0;
void init(lel::Cell &cell);
void run();
void step();
shared_ptr<sf::Shader> checkout_ptr();
};
struct Sound {
std::string on_click{"ui_click"};
void play(bool hover);
};
struct Background {
float x = 0.0f;
float y = 0.0f;
float w = 0.0f;
float h = 0.0f;
sf::Color color = GUECS_BG_COLOR;
shared_ptr<sf::RectangleShape> shape = nullptr;
Background(lel::Parser& parser, sf::Color bg_color=GUECS_BG_COLOR) :
x(parser.grid_x),
y(parser.grid_y),
w(parser.grid_w),
h(parser.grid_h),
color(bg_color)
{}
Background() {}
void init();
};
class UI {
public:
DinkyECS::World $world;
std::unordered_map<std::string, DinkyECS::Entity> $name_ents;
shared_ptr<sf::Font> $font = nullptr;
lel::Parser $parser;
std::string $grid = "";
UI();
void position(int x, int y, int width, int height);
void layout(std::string grid);
DinkyECS::Entity init_entity(std::string name);
DinkyECS::Entity entity(std::string name);
inline lel::CellMap& cells() {
return $parser.cells;
}
inline DinkyECS::World& world() {
return $world;
}
void init();
void render(sf::RenderWindow& window);
bool mouse(float x, float y, bool hover);
void debug_layout(sf::RenderWindow& window);
template <typename Comp>
void set(DinkyECS::Entity ent, Comp val) {
$world.set<Comp>(ent, val);
}
template <typename Comp>
void set_init(DinkyECS::Entity ent, Comp val) {
dbc::check(has<lel::Cell>(ent),"WRONG! slot is missing its cell?!");
auto& cell = get<lel::Cell>(ent);
val.init(cell);
$world.set<Comp>(ent, val);
}
template <typename Comp>
void do_if(DinkyECS::Entity ent, std::function<void(Comp &)> cb) {
if($world.has<Comp>(ent)) {
cb($world.get<Comp>(ent));
}
}
lel::Cell& cell_for(DinkyECS::Entity ent) {
return $world.get<lel::Cell>(ent);
}
lel::Cell& cell_for(std::string name) {
DinkyECS::Entity ent = entity(name);
return $world.get<lel::Cell>(ent);
}
template <typename Comp>
Comp& get(DinkyECS::Entity entity) {
return $world.get<Comp>(entity);
}
template <typename Comp>
std::optional<Comp> get_if(DinkyECS::Entity entity) {
return $world.get_if<Comp>(entity);
}
template <typename Comp>
bool has(DinkyECS::Entity entity) {
return $world.has<Comp>(entity);
}
template <typename Comp>
void remove(DinkyECS::Entity ent) {
$world.remove<Comp>(ent);
}
template <typename Comp>
void close(string region) {
auto ent = entity(region);
remove<Comp>(ent);
}
template<typename T>
void render_helper(sf::RenderWindow& window, DinkyECS::Entity ent, bool is_shape, T& target) {
sf::Shader *shader_ptr = nullptr;
if($world.has<Effect>(ent)) {
auto& shader = $world.get<Effect>(ent);
if(shader.$active && !is_shape) {
auto ptr = shader.checkout_ptr();
ptr->setUniform("is_shape", is_shape);
// NOTE: this is needed because SFML doesn't handle shared_ptr
shader_ptr = ptr.get();
}
}
window.draw(*target, shader_ptr);
}
void show_sprite(string region, string sprite_name);
void show_text(string region, wstring content);
void update_text(string region, wstring content);
void update_label(string region, wstring content);
void show_label(string region, wstring content);
};
Clickable make_action(DinkyECS::World& target, Events::GUI event);
Clickable make_action(DinkyECS::World& target, Events::GUI event, std::any data);
}

@ -1,4 +1,4 @@
#include "boss_fight_ui.hpp" #include "gui/boss_fight_ui.hpp"
#include "easings.hpp" #include "easings.hpp"
#include "sound.hpp" #include "sound.hpp"
#include <fmt/xchar.h> #include <fmt/xchar.h>
@ -54,7 +54,7 @@ namespace gui {
$boss_background = textures::get(boss.background); $boss_background = textures::get(boss.background);
$boss_background.sprite->setPosition({BOSS_VIEW_X, BOSS_VIEW_Y}); $boss_background.sprite->setPosition({BOSS_VIEW_X, BOSS_VIEW_Y});
$status.world().set_the<Background>({$status.$parser}); $status.set<Background>($status.MAIN, {$status.$parser});
if(boss.stage) { if(boss.stage) {
$boss_has_stage = true; $boss_has_stage = true;

@ -1,7 +1,7 @@
#pragma once #pragma once
#include <SFML/Graphics/RenderWindow.hpp> #include <SFML/Graphics/RenderWindow.hpp>
#include <SFML/Graphics/Font.hpp> #include <SFML/Graphics/Font.hpp>
#include "guecs.hpp" #include <guecs/ui.hpp>
#include "textures.hpp" #include "textures.hpp"
#include "components.hpp" #include "components.hpp"
#include <SFML/System/Clock.hpp> #include <SFML/System/Clock.hpp>

@ -1,8 +1,8 @@
#include "combat_ui.hpp" #include "gui/combat_ui.hpp"
#include "constants.hpp" #include "constants.hpp"
#include "color.hpp"
#include "rituals.hpp" #include "rituals.hpp"
#include <fmt/xchar.h> #include <fmt/xchar.h>
#include "gui/guecstra.hpp"
namespace gui { namespace gui {
using namespace guecs; using namespace guecs;
@ -12,19 +12,23 @@ namespace gui {
{ {
$gui.position(COMBAT_UI_X, COMBAT_UI_Y, COMBAT_UI_WIDTH, COMBAT_UI_HEIGHT); $gui.position(COMBAT_UI_X, COMBAT_UI_Y, COMBAT_UI_WIDTH, COMBAT_UI_HEIGHT);
$gui.layout( $gui.layout(
"[*%(100,150)button_0 | *%(100,150)button_1 | *%(100,150)button_2 | *%(100,150)button_3]"); "[button_0 | button_1 | button_2 | button_3"
"|button_4 | button_5 | button_6 | hp_gauge ]"
);
} }
DinkyECS::Entity CombatUI::make_button(std::string name, std::wstring label, Events::GUI event, int action, const std::string &icon_name, DinkyECS::Entity CombatUI::make_button(
const std::string &sound, const std::string &effect_name) std::string name,
Events::GUI event,
int action,
const std::string &icon_name,
const std::string &sound,
const std::string &effect_name)
{ {
(void)label;
auto button = $gui.entity(name); auto button = $gui.entity(name);
$gui.set<Sprite>(button, {icon_name}); $gui.set<Sprite>(button, {icon_name});
// $gui.set<Rectangle>(button, {});
// $gui.set<Label>(button, {label});
$gui.set<Sound>(button, {sound}); $gui.set<Sound>(button, {sound});
$gui.set<Effect>(button, {.duration=1.0f, .name=effect_name}); $gui.set<Effect>(button, {.duration=0.5f, .name=effect_name});
$gui.set<Clickable>(button, $gui.set<Clickable>(button,
guecs::make_action(*$level.world, event, {action})); guecs::make_action(*$level.world, event, {action}));
@ -32,35 +36,38 @@ namespace gui {
} }
void CombatUI::init() { void CombatUI::init() {
$gui.world().set_the<Background>({$gui.$parser, ColorValue::DARK_MID}); using guecs::THEME;
auto& the_belt = $level.world->get<combat::RitualBelt>($level.player); $gui.set<Background>($gui.MAIN, {$gui.$parser, THEME.DARK_MID});
auto& the_belt = $level.world->get_the<ritual::Belt>();
for(int slot = 0; slot < 4; slot++) { for(int slot = 0; slot < the_belt.max_slots; slot++) {
if(the_belt.has(slot)) { if(the_belt.has(slot)) {
std::string name = fmt::format("button_{}", slot); std::string name = fmt::format("button_{}", slot);
std::wstring label = fmt::format(L"Attack {}", slot+1);
auto& ritual = the_belt.get(slot); auto& ritual = the_belt.get(slot);
using enum ritual::Element;
using enum combat::RitualElement;
switch(ritual.element) { switch(ritual.element) {
case FIRE: case FIRE:
make_button(name, label, Events::GUI::ATTACK, make_button(name, Events::GUI::ATTACK,
slot, "broken_yoyo-64", "fireball_01", "flame"); slot, "broken_yoyo-64", "fireball_01", "flame");
break; break;
case LIGHTNING: case LIGHTNING:
make_button(name, label, Events::GUI::ATTACK, make_button(name, Events::GUI::ATTACK,
slot, "stone_doll_cursed-64", "electric_shock_01", "lightning"); slot, "pocket_watch-64", "electric_shock_01", "lightning");
break; break;
default: default:
make_button(name, label, Events::GUI::ATTACK, make_button(name, Events::GUI::ATTACK,
slot, "severed_finger-64", "punch_cartoony", "ui_shader"); slot, "severed_finger-64", "punch_cartoony", "ui_shader");
} }
} }
} }
auto hp_gauge = $gui.entity("hp_gauge");
$gui.set<Sprite>(hp_gauge, {"stone_doll_cursed-64"});
$gui.set<Clickable>(hp_gauge,
guecs::make_action(*$level.world, Events::GUI::HP_STATUS, {}));
$gui.init(); $gui.init();
} }

@ -2,7 +2,7 @@
#include "levelmanager.hpp" #include "levelmanager.hpp"
#include <SFML/Graphics/RenderWindow.hpp> #include <SFML/Graphics/RenderWindow.hpp>
#include <SFML/Graphics/Font.hpp> #include <SFML/Graphics/Font.hpp>
#include "guecs.hpp" #include <guecs/ui.hpp>
#include "events.hpp" #include "events.hpp"
namespace gui { namespace gui {
@ -17,8 +17,8 @@ namespace gui {
void render(sf::RenderWindow& window); void render(sf::RenderWindow& window);
void update_level(GameLevel &level); void update_level(GameLevel &level);
bool mouse(float x, float y, bool hover); bool mouse(float x, float y, bool hover);
DinkyECS::Entity make_button(std::string name, std::wstring label, DinkyECS::Entity make_button(std::string name, Events::GUI event,
Events::GUI event, int action, const std::string &icon_name, int action, const std::string &icon_name,
const std::string &sound, const std::string &effect_name); const std::string &sound, const std::string &effect_name);
}; };
} }

@ -1,6 +1,5 @@
#include "debug_ui.hpp" #include "gui/debug_ui.hpp"
#include "constants.hpp" #include "constants.hpp"
#include "color.hpp"
#include "events.hpp" #include "events.hpp"
#include <optional> #include <optional>
#include <fmt/core.h> #include <fmt/core.h>
@ -29,6 +28,7 @@ namespace gui {
add_spawn_button("KNIGHT","armored_knight", "spawn2"); add_spawn_button("KNIGHT","armored_knight", "spawn2");
add_spawn_button("SPIDER_GIANT_HAIRY", "hairy_spider", "spawn3"); add_spawn_button("SPIDER_GIANT_HAIRY", "hairy_spider", "spawn3");
add_spawn_button("RAT_GIANT", "rat_with_sword", "spawn4"); add_spawn_button("RAT_GIANT", "rat_with_sword", "spawn4");
add_spawn_button("GOLD_SAVIOR", "gold_savior", "spawn5");
$gui.init(); $gui.init();
} }

@ -2,7 +2,7 @@
#include "levelmanager.hpp" #include "levelmanager.hpp"
#include <SFML/Graphics/RenderWindow.hpp> #include <SFML/Graphics/RenderWindow.hpp>
#include <SFML/Graphics/Font.hpp> #include <SFML/Graphics/Font.hpp>
#include "guecs.hpp" #include <guecs/ui.hpp>
#include "stats.hpp" #include "stats.hpp"
namespace gui { namespace gui {

@ -1,4 +1,4 @@
#include "gui_fsm.hpp" #include "gui/fsm.hpp"
#include <iostream> #include <iostream>
#include <chrono> #include <chrono>
#include <numeric> #include <numeric>
@ -8,6 +8,7 @@
#include "systems.hpp" #include "systems.hpp"
#include "events.hpp" #include "events.hpp"
#include "sound.hpp" #include "sound.hpp"
#include "shaders.hpp"
#include <fmt/xchar.h> #include <fmt/xchar.h>
namespace gui { namespace gui {
@ -22,9 +23,9 @@ namespace gui {
$status_ui($level), $status_ui($level),
$map_ui($level), $map_ui($level),
$mini_map($level), $mini_map($level),
$loot_ui($level),
$font{FONT_FILE_NAME} $font{FONT_FILE_NAME}
{ {
$levels.temp_create_player_rituals();
} }
void FSM::event(Event ev) { void FSM::event(Event ev) {
@ -37,6 +38,7 @@ namespace gui {
FSM_STATE(State, IN_COMBAT, ev); FSM_STATE(State, IN_COMBAT, ev);
FSM_STATE(State, COMBAT_ROTATE, ev); FSM_STATE(State, COMBAT_ROTATE, ev);
FSM_STATE(State, NEXT_LEVEL, ev); FSM_STATE(State, NEXT_LEVEL, ev);
FSM_STATE(State, LOOTING, ev);
FSM_STATE(State, END, ev); FSM_STATE(State, END, ev);
} }
} }
@ -46,6 +48,7 @@ namespace gui {
$main_ui.update_level($level); $main_ui.update_level($level);
$level.world->set_the<Debug>({}); $level.world->set_the<Debug>({});
$main_ui.init(); $main_ui.init();
$loot_ui.init();
// BUG: maybe this is a function on main_ui? // BUG: maybe this is a function on main_ui?
auto cell = $main_ui.$overlay_ui.$gui.cell_for("left"); auto cell = $main_ui.$overlay_ui.$gui.cell_for("left");
@ -55,12 +58,12 @@ namespace gui {
$combat_ui.init(); $combat_ui.init();
$status_ui.init(); $status_ui.init();
$status_ui.log(L"Welcome to the game!");
$boss_fight_ui = $levels.create_bossfight($level.world); $boss_fight_ui = $levels.create_bossfight($level.world);
$boss_fight_ui->init(); $boss_fight_ui->init();
$map_ui.init(); $map_ui.init();
$map_ui.log(L"Welcome to the game!");
$mini_map.init($main_ui.$overlay_ui.$gui); $mini_map.init($main_ui.$overlay_ui.$gui);
run_systems(); run_systems();
@ -110,6 +113,22 @@ namespace gui {
} }
} }
void FSM::LOOTING(Event ev) {
using enum Event;
switch(ev) {
case LOOT_OPEN:
$loot_ui.active = false;
state(State::IDLE);
break;
case TICK:
// do nothing
break;
default:
state(State::LOOTING);
}
}
void FSM::IDLE(Event ev) { void FSM::IDLE(Event ev) {
using enum Event; using enum Event;
@ -161,6 +180,10 @@ namespace gui {
case TICK: case TICK:
// do nothing // do nothing
break; break;
case LOOT_OPEN:
$loot_ui.active = true;
state(State::LOOTING);
break;
default: default:
dbc::sentinel("unhandled event in IDLE"); dbc::sentinel("unhandled event in IDLE");
} }
@ -229,6 +252,7 @@ namespace gui {
void FSM::keyboard_mouse() { void FSM::keyboard_mouse() {
while(const auto ev = $window.pollEvent()) { while(const auto ev = $window.pollEvent()) {
if(ev->is<sf::Event::Closed>()) { if(ev->is<sf::Event::Closed>()) {
event(Event::QUIT); event(Event::QUIT);
} }
@ -247,6 +271,7 @@ namespace gui {
$combat_ui.mouse(pos.x, pos.y, false); $combat_ui.mouse(pos.x, pos.y, false);
$status_ui.mouse(pos.x, pos.y, false); $status_ui.mouse(pos.x, pos.y, false);
$main_ui.mouse(pos.x, pos.y, false); $main_ui.mouse(pos.x, pos.y, false);
if($loot_ui.active) $loot_ui.mouse(pos.x, pos.y, false);
} }
} }
} else if(const auto* mouse = ev->getIf<sf::Event::MouseMoved>()) { } else if(const auto* mouse = ev->getIf<sf::Event::MouseMoved>()) {
@ -300,6 +325,9 @@ namespace gui {
autowalking = true; autowalking = true;
break; break;
case KEY::L: case KEY::L:
event(Event::LOOT_OPEN);
break;
case KEY::X:
event(Event::STAIRS_DOWN); event(Event::STAIRS_DOWN);
break; break;
default: default:
@ -321,6 +349,8 @@ namespace gui {
$status_ui.render($window); $status_ui.render($window);
$combat_ui.render($window); $combat_ui.render($window);
if($loot_ui.active) $loot_ui.render($window);
if($map_open) { if($map_open) {
$map_ui.render($window, $main_ui.$compass_dir); $map_ui.render($window, $main_ui.$compass_dir);
} else { } else {
@ -367,15 +397,15 @@ namespace gui {
auto &damage = std::any_cast<Events::Combat&>(data); auto &damage = std::any_cast<Events::Combat&>(data);
if(damage.enemy_did > 0) { if(damage.enemy_did > 0) {
$status_ui.log(fmt::format(L"Enemy HIT YOU for {} damage!", damage.enemy_did)); $map_ui.log(fmt::format(L"Enemy HIT YOU for {} damage!", damage.enemy_did));
} else { } else {
$status_ui.log(L"Enemy MISSED YOU."); $map_ui.log(L"Enemy MISSED YOU.");
} }
if(damage.player_did > 0) { if(damage.player_did > 0) {
$status_ui.log(fmt::format(L"You HIT enemy for {} damage!", damage.player_did)); $map_ui.log(fmt::format(L"You HIT enemy for {} damage!", damage.player_did));
} else { } else {
$status_ui.log(L"You MISSED the enemy."); $map_ui.log(L"You MISSED the enemy.");
} }
} }
break; break;
@ -390,15 +420,21 @@ namespace gui {
case eGUI::NO_NEIGHBORS: case eGUI::NO_NEIGHBORS:
event(Event::STOP_COMBAT); event(Event::STOP_COMBAT);
break; break;
case eGUI::LOOT_CLOSE:
// BUG: need to resolve GUI events vs. FSM events better
event(Event::LOOT_OPEN);
break;
case eGUI::LOOT: { case eGUI::LOOT: {
// auto &item = std::any_cast<InventoryItem&>(data); $map_ui.log(L"You picked up an item.");
// $status_ui.log(fmt::format("You picked up a {}.",
// std::string(item.data["name"])));
$status_ui.log(L"You picked up an item.");
} break; } break;
case eGUI::HP_STATUS:
System::player_status($level);
break;
case eGUI::NEW_RITUAL:
$combat_ui.init();
break;
case eGUI::EVADE: case eGUI::EVADE:
case eGUI::BLOCK: case eGUI::BLOCK:
case eGUI::HEAL:
dbc::log("YOU NEED TO IMPLEMENT THIS!!!!!"); dbc::log("YOU NEED TO IMPLEMENT THIS!!!!!");
break; break;
case eGUI::ATTACK: case eGUI::ATTACK:
@ -417,11 +453,11 @@ namespace gui {
case eGUI::NOOP: { case eGUI::NOOP: {
if(data.type() == typeid(std::string)) { if(data.type() == typeid(std::string)) {
auto name = std::any_cast<std::string>(data); auto name = std::any_cast<std::string>(data);
$status_ui.log(fmt::format(L"NOOP EVENT! {},{}", evt, entity)); $map_ui.log(fmt::format(L"NOOP EVENT! {},{}", evt, entity));
} }
} break; } break;
default: default:
$status_ui.log(fmt::format(L"INVALID EVENT! {},{}", evt, entity)); $map_ui.log(fmt::format(L"INVALID EVENT! {},{}", evt, entity));
} }
} }
} }
@ -436,6 +472,7 @@ namespace gui {
$mini_map.update_level($level); $mini_map.update_level($level);
$combat_ui.update_level($level); $combat_ui.update_level($level);
$main_ui.update_level($level); $main_ui.update_level($level);
$loot_ui.update_level($level);
$boss_fight_ui = $levels.create_bossfight($level.world); $boss_fight_ui = $levels.create_bossfight($level.world);
$boss_fight_ui->init(); $boss_fight_ui->init();

@ -1,15 +1,15 @@
#pragma once #pragma once
#include "constants.hpp" #include "constants.hpp"
#include "stats.hpp"
#include "levelmanager.hpp" #include "levelmanager.hpp"
#include "fsm.hpp" #include "simplefsm.hpp"
#include "debug_ui.hpp" #include "gui/debug_ui.hpp"
#include "main_ui.hpp" #include "gui/main_ui.hpp"
#include "combat_ui.hpp" #include "gui/combat_ui.hpp"
#include "status_ui.hpp" #include "gui/status_ui.hpp"
#include "boss_fight_ui.hpp" #include "gui/loot_ui.hpp"
#include "map_view.hpp" #include "gui/boss_fight_ui.hpp"
#include "mini_map.hpp" #include "gui/map_view.hpp"
#include "gui/mini_map.hpp"
namespace gui { namespace gui {
enum class State { enum class State {
@ -20,6 +20,7 @@ namespace gui {
ATTACKING, ATTACKING,
ROTATING, ROTATING,
NEXT_LEVEL, NEXT_LEVEL,
LOOTING,
IDLE, IDLE,
END END
}; };
@ -39,7 +40,8 @@ namespace gui {
START_COMBAT = 11, START_COMBAT = 11,
STOP_COMBAT = 12, STOP_COMBAT = 12,
STAIRS_DOWN = 13, STAIRS_DOWN = 13,
QUIT = 14 LOOT_OPEN=14,
QUIT = 15
}; };
class FSM : public DeadSimpleFSM<State, Event> { class FSM : public DeadSimpleFSM<State, Event> {
@ -58,6 +60,7 @@ namespace gui {
StatusUI $status_ui; StatusUI $status_ui;
MapViewUI $map_ui; MapViewUI $map_ui;
MiniMapUI $mini_map; MiniMapUI $mini_map;
LootUI $loot_ui;
sf::Font $font; sf::Font $font;
FSM(); FSM();
@ -75,6 +78,7 @@ namespace gui {
void IN_COMBAT(Event ev); void IN_COMBAT(Event ev);
void COMBAT_ROTATE(Event ev); void COMBAT_ROTATE(Event ev);
void NEXT_LEVEL(Event ev); void NEXT_LEVEL(Event ev);
void LOOTING(Event ev);
void END(Event ev); void END(Event ev);
void try_move(int dir, bool strafe); void try_move(int dir, bool strafe);

@ -0,0 +1,18 @@
#include "gui/guecstra.hpp"
namespace guecs {
Clickable make_action(DinkyECS::World& target, Events::GUI event) {
return {[&, event](auto ent, auto data){
// remember that ent is passed in from the UI::mouse handler
target.send<Events::GUI>(event, ent, data);
}};
}
Clickable make_action(DinkyECS::World& target, Events::GUI event, std::any data) {
return {[&, event, data](auto ent, auto){
// remember that ent is passed in from the UI::mouse handler
target.send<Events::GUI>(event, ent, data);
}};
}
}

@ -0,0 +1,8 @@
#include "components.hpp"
#include "events.hpp"
#include <guecs/ui.hpp>
namespace guecs {
Clickable make_action(DinkyECS::World& target, Events::GUI event);
Clickable make_action(DinkyECS::World& target, Events::GUI event, std::any data);
}

@ -0,0 +1,65 @@
#include "gui/loot_ui.hpp"
#include "constants.hpp"
#include <fmt/xchar.h>
#include "gui/guecstra.hpp"
namespace gui {
using namespace guecs;
LootUI::LootUI(GameLevel level) :
$level(level)
{
$gui.position(RAY_VIEW_X+RAY_VIEW_WIDTH/2-200,
RAY_VIEW_Y+RAY_VIEW_HEIGHT/2-200, 400, 400);
$gui.layout(
"[item_0 | item_1 |item_2 | item_3 ]"
"[item_4 | item_5 |item_6 | item_7 ]"
"[item_8 | item_9 |item_10| item_11]"
"[item_12| item_13|item_14|item_15 ]"
"[_ | %(100,50)close| _]"
);
}
void LootUI::init() {
using guecs::THEME;
auto bg_color = THEME.DARK_LIGHT;
bg_color.a = 140;
$gui.set<Background>($gui.MAIN, {$gui.$parser, bg_color});
// fill in 4 slots for prototype
for(int i = 0; i < 4; 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, {
[=](auto, auto) { fmt::println("clicked button_{}", i); }
});
$gui.set<guecs::Sprite>(id, {"broken_yoyo-64"});
}
auto close = $gui.entity("close");
$gui.set<guecs::Rectangle>(close, {});
$gui.set<guecs::Label>(close, {L"CLOSE"});
$gui.set<guecs::Clickable>(close,
guecs::make_action(*$level.world, Events::GUI::LOOT_CLOSE));
$gui.init();
}
void LootUI::render(sf::RenderWindow& window) {
$gui.render(window);
}
void LootUI::update_level(GameLevel &level) {
$level = level;
init();
}
bool LootUI::mouse(float x, float y, bool hover) {
return $gui.mouse(x, y, hover);
}
}

@ -0,0 +1,22 @@
#pragma once
#include "levelmanager.hpp"
#include <SFML/Graphics/RenderWindow.hpp>
#include <SFML/Graphics/Font.hpp>
#include <guecs/ui.hpp>
#include "events.hpp"
namespace gui {
class LootUI {
public:
bool active = false;
guecs::UI $gui;
GameLevel $level;
LootUI(GameLevel level);
void init();
void render(sf::RenderWindow& window);
void update_level(GameLevel &level);
bool mouse(float x, float y, bool hover);
};
}

@ -1,4 +1,4 @@
#include "main_ui.hpp" #include "gui/main_ui.hpp"
#include "components.hpp" #include "components.hpp"
#include "easings.hpp" #include "easings.hpp"
#include <fmt/xchar.h> #include <fmt/xchar.h>
@ -37,10 +37,6 @@ namespace gui {
$overlay_ui.init(); $overlay_ui.init();
} }
void MainUI::show_level() {
$show_level = true;
}
void MainUI::render() { void MainUI::render() {
auto aimed_at = $camera.aimed_at(); auto aimed_at = $camera.aimed_at();
if($level.collision->occupied(aimed_at)) { if($level.collision->occupied(aimed_at)) {
@ -49,19 +45,8 @@ namespace gui {
$rayview.aiming_at = 0; $rayview.aiming_at = 0;
} }
if($show_level) {
auto time = $clock.getElapsedTime();
auto st = textures::get("down_the_well");
float tick = ease::in_out_back(ease::sine(time.asSeconds()));
float scale = std::lerp(1.0, 1.3, tick);
st.sprite->setScale({scale, scale});
$window.draw(*st.sprite);
$overlay_ui.show_label("middle", L"INTO THE WELL YOU GO...");
} else {
if($needs_render) $rayview.render(); if($needs_render) $rayview.render();
$rayview.draw($window); $rayview.draw($window);
}
$overlay_ui.render($window); $overlay_ui.render($window);
} }
@ -125,12 +110,6 @@ namespace gui {
} }
void MainUI::mouse(int x, int y, bool hover) { void MainUI::mouse(int x, int y, bool hover) {
if($show_level) {
$show_level = false;
$level.world->send<Events::GUI>(Events::GUI::STAIRS_DOWN, $level.player, {});
$overlay_ui.close_label("middle");
} else {
$overlay_ui.$gui.mouse(x, y, hover); $overlay_ui.$gui.mouse(x, y, hover);
} }
} }
}

@ -3,19 +3,18 @@
#include <SFML/Graphics/RenderWindow.hpp> #include <SFML/Graphics/RenderWindow.hpp>
#include <SFML/System/Clock.hpp> #include <SFML/System/Clock.hpp>
#include "stats.hpp" #include "stats.hpp"
#include "overlay_ui.hpp" #include <guecs/ui.hpp>
#include "debug_ui.hpp" #include "gui/overlay_ui.hpp"
#include "gui/debug_ui.hpp"
#include "raycaster.hpp" #include "raycaster.hpp"
#include "camera.hpp" #include "camera.hpp"
#include <optional> #include <optional>
#include "guecs.hpp"
namespace gui { namespace gui {
class MainUI { class MainUI {
public: public:
int $compass_dir = 0; int $compass_dir = 0;
bool $show_level = false;
bool $needs_render = true; bool $needs_render = true;
sf::Clock $clock; sf::Clock $clock;
sf::RenderWindow& $window; sf::RenderWindow& $window;
@ -41,7 +40,6 @@ namespace gui {
void render(); void render();
void dirty(); void dirty();
void show_level();
void health_low(); void health_low();
void dead_entity(DinkyECS::Entity entity); void dead_entity(DinkyECS::Entity entity);
}; };

@ -13,6 +13,7 @@
namespace gui { namespace gui {
using namespace components; using namespace components;
using namespace guecs;
MapViewUI::MapViewUI(GameLevel &level) : MapViewUI::MapViewUI(GameLevel &level) :
$level(level), $level(level),
@ -29,16 +30,15 @@ namespace gui {
//auto cell = overlay.cell_for(top_right); //auto cell = overlay.cell_for(top_right);
$gui.position(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); $gui.position(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
$gui.layout( $gui.layout(
"[status| *%(200)map_grid | _ ]" "[log_view| *%(200)map_grid | _ ]"
); );
auto grid = $gui.entity("map_grid"); auto grid = $gui.entity("map_grid");
$gui.set<guecs::Textual>(grid, $gui.set<Textual>(grid,
{L"Loading...", 65, {27, 26, 23, 150}, 10}); {L"Loading...", 65, {27, 26, 23, 150}, 10});
auto status = $gui.entity("status"); $log_to = $gui.entity("log_view");
$gui.set<guecs::Textual>(status, $gui.set<Textual>($log_to, {L"Welcome to the Game!", 25, {37, 36, 33}, 25});
{L"Loading...", 25, {37, 36, 33}, 25});
$paper.sprite->setPosition({0, 0}); $paper.sprite->setPosition({0, 0});
$gui.init(); $gui.init();
@ -48,23 +48,33 @@ namespace gui {
window.draw(*$paper.sprite); window.draw(*$paper.sprite);
auto grid = $gui.entity("map_grid"); auto grid = $gui.entity("map_grid");
auto status = $gui.entity("status");
std::wstring map_out = System::draw_map($level, 23, 9, compass_dir); std::wstring map_out = System::draw_map($level, 23, 9, compass_dir);
auto& map_text = $gui.get<guecs::Textual>(grid); auto& map_text = $gui.get<Textual>(grid);
map_text.update(map_out); map_text.update(map_out);
auto& status_text = $gui.get<guecs::Textual>(status);
std::wstring status_out = fmt::format(
L"Level: {}\n"
L"Enemies Killed: A Lot\n",
$level.index + 1);
status_text.update(status_out);
$gui.render(window); $gui.render(window);
// $gui.debug_layout(window); // $gui.debug_layout(window);
} }
void MapViewUI::update() {
if($gui.has<Textual>($log_to)) {
auto& text = $gui.get<Textual>($log_to);
//BUG: I'm calling this what it is, fix it
wstring log_garbage;
for(auto msg : $messages) {
log_garbage += msg + L"\n";
}
text.update(log_garbage);
}
}
void MapViewUI::log(wstring msg) {
$messages.push_front(msg);
if($messages.size() > MAX_LOG_MESSAGES) {
$messages.pop_back();
}
update();
}
} }

@ -1,19 +1,24 @@
#pragma once #pragma once
#include "levelmanager.hpp" #include "levelmanager.hpp"
#include "textures.hpp" #include "textures.hpp"
#include "guecs.hpp" #include <guecs/ui.hpp>
#include "tilemap.hpp" #include "tilemap.hpp"
#include <string>
namespace gui { namespace gui {
class MapViewUI { class MapViewUI {
public: public:
guecs::UI $gui; guecs::UI $gui;
GameLevel $level; GameLevel $level;
DinkyECS::Entity $log_to;
textures::SpriteTexture $paper; textures::SpriteTexture $paper;
std::deque<std::wstring> $messages;
MapViewUI(GameLevel &level); MapViewUI(GameLevel &level);
void init(); void init();
void render(sf::RenderWindow &window, int compass_dir); void render(sf::RenderWindow &window, int compass_dir);
void update_level(GameLevel &level); void update_level(GameLevel &level);
void log(std::wstring msg);
void update();
}; };
} }

@ -1,7 +1,7 @@
#pragma once #pragma once
#include "levelmanager.hpp" #include "levelmanager.hpp"
#include "textures.hpp" #include "textures.hpp"
#include "guecs.hpp" #include <guecs/ui.hpp>
#include "tilemap.hpp" #include "tilemap.hpp"
namespace gui { namespace gui {

@ -1,6 +1,5 @@
#include "overlay_ui.hpp" #include "gui/overlay_ui.hpp"
#include "constants.hpp" #include "constants.hpp"
#include "color.hpp"
#include "events.hpp" #include "events.hpp"
#include <optional> #include <optional>

@ -1,9 +1,11 @@
#pragma once #pragma once
#include <SFML/Graphics/RenderWindow.hpp> #include <SFML/Graphics/RenderWindow.hpp>
#include <SFML/Graphics/Font.hpp> #include <SFML/Graphics/Font.hpp>
#include "guecs.hpp" #include <guecs/ui.hpp>
namespace gui { namespace gui {
using std::string;
class OverlayUI { class OverlayUI {
public: public:
guecs::UI $gui; guecs::UI $gui;

@ -0,0 +1,252 @@
#include "gui/ritual_ui.hpp"
#include "components.hpp"
#include <guecs/ui.hpp>
#include "rand.hpp"
#include "animation.hpp"
#include "rand.hpp"
#include "sound.hpp"
#include "events.hpp"
namespace gui {
namespace ritual {
using namespace guecs;
using std::any, std::any_cast, std::string, std::make_any;
UI::UI(GameLevel level) :
$level(level),
$blanket($level.world->get_the<::ritual::Blanket>())
{
$gui.position(STATUS_UI_X, STATUS_UI_Y, STATUS_UI_WIDTH, STATUS_UI_HEIGHT);
$gui.layout(
"[_]"
"[inv_slot0 | inv_slot1 | inv_slot2| inv_slot3]"
"[inv_slot4 | inv_slot5 | inv_slot6| inv_slot7]"
"[inv_slot8 | inv_slot9 | inv_slot10| inv_slot11]"
"[inv_slot12 | inv_slot13 | inv_slot14| inv_slot15]"
"[inv_slot16 | inv_slot17 | inv_slot18| inv_slot19]"
"[_ |*%(200,400)result_text|_]"
"[*%(100,200)result_image|_ |_]"
"[_|_|_]"
"[_|_|_]"
"[_]"
"[ ritual_ui ]");
}
void UI::event(Event ev, std::any data) {
switch($state) {
FSM_STATE(State, START, ev);
FSM_STATE(State, OPENED, ev, data);
FSM_STATE(State, CRAFTING, ev, data);
FSM_STATE(State, CLOSED, ev);
FSM_STATE(State, OPENING, ev);
FSM_STATE(State, CLOSING, ev);
}
}
void UI::START(Event) {
$ritual_ui = textures::get("ritual_crafting_area");
$ritual_ui.sprite->setPosition($gui.get_position());
$ritual_ui.sprite->setTextureRect($ritual_closed_rect);
state(State::CLOSED);
$ritual_anim = animation::load("ritual_blanket");
auto open_close_toggle = $gui.entity("ritual_ui");
$gui.set<Clickable>(open_close_toggle, {
[&](auto, auto){ event(Event::TOGGLE); }
});
$craft_state = $ritual_engine.start();
$gui.init();
state(State::CLOSED);
}
void UI::OPENED(Event ev, std::any data) {
if(ev == Event::TOGGLE) {
clear_blanket();
state(State::CLOSING);
} else if(ev == Event::SELECT) {
// do this before transitioning
state(State::CRAFTING);
UI::CRAFTING(ev, data);
}
}
void UI::CRAFTING(Event ev, std::any data) {
if(ev == Event::TOGGLE) {
clear_blanket();
state(State::CLOSING);
} else if(ev == Event::COMBINE) {
complete_combine();
} else if(ev == Event::SELECT) {
dbc::check(data.has_value(), "OPENED state given SELECT with no data");
auto pair = std::any_cast<SelectedItem>(data);
select_item(pair);
update_selection_state();
}
}
void UI::CLOSED(Event ev) {
if(ev == Event::TOGGLE) {
$ritual_anim.play();
load_blanket();
state(State::OPENING);
}
}
void UI::OPENING(Event ev) {
if(ev == Event::TICK) {
if(!animation::apply($ritual_anim, $ritual_ui)) {
state(State::OPENED);
}
}
}
void UI::CLOSING(Event ev) {
if(ev == Event::TICK) {
$ritual_ui.sprite->setTextureRect($ritual_closed_rect);
state(State::CLOSED);
}
}
bool UI::mouse(float x, float y, bool hover) {
return $gui.mouse(x, y, hover);
}
bool UI::is_open() {
return !in_state(State::CLOSED);
}
void UI::render(sf::RenderWindow &window) {
event(Event::TICK);
window.draw(*$ritual_ui.sprite);
if(in_state(State::OPENED) || in_state(State::CRAFTING)) {
$gui.render(window);
// $gui.debug_layout(window);
}
}
void UI::clear_blanket() {
for(int i = 0; i < INV_SLOTS; i++) {
auto slot_id = $gui.entity("inv_slot", i);
if($gui.has<Sprite>(slot_id)) {
$gui.remove<Sprite>(slot_id);
$gui.remove<Clickable>(slot_id);
}
}
$blanket.reset();
}
void UI::select_item(SelectedItem pair) {
auto& sprite = $gui.get<Sprite>(pair.slot_id);
if($blanket.is_selected(pair.item_id)) {
$blanket.deselect(pair.item_id);
sprite.sprite->setColor({255, 255, 255, 255});
} else {
$blanket.select(pair.item_id);
sprite.sprite->setColor({255, 200, 200, 200});
}
}
void UI::update_selection_state() {
if($blanket.no_selections()) {
clear_craft_result();
state(State::OPENED);
} else {
run_crafting_engine();
show_craft_result();
}
}
void UI::load_blanket() {
// update the list of available items
int i = 0;
for(auto& [item_id, item] : $blanket.contents) {
auto slot_id = $gui.entity("inv_slot", i++);
auto icon_name = fmt::format("{}-64", item);
$gui.set_init<Sprite>(slot_id, {icon_name});
$gui.set<Clickable>(slot_id, {
[&, slot_id, item_id](auto, auto) {
auto data = std::make_any<SelectedItem>(slot_id, item_id);
event(Event::SELECT, data);
}
});
}
for(; i < INV_SLOTS; i++) {
auto slot_id = $gui.entity("inv_slot", i);
$gui.remove<Sprite>(slot_id);
$gui.remove<Clickable>(slot_id);
}
}
void UI::complete_combine() {
if($craft_state.is_combined()) {
auto ritual = $ritual_engine.finalize($craft_state);
auto& belt = $level.world->get_the<::ritual::Belt>();
belt.equip(belt.next(), ritual);
$level.world->send<Events::GUI>(Events::GUI::NEW_RITUAL, $level.player, {});
$blanket.consume_crafting();
clear_craft_result();
load_blanket();
state(State::OPENED);
}
}
void UI::run_crafting_engine() {
$craft_state.reset();
for(auto [item_id, setting] : $blanket.selected) {
auto& item = $blanket.get(item_id);
$ritual_engine.load_junk($craft_state, item);
}
$ritual_engine.plan($craft_state);
}
void UI::show_craft_result() {
using enum ::ritual::Element;
auto ritual = $ritual_engine.finalize($craft_state);
auto combine = $gui.entity("result_image");
if($craft_state.is_combined()) {
$gui.show_label("result_text", L"This might work...");
switch(ritual.element) {
case FIRE:
$gui.show_sprite("result_image", "broken_yoyo-64");
break;
case LIGHTNING:
$gui.show_sprite("result_image", "pocket_watch-64");
break;
default:
$gui.show_sprite("result_image", "severed_finger-64");
}
$gui.set<Clickable>(combine, {
[&](auto, auto){ event(Event::COMBINE); }
});
} else {
$gui.show_label("result_text", L"That won't work.");
$gui.show_sprite("result_image", "dubious_combination-128");
$gui.remove<Clickable>(combine);
return;
}
}
void UI::clear_craft_result() {
$blanket.reset();
$gui.close<Label>("result_text");
$gui.close<Sprite>("result_image");
}
}
}

@ -0,0 +1,69 @@
#pragma once
#include "levelmanager.hpp"
#include "constants.hpp"
#include <deque>
#include "textures.hpp"
#include <guecs/ui.hpp>
#include "rituals.hpp"
#include "simplefsm.hpp"
namespace gui {
namespace ritual {
enum class State {
START=0,
OPENED=1,
CLOSED=2,
OPENING=3,
CLOSING=4,
CRAFTING=5
};
enum class Event {
STARTED=0,
TOGGLE=1,
TICK=2,
SELECT=3,
COMBINE=4
};
struct SelectedItem {
DinkyECS::Entity slot_id;
DinkyECS::Entity item_id;
};
class UI : public DeadSimpleFSM<State, Event> {
public:
sf::IntRect $ritual_closed_rect{{0,0},{380,720}};
sf::IntRect $ritual_open_rect{{380 * 2,0},{380,720}};
components::Animation $ritual_anim;
guecs::UI $gui;
GameLevel $level;
textures::SpriteTexture $ritual_ui;
::ritual::Blanket& $blanket;
::ritual::Engine $ritual_engine;
::ritual::CraftingState $craft_state;
UI(GameLevel level);
void event(Event ev, std::any data={});
void START(Event);
void OPENED(Event, std::any data={});
void CRAFTING(Event, std::any data={});
void CLOSED(Event);
void OPENING(Event);
void CLOSING(Event);
bool mouse(float x, float y, bool hover);
void render(sf::RenderWindow &window);
bool is_open();
void load_blanket();
void clear_blanket();
void select_item(SelectedItem pair);
void show_craft_result();
void clear_craft_result();
void run_crafting_engine();
void complete_combine();
void update_selection_state();
};
}
}

@ -1,8 +1,7 @@
#include "status_ui.hpp" #include "gui/status_ui.hpp"
#include "components.hpp" #include "components.hpp"
#include "inventory.hpp" #include "inventory.hpp"
#include "color.hpp" #include <guecs/ui.hpp>
#include "guecs.hpp"
#include "rand.hpp" #include "rand.hpp"
#include <fmt/xchar.h> #include <fmt/xchar.h>
@ -18,9 +17,9 @@ namespace gui {
"[ ritual_ui ]" "[ ritual_ui ]"
"[inv_slot1 | inv_slot2 | inv_slot3]" "[inv_slot1 | inv_slot2 | inv_slot3]"
"[inv_slot4 | inv_slot5 | inv_slot6]" "[inv_slot4 | inv_slot5 | inv_slot6]"
"[*%(100,300)log_view]" "[*%(200,300)character_view|_|stat1]"
"[_]" "[_|_|stat2]"
"[_]"); "[_|_|stat3]");
size_t inv_id = 0; size_t inv_id = 0;
for(auto [name, entity] : $gui.$name_ents) { for(auto [name, entity] : $gui.$name_ents) {
@ -31,13 +30,17 @@ namespace gui {
} }
void StatusUI::init() { void StatusUI::init() {
$gui.world().set_the<Background>({$gui.$parser}); $gui.set<Background>($gui.MAIN, {$gui.$parser});
for(auto& [name, cell] : $gui.cells()) { for(auto& [name, cell] : $gui.cells()) {
if(name == "log_view") { if(name == "character_view") {
$log_to = $gui.entity("log_view"); auto char_view = $gui.entity(name);
$gui.set<Rectangle>($log_to, {}); $gui.set<Rectangle>(char_view, {});
$gui.set<Textual>($log_to, {L"Welcome to the Game!", 20}); $gui.set<Sprite>(char_view, {"peasant_girl"});
} else if(name.starts_with("stat")) {
auto stat = $gui.entity(name);
$gui.set<Rectangle>(stat, {});
$gui.set<Label>(stat, {guecs::to_wstring(name)});
} else { } else {
auto button = $gui.entity(name); auto button = $gui.entity(name);
$gui.set<Rectangle>(button, {}); $gui.set<Rectangle>(button, {});
@ -57,7 +60,7 @@ namespace gui {
} }
} }
$ritual_ui.init(); $ritual_ui.event(ritual::Event::STARTED);
$gui.init(); $gui.init();
} }
@ -70,7 +73,7 @@ namespace gui {
} }
void StatusUI::select_ritual() { void StatusUI::select_ritual() {
$ritual_ui.toggle(); $ritual_ui.event(ritual::Event::TOGGLE);
} }
void StatusUI::select_slot(DinkyECS::Entity ent, any slot_name) { void StatusUI::select_slot(DinkyECS::Entity ent, any slot_name) {
@ -85,30 +88,12 @@ namespace gui {
if(inventory.has_item(inv_id)) { if(inventory.has_item(inv_id)) {
auto [used, name] = inventory.use($level, inv_id); auto [used, name] = inventory.use($level, inv_id);
if(used) {
// log(fmt::format(L"Used item: {}", name));
log(fmt::format(L"Used item: {}", L"FIX ME ZED"));
} else {
// log(fmt::format(L"You are out of {}.", name));
log(fmt::format(L"Used item: {}", L"FIX ME ZED"));
}
} }
} }
} }
/* WARNING: This is really not the greatest way to do this. */ /* WARNING: This is really not the greatest way to do this. */
void StatusUI::update() { void StatusUI::update() {
if($gui.has<Textual>($log_to)) {
auto& text = $gui.get<Textual>($log_to);
//BUG: I'm calling this what it is, fix it
wstring log_garbage;
for(auto msg : $messages) {
log_garbage += msg + L"\n";
}
text.update(log_garbage);
}
auto world = $level.world; auto world = $level.world;
if(world->has<components::Inventory>($level.player)) { if(world->has<components::Inventory>($level.player)) {
auto& inventory = world->get<components::Inventory>($level.player); auto& inventory = world->get<components::Inventory>($level.player);
@ -137,17 +122,10 @@ namespace gui {
void StatusUI::render(sf::RenderWindow &window) { void StatusUI::render(sf::RenderWindow &window) {
$gui.render(window); $gui.render(window);
// $gui.debug_layout(window);
$ritual_ui.render(window); $ritual_ui.render(window);
} }
void StatusUI::log(wstring msg) {
$messages.push_front(msg);
if($messages.size() > MAX_LOG_MESSAGES) {
$messages.pop_back();
}
update();
}
void StatusUI::update_level(GameLevel &level) { void StatusUI::update_level(GameLevel &level) {
$level = level; $level = level;
init(); init();

@ -3,25 +3,22 @@
#include "constants.hpp" #include "constants.hpp"
#include <deque> #include <deque>
#include "textures.hpp" #include "textures.hpp"
#include "guecs.hpp" #include <guecs/ui.hpp>
#include "ritual_ui.hpp" #include "gui/ritual_ui.hpp"
namespace gui { namespace gui {
class StatusUI { class StatusUI {
public: public:
guecs::UI $gui; guecs::UI $gui;
DinkyECS::Entity $log_to;
std::map<std::string, size_t> $slots; std::map<std::string, size_t> $slots;
std::deque<std::wstring> $messages;
GameLevel $level; GameLevel $level;
RitualUI $ritual_ui; ritual::UI $ritual_ui;
StatusUI(GameLevel level); StatusUI(GameLevel level);
void select_slot(DinkyECS::Entity ent, std::any data); void select_slot(DinkyECS::Entity ent, std::any data);
void select_ritual(); void select_ritual();
void update_level(GameLevel &level); void update_level(GameLevel &level);
bool mouse(float x, float y, bool hover); bool mouse(float x, float y, bool hover);
void log(std::wstring msg);
void init(); void init();
void render(sf::RenderWindow &window); void render(sf::RenderWindow &window);
void update(); void update();

@ -1,117 +0,0 @@
#include "lel.hpp"
#include <fmt/core.h>
#include "dbc.hpp"
#include <numeric>
#include "lel_parser.cpp"
namespace lel {
Parser::Parser(int x, int y, int width, int height) :
grid_x(x),
grid_y(y),
grid_w(width),
grid_h(height),
cur(0, 0)
{
}
Parser::Parser() : cur(0, 0) { }
void Parser::position(int x, int y, int width, int height) {
grid_x = x;
grid_y = y;
grid_w = width;
grid_h = height;
}
void Parser::id(std::string name) {
if(name != "_") {
dbc::check(!cells.contains(name),
fmt::format("duplicate cell name {}", name));
cells.insert_or_assign(name, cur);
}
cur = {cur.col + 1, cur.row};
auto& row = grid.back();
row.push_back(name);
}
void Parser::finalize() {
size_t rows = grid.size();
int cell_height = grid_h / rows;
for(auto& row : grid) {
size_t columns = row.size();
int cell_width = grid_w / columns;
dbc::check(cell_width > 0, "invalid cell width calc");
dbc::check(cell_height > 0, "invalid cell height calc");
for(auto& name : row) {
if(name == "_") continue;
auto& cell = cells.at(name);
// ZED: getting a bit hairy but this should work
if(cell.percent) {
// when percent mode we have to take unset to 100%
if(cell.max_w == 0) cell.max_w = 100;
if(cell.max_h == 0) cell.max_h = 100;
cell.max_w *= cell_width * 0.01;
cell.max_h *= cell_height * 0.01;
} else {
if(cell.max_w == 0) cell.max_w = cell_width;
if(cell.max_h == 0) cell.max_h = cell_height;
}
cell.w = cell.expand ? std::min(cell.max_w, grid_w) : std::min(cell_width, cell.max_w);
cell.h = cell.expand ? std::min(cell.max_h, grid_h) : std::min(cell_height, cell.max_h);
dbc::check(cell.h > 0, fmt::format("invalid height cell {}", name));
dbc::check(cell.w > 0, fmt::format("invalid width cell {}", name));
cell.x = grid_x + (cell.col * cell_width);
cell.y = grid_y + (cell.row * cell_height);
// keep the midpoint since it is used a lot
cell.mid_x = std::midpoint(cell.x, cell.x + cell.w);
cell.mid_y = std::midpoint(cell.y, cell.y + cell.h);
// perform alignments
if(cell.right) cell.x += cell_width - cell.w;
if(cell.bottom) cell.y += cell_height - cell.h;
if(cell.center) {
cell.x = cell.mid_x - cell.w / 2;
cell.y = cell.mid_y - cell.h / 2;
}
}
}
}
void Parser::reset() {
cur = {0, 0};
grid.clear();
cells.clear();
}
std::optional<std::string> Parser::hit(int x, int y) {
for(auto& [name, cell] : cells) {
if((x >= cell.x && x <= cell.x + cell.w) &&
(y >= cell.y && y <= cell.y + cell.h)) {
return name;
}
}
return std::nullopt;
}
Cell center(int width, int height, Cell &parent) {
Cell copy = parent;
copy.x = parent.mid_x - width / 2;
copy.y = parent.mid_y - height / 2;
copy.w = width;
copy.h = height;
return copy;
}
}

@ -1,55 +0,0 @@
#pragma once
#include <string>
#include <unordered_map>
#include <functional>
#include <optional>
#include <vector>
namespace lel {
struct Cell {
int x = 0;
int y = 0;
int w = 0;
int h = 0;
int mid_x = 0;
int mid_y = 0;
int max_w = 0;
int max_h = 0;
int col = 0;
int row = 0;
bool right = false;
bool bottom = false;
bool expand = false;
bool center = false;
bool percent = false;
Cell(int col, int row) : col(col), row(row) {}
Cell() {}
};
using Row = std::vector<std::string>;
using CellMap = std::unordered_map<std::string, Cell>;
struct Parser {
int grid_x = 0;
int grid_y = 0;
int grid_w = 0;
int grid_h = 0;
Cell cur;
std::vector<Row> grid;
CellMap cells;
Parser(int x, int y, int width, int height);
Parser();
void position(int x, int y, int width, int height);
void id(std::string name);
void reset();
bool parse(std::string input);
void finalize();
std::optional<std::string> hit(int x, int y);
};
Cell center(int width, int height, Cell &parent);
}

@ -1,261 +0,0 @@
#line 1 "lel_parser.rl"
#include "lel.hpp"
#include <fmt/core.h>
#include <iostream>
namespace lel {
#line 40 "lel_parser.rl"
#line 10 "lel_parser.cpp"
static const char _Parser_actions[] = {
0, 1, 1, 1, 2, 1, 3, 1,
4, 1, 5, 1, 6, 1, 9, 1,
10, 1, 11, 1, 12, 1, 13, 2,
0, 7, 2, 0, 8, 2, 4, 1,
2, 4, 5
};
static const char _Parser_key_offsets[] = {
0, 0, 4, 20, 33, 35, 39, 41,
44, 56, 61
};
static const char _Parser_trans_keys[] = {
32, 91, 9, 13, 32, 37, 40, 42,
46, 61, 94, 95, 9, 13, 60, 62,
65, 90, 97, 122, 37, 40, 42, 46,
61, 94, 95, 60, 62, 65, 90, 97,
122, 48, 57, 41, 44, 48, 57, 48,
57, 41, 48, 57, 32, 93, 95, 124,
9, 13, 48, 57, 65, 90, 97, 122,
32, 93, 124, 9, 13, 32, 91, 9,
13, 0
};
static const char _Parser_single_lengths[] = {
0, 2, 8, 7, 0, 2, 0, 1,
4, 3, 2
};
static const char _Parser_range_lengths[] = {
0, 1, 4, 3, 1, 1, 1, 1,
4, 1, 1
};
static const char _Parser_index_offsets[] = {
0, 0, 4, 17, 28, 30, 34, 36,
39, 48, 53
};
static const char _Parser_indicies[] = {
0, 2, 0, 1, 3, 4, 5, 6,
7, 9, 7, 10, 3, 8, 10, 10,
1, 4, 5, 6, 7, 9, 7, 10,
8, 10, 10, 1, 11, 1, 12, 13,
14, 1, 15, 1, 16, 17, 1, 18,
20, 19, 21, 18, 19, 19, 19, 1,
22, 23, 24, 22, 1, 25, 2, 25,
1, 0
};
static const char _Parser_trans_targs[] = {
1, 0, 2, 2, 3, 4, 3, 3,
3, 3, 8, 5, 3, 6, 5, 7,
3, 7, 9, 8, 10, 2, 9, 10,
2, 10
};
static const char _Parser_trans_actions[] = {
0, 0, 3, 0, 17, 0, 13, 5,
11, 15, 21, 19, 23, 23, 0, 19,
26, 0, 7, 0, 32, 29, 0, 9,
1, 0
};
static const int Parser_start = 1;
static const int Parser_first_final = 10;
static const int Parser_error = 0;
static const int Parser_en_main = 1;
#line 43 "lel_parser.rl"
bool Parser::parse(std::string input) {
reset();
int cs = 0;
const char *start = nullptr;
const char *begin = input.data();
const char *p = input.data();
const char *pe = p + input.size();
std::string tk;
#line 91 "lel_parser.cpp"
{
cs = Parser_start;
}
#line 54 "lel_parser.rl"
#line 94 "lel_parser.cpp"
{
int _klen;
unsigned int _trans;
const char *_acts;
unsigned int _nacts;
const char *_keys;
if ( p == pe )
goto _test_eof;
if ( cs == 0 )
goto _out;
_resume:
_keys = _Parser_trans_keys + _Parser_key_offsets[cs];
_trans = _Parser_index_offsets[cs];
_klen = _Parser_single_lengths[cs];
if ( _klen > 0 ) {
const char *_lower = _keys;
const char *_mid;
const char *_upper = _keys + _klen - 1;
while (1) {
if ( _upper < _lower )
break;
_mid = _lower + ((_upper-_lower) >> 1);
if ( (*p) < *_mid )
_upper = _mid - 1;
else if ( (*p) > *_mid )
_lower = _mid + 1;
else {
_trans += (unsigned int)(_mid - _keys);
goto _match;
}
}
_keys += _klen;
_trans += _klen;
}
_klen = _Parser_range_lengths[cs];
if ( _klen > 0 ) {
const char *_lower = _keys;
const char *_mid;
const char *_upper = _keys + (_klen<<1) - 2;
while (1) {
if ( _upper < _lower )
break;
_mid = _lower + (((_upper-_lower) >> 1) & ~1);
if ( (*p) < _mid[0] )
_upper = _mid - 2;
else if ( (*p) > _mid[1] )
_lower = _mid + 2;
else {
_trans += (unsigned int)((_mid - _keys)>>1);
goto _match;
}
}
_trans += _klen;
}
_match:
_trans = _Parser_indicies[_trans];
cs = _Parser_trans_targs[_trans];
if ( _Parser_trans_actions[_trans] == 0 )
goto _again;
_acts = _Parser_actions + _Parser_trans_actions[_trans];
_nacts = (unsigned int) *_acts++;
while ( _nacts-- > 0 )
{
switch ( *_acts++ )
{
case 0:
#line 11 "lel_parser.rl"
{tk = input.substr(start - begin, p - start); }
break;
case 1:
#line 13 "lel_parser.rl"
{}
break;
case 2:
#line 14 "lel_parser.rl"
{ grid.push_back(Row()); }
break;
case 3:
#line 15 "lel_parser.rl"
{ cur.bottom = (*p) == '.'; }
break;
case 4:
#line 16 "lel_parser.rl"
{ id(input.substr(start - begin, p - start)); }
break;
case 5:
#line 17 "lel_parser.rl"
{ cur.col = 0; cur.row++; }
break;
case 6:
#line 18 "lel_parser.rl"
{ cur.right = (*p) == '>'; }
break;
case 7:
#line 19 "lel_parser.rl"
{cur.max_w = std::stoi(tk); }
break;
case 8:
#line 20 "lel_parser.rl"
{ cur.max_h = std::stoi(tk); }
break;
case 9:
#line 21 "lel_parser.rl"
{ cur.expand = true; }
break;
case 10:
#line 22 "lel_parser.rl"
{ cur.center = true; }
break;
case 11:
#line 23 "lel_parser.rl"
{ cur.percent = true; }
break;
case 12:
#line 33 "lel_parser.rl"
{ start = p; }
break;
case 13:
#line 36 "lel_parser.rl"
{start = p;}
break;
#line 209 "lel_parser.cpp"
}
}
_again:
if ( cs == 0 )
goto _out;
if ( ++p != pe )
goto _resume;
_test_eof: {}
_out: {}
}
#line 55 "lel_parser.rl"
bool good = pe - p == 0;
if(good) {
finalize();
} else {
dbc::log("error at:");
std::cout << p;
}
return good;
}
}

@ -1,66 +0,0 @@
#include "lel.hpp"
#include <fmt/core.h>
#include <iostream>
namespace lel {
%%{
machine Parser;
alphtype char;
action token {tk = input.substr(start - begin, fpc - start); }
action col {}
action ltab { grid.push_back(Row()); }
action valign { cur.bottom = fc == '.'; }
action id { id(input.substr(start - begin, fpc - start)); }
action row { cur.col = 0; cur.row++; }
action align { cur.right = fc == '>'; }
action setwidth {cur.max_w = std::stoi(tk); }
action setheight { cur.max_h = std::stoi(tk); }
action expand { cur.expand = true; }
action center { cur.center = true; }
action percent { cur.percent = true; }
col = "|" $col;
ltab = "[" $ltab;
rtab = "]" $row;
valign = ("^" | ".") $valign;
expand = "*" $expand;
center = "=" $center;
percent = "%" $percent;
halign = ("<" | ">") $align;
number = digit+ >{ start = fpc; } %token;
setw = ("(" number %setwidth ("," number %setheight)? ")") ;
modifiers = (percent | center | expand | valign | halign | setw);
id = modifiers* ((alpha | '_')+ :>> (alnum | '_')*) >{start = fpc;} %id;
row = space* ltab space* id space* (col space* id space*)* space* rtab space*;
main := row+;
}%%
%% write data;
bool Parser::parse(std::string input) {
reset();
int cs = 0;
const char *start = nullptr;
const char *begin = input.data();
const char *p = input.data();
const char *pe = p + input.size();
std::string tk;
%% write init;
%% write exec;
bool good = pe - p == 0;
if(good) {
finalize();
} else {
dbc::log("error at:");
std::cout << p;
}
return good;
}
}

@ -22,32 +22,6 @@ LevelScaling LevelManager::scale_level() {
}; };
} }
void LevelManager::temp_create_player_rituals() {
auto& level = current();
auto player = level.player;
auto& the_belt = level.world->get<combat::RitualBelt>(player);
Config config("assets/config.json");
combat::RitualEngine re("assets/rituals.json");
int slot = 0;
for(auto& settings : config["test_rituals"]) {
if(settings["active"]) {
auto blanket = re.start();
settings.erase("active");
for(auto& el : settings.items()) {
re.set_state(blanket, el.key(), el.value());
}
re.plan(blanket);
auto ritual = re.finalize(blanket);
the_belt.equip(slot, ritual);
slot++;
}
}
}
inline shared_ptr<DinkyECS::World> clone_load_world(shared_ptr<DinkyECS::World> prev_world) inline shared_ptr<DinkyECS::World> clone_load_world(shared_ptr<DinkyECS::World> prev_world)
{ {

@ -7,7 +7,7 @@
#include <memory> #include <memory>
#include "spatialmap.hpp" #include "spatialmap.hpp"
#include "components.hpp" #include "components.hpp"
#include "boss_fight_ui.hpp" #include "gui/boss_fight_ui.hpp"
using std::shared_ptr; using std::shared_ptr;
@ -42,7 +42,5 @@ class LevelManager {
GameLevel &get(size_t index); GameLevel &get(size_t index);
LevelScaling scale_level(); LevelScaling scale_level();
void temp_create_player_rituals();
DinkyECS::Entity spawn_enemy(std::string named); DinkyECS::Entity spawn_enemy(std::string named);
}; };

@ -1,4 +1,4 @@
#include "gui_fsm.hpp" #include "gui/fsm.hpp"
#include "textures.hpp" #include "textures.hpp"
#include "sound.hpp" #include "sound.hpp"
#include "autowalker.hpp" #include "autowalker.hpp"
@ -6,14 +6,14 @@
#include "animation.hpp" #include "animation.hpp"
#include <iostream> #include <iostream>
#include "shaders.hpp" #include "shaders.hpp"
#include "backend.hpp"
int main(int argc, char* argv[]) { int main(int argc, char* argv[]) {
try { try {
textures::init(); sfml::Backend backend;
sound::init(); guecs::init(&backend);
ai::init("assets/ai.json"); ai::init("assets/ai.json");
animation::init(); animation::init();
shaders::init();
if(DEBUG_BUILD) sound::mute(true); if(DEBUG_BUILD) sound::mute(true);
@ -33,6 +33,7 @@ int main(int argc, char* argv[]) {
// ZED: need to sort out how to deal with this in the FSM // ZED: need to sort out how to deal with this in the FSM
if(main.in_state(gui::State::IDLE) if(main.in_state(gui::State::IDLE)
|| main.in_state(gui::State::NEXT_LEVEL) || main.in_state(gui::State::NEXT_LEVEL)
|| main.in_state(gui::State::LOOTING)
|| main.in_state(gui::State::IN_COMBAT)) || main.in_state(gui::State::IN_COMBAT))
{ {
if(main.autowalking) { if(main.autowalking) {

@ -24,7 +24,7 @@ if build_machine.system() == 'windows'
language: 'cpp', language: 'cpp',
) )
sfml_main = dependency('sfml_main') sfml_main = subproject('sfml').get_variable('sfml_main_dep')
opengl32 = cc.find_library('opengl32', required: true) opengl32 = cc.find_library('opengl32', required: true)
winmm = cc.find_library('winmm', required: true) winmm = cc.find_library('winmm', required: true)
gdi32 = cc.find_library('gdi32', required: true) gdi32 = cc.find_library('gdi32', required: true)
@ -68,13 +68,14 @@ sfml_graphics = subproject('sfml').get_variable('sfml_graphics_dep')
sfml_network = subproject('sfml').get_variable('sfml_network_dep') sfml_network = subproject('sfml').get_variable('sfml_network_dep')
sfml_system = subproject('sfml').get_variable('sfml_system_dep') sfml_system = subproject('sfml').get_variable('sfml_system_dep')
sfml_window = subproject('sfml').get_variable('sfml_window_dep') sfml_window = subproject('sfml').get_variable('sfml_window_dep')
lel_guecs = subproject('lel-guecs').get_variable('lel_guecs_dep')
dependencies += [ dependencies += [
fmt, json, freetype2, fmt, json, freetype2,
flac, ogg, vorbis, vorbisfile, vorbisenc, flac, ogg, vorbis, vorbisfile, vorbisenc,
sfml_audio, sfml_graphics, sfml_audio, sfml_graphics,
sfml_network, sfml_system, sfml_network, sfml_system,
sfml_window sfml_window, lel_guecs
] ]
sources = [ sources = [
@ -83,34 +84,35 @@ sources = [
'animation.cpp', 'animation.cpp',
'animation.cpp', 'animation.cpp',
'autowalker.cpp', 'autowalker.cpp',
'backend.cpp',
'battle.cpp', 'battle.cpp',
'boss_fight_ui.cpp',
'camera.cpp', 'camera.cpp',
'combat.cpp', 'combat.cpp',
'combat_ui.cpp',
'components.cpp', 'components.cpp',
'config.cpp', 'config.cpp',
'dbc.cpp', 'dbc.cpp',
'debug_ui.cpp',
'devices.cpp', 'devices.cpp',
'goap.cpp', 'goap.cpp',
'guecs.cpp', 'gui/boss_fight_ui.cpp',
'gui_fsm.cpp', 'gui/combat_ui.cpp',
'gui/debug_ui.cpp',
'gui/fsm.cpp',
'gui/guecstra.cpp',
'gui/loot_ui.cpp',
'gui/main_ui.cpp',
'gui/map_view.cpp',
'gui/mini_map.cpp',
'gui/overlay_ui.cpp',
'gui/ritual_ui.cpp',
'gui/status_ui.cpp',
'inventory.cpp', 'inventory.cpp',
'lel.cpp',
'levelmanager.cpp', 'levelmanager.cpp',
'lights.cpp', 'lights.cpp',
'main_ui.cpp',
'map.cpp', 'map.cpp',
'map_view.cpp',
'matrix.cpp', 'matrix.cpp',
'matrix.cpp',
'mini_map.cpp',
'overlay_ui.cpp',
'pathing.cpp', 'pathing.cpp',
'rand.cpp', 'rand.cpp',
'raycaster.cpp', 'raycaster.cpp',
'ritual_ui.cpp',
'rituals.cpp', 'rituals.cpp',
'save.cpp', 'save.cpp',
'shaders.cpp', 'shaders.cpp',
@ -118,7 +120,6 @@ sources = [
'sound.cpp', 'sound.cpp',
'spatialmap.cpp', 'spatialmap.cpp',
'stats.cpp', 'stats.cpp',
'status_ui.cpp',
'systems.cpp', 'systems.cpp',
'textures.cpp', 'textures.cpp',
'tilemap.cpp', 'tilemap.cpp',
@ -136,9 +137,7 @@ executable('runtests', sources + [
'tests/dinkyecs.cpp', 'tests/dinkyecs.cpp',
'tests/easings.cpp', 'tests/easings.cpp',
'tests/fsm.cpp', 'tests/fsm.cpp',
'tests/guecs.cpp',
'tests/inventory.cpp', 'tests/inventory.cpp',
'tests/lel.cpp',
'tests/levelmanager.cpp', 'tests/levelmanager.cpp',
'tests/lighting.cpp', 'tests/lighting.cpp',
'tests/map.cpp', 'tests/map.cpp',
@ -166,7 +165,8 @@ executable('zedcaster',
dependencies: dependencies) dependencies: dependencies)
executable('fragviewer', executable('fragviewer',
sources + [ 'tools/fragviewer.cpp' ], [ 'textures.cpp', 'config.cpp',
'dbc.cpp', 'tools/fragviewer.cpp' ],
cpp_args: cpp_args, cpp_args: cpp_args,
link_args: link_args, link_args: link_args,
override_options: exe_defaults, override_options: exe_defaults,

@ -10,6 +10,7 @@
#include "components.hpp" #include "components.hpp"
#include "textures.hpp" #include "textures.hpp"
#include "systems.hpp" #include "systems.hpp"
#include "shaders.hpp"
using namespace fmt; using namespace fmt;
using std::make_unique; using std::make_unique;
@ -187,6 +188,7 @@ void Raycaster::sprite_casting(sf::RenderTarget &target) {
apply_sprite_effect(effect, sprite_width, sprite_height); apply_sprite_effect(effect, sprite_width, sprite_height);
} else { } else {
effect = $brightness; effect = $brightness;
level += (aiming_at == rec.second) * 0.1;
effect->setUniform("darkness", level); effect->setUniform("darkness", level);
} }

@ -1,156 +0,0 @@
#include "ritual_ui.hpp"
#include "components.hpp"
#include "guecs.hpp"
#include "rand.hpp"
#include "animation.hpp"
#include "rand.hpp"
namespace gui {
using namespace guecs;
using std::any, std::any_cast, std::string, std::make_any;
RitualUI::RitualUI(GameLevel level) :
$level(level)
{
$gui.position(STATUS_UI_X, STATUS_UI_Y, STATUS_UI_WIDTH, STATUS_UI_HEIGHT);
$gui.layout(
"[_]"
"[inv_slot9 | inv_slot10 | inv_slot11| inv_slot12]"
"[inv_slot13 | inv_slot14 | inv_slot15| inv_slot16]"
"[inv_slot17 | inv_slot18 | inv_slot19| inv_slot20]"
"[inv_slot21 | inv_slot22 | inv_slot23| inv_slot24]"
"[*%(100,600)circle_area]"
"[_]"
"[_]"
"[_]"
"[_]"
"[_]"
"[ ritual_ui ]");
}
void RitualUI::init() {
std::vector<std::string> junk_list{
{"chess_pawn"},
{"dirty_kerchief"},
{"mushroom"},
{"pocket_watch"},
{"rusty_nails"},
{"severed_finger"}
};
for(auto& [name, cell] : $gui.cells()) {
auto button = $gui.entity(name);
if(name == "circle_area") {
$gui.set<Effect>(button, {0.4f});
$gui.set<Sprite>(button, {"the_ritual_circle"});
$gui.set<Clickable>(button, {
[&](auto ent, auto){ ritual_circle_clicked(ent); }
});
} else if(name.starts_with("inv_slot")) {
$gui.set<Sprite>(button, {
fmt::format("{}-64", junk_list[button % junk_list.size()])});
$gui.set<Effect>(button, {0.4f});
$gui.set<Sound>(button, {"ui_click"});
$gui.set<Clickable>(button, {
[&](auto ent, auto){ inv_slot_clicked(ent); }
});
} else if(name == "ritual_ui") {
$gui.set<Clickable>(button, {
[&](auto, auto){ toggle(); }
});
$gui.set<Sound>(button, {"pickup"});
}
}
$ritual_ui = textures::get("ritual_crafting_area");
$ritual_ui.sprite->setPosition({0,0});
$ritual_ui.sprite->setTextureRect($ritual_closed_rect);
$ritual_state = RitualUIState::CLOSED;
$ritual_anim = animation::load("ritual_blanket");
$gui.init();
}
bool RitualUI::is_open() {
return $ritual_state != RitualUIState::CLOSED;
}
void RitualUI::inv_slot_clicked(DinkyECS::Entity ent) {
if($gui.has<Sprite>(ent)) {
auto& bs = $gui.get<Sprite>(ent);
auto ritual_circle = $gui.entity("circle_area");
auto& ritual_cell = $gui.cell_for(ritual_circle);
dbc::log(fmt::format("inv_slot clicked {}", bs.name));
int inner_x = ritual_cell.x + ritual_cell.x / 2;
int inner_y = ritual_cell.y + ritual_cell.y / 2;
float x = Random::uniform(inner_x, inner_x + ritual_cell.w / 2);
float y = Random::uniform(inner_y, inner_y + ritual_cell.h / 2);
bs.sprite->setPosition({float(x), float(y)});
}
}
void RitualUI::reset_inv_positions() {
auto ritual_circle = $gui.entity("circle_area");
$gui.world().query<lel::Cell, Sprite>(
[&](const auto ent, auto &cell, auto &bs) {
if(ent == ritual_circle) {
bs.sprite->setColor({255,255,255,255});
bs.sprite->setRotation(sf::degrees(0.0));
} else {
bs.sprite->setPosition({(float)cell.x, (float)cell.y});
}
});
}
void RitualUI::ritual_circle_clicked(DinkyECS::Entity ent) {
auto cell = $gui.cell_for(ent);
auto& bs = $gui.get<Sprite>(ent);
bs.sprite->setColor({200, 0, 0});
animation::center(*bs.sprite, {(float)cell.x, (float)cell.y});
animation::rotate(*bs.sprite, 20.0);
}
bool RitualUI::mouse(float x, float y, bool hover) {
return $gui.mouse(x, y, hover);
}
void RitualUI::toggle() {
using enum RitualUIState;
if($ritual_state == OPEN) {
$ritual_state = CLOSING;
} else if($ritual_state == CLOSED) {
$ritual_state = OPENING;
$ritual_anim.play();
}
}
/* WARNING: This is really not the greatest way to do this.
* look in status_ui.update_level()
* */
void RitualUI::update() {
dbc::log("RITUAL UPDATE NOT IMPLEMENTED");
}
void RitualUI::render(sf::RenderWindow &window) {
using enum RitualUIState;
if($ritual_state == OPENING) {
if(!animation::apply($ritual_anim, $ritual_ui)) {
$ritual_state = OPEN;
}
} else if($ritual_state == CLOSING) {
reset_inv_positions();
$ritual_ui.sprite->setTextureRect($ritual_closed_rect);
$ritual_state = CLOSED;
}
window.draw(*$ritual_ui.sprite);
if($ritual_state == OPEN) $gui.render(window);
}
}

@ -1,38 +0,0 @@
#pragma once
#include "levelmanager.hpp"
#include "constants.hpp"
#include <deque>
#include "textures.hpp"
#include "guecs.hpp"
namespace gui {
enum class RitualUIState {
OPEN=0,
CLOSED=1,
OPENING=2,
CLOSING=3
};
class RitualUI {
public:
sf::IntRect $ritual_closed_rect{{0,0},{380,720}};
sf::IntRect $ritual_open_rect{{380 * 2,0},{380,720}};
RitualUIState $ritual_state = RitualUIState::CLOSED;
textures::SpriteTexture $ritual_ui;
components::Animation $ritual_anim;
guecs::UI $gui;
GameLevel $level;
RitualUI(GameLevel level);
bool mouse(float x, float y, bool hover);
void toggle();
bool is_open();
void init();
void render(sf::RenderWindow &window);
void update();
void ritual_circle_clicked(DinkyECS::Entity ent);
void inv_slot_clicked(DinkyECS::Entity ent);
void reset_inv_positions();
};
}

@ -2,8 +2,8 @@
#include "ai_debug.hpp" #include "ai_debug.hpp"
#include "ai.hpp" #include "ai.hpp"
namespace combat { namespace ritual {
RitualEngine::RitualEngine(std::string config_path) : Engine::Engine(std::string config_path) :
$config(config_path) $config(config_path)
{ {
$profile = $config["profile"]; $profile = $config["profile"];
@ -31,21 +31,34 @@ namespace combat {
} }
} }
ai::State RitualEngine::load_state(std::string name) { ai::State Engine::load_state(std::string name) {
return $states.at(name); return $states.at(name);
} }
ai::Action RitualEngine::load_action(std::string name) { void Engine::load_junk(CraftingState& ritual, const JunkItem& item) {
Config config("assets/rituals.json");
auto& junk = config["junk"];
auto& item_desc = junk[item];
fmt::print("Item {} provides: ", item);
for(auto& effect : item_desc["provides"]) {
fmt::print("{} ", (std::string)effect);
set_state(ritual, effect, true);
}
fmt::print("\n");
}
ai::Action Engine::load_action(std::string name) {
return $actions.at(name); return $actions.at(name);
} }
RitualAI RitualEngine::start() { CraftingState Engine::start() {
auto start = load_state("initial"); auto start = load_state("initial");
auto goal = load_state("final"); auto goal = load_state("final");
return {"actions", start, goal}; return {"actions", start, goal};
} }
void RitualEngine::set_state(RitualAI& ritual, std::string name, bool setting) { void Engine::set_state(CraftingState& ritual, std::string name, bool setting) {
dbc::check($profile.contains(name), dbc::check($profile.contains(name),
fmt::format("ritual action named {} is not in profile, look in {} config", fmt::format("ritual action named {} is not in profile, look in {} config",
name, $config.$src_path)); name, $config.$src_path));
@ -53,41 +66,45 @@ namespace combat {
ritual.start.set($profile.at(name), setting); ritual.start.set($profile.at(name), setting);
} }
void RitualEngine::reset(RitualAI& ritual) { void CraftingState::reset() {
ritual.start = ritual.original; start = original;
plan.complete = false;
plan.script.clear();
} }
void RitualEngine::plan(RitualAI& ritual) { void Engine::plan(CraftingState& ritual) {
ritual.plan = ai::plan_actions($scripts.at(ritual.script), ritual.start, ritual.goal); ritual.plan = ai::plan_actions($scripts.at(ritual.script), ritual.start, ritual.goal);
} }
bool RitualAI::will_do(std::string name) { bool CraftingState::will_do(std::string name) {
if(plan.script.size() == 0) return false; if(plan.script.size() == 0) return false;
return plan.script[0].name == name; return plan.script[0].name == name;
} }
ai::Action RitualAI::pop() { ai::Action CraftingState::pop() {
auto result = plan.script.front(); auto result = plan.script.front();
plan.script.pop_front(); plan.script.pop_front();
return result; return result;
} }
// BUG: maybe this should be called RitualBlanket instead? // BUG: maybe this should be called CraftingState instead?
void RitualAI::dump() { void CraftingState::dump() {
ai::dump_script(script, start, plan.script); ai::dump_script(script, start, plan.script);
} }
bool RitualAI::is_combined() { bool CraftingState::is_combined() {
dbc::check(!plan.script.empty(), "you are attempting to check an empty plan"); // it's only combined if it has > 1 and last is combined
if(plan.script.size() <= 1) return false;
auto& last = plan.script.back(); auto& last = plan.script.back();
return plan.script.size() > 1 && last.name == "combined"; return last.name == "combined";
} }
RitualAction RitualEngine::finalize(RitualAI& ritual) { Action Engine::finalize(CraftingState& ritual) {
(void)ritual; (void)ritual;
RitualAction result; Action result;
auto effects = $config["effects"]; auto effects = $config["effects"];
for(auto action : ritual.plan.script) { for(auto action : ritual.plan.script) {
@ -95,15 +112,15 @@ namespace combat {
auto& effect = effects[action.name]; auto& effect = effects[action.name];
result.damage += int(effect["damage"]); result.damage += int(effect["damage"]);
result.probability *= float(effect["probability"]); result.probability *= float(effect["probability"]);
if(effect.contains("kind")) result.kind = RitualKind(int(effect["kind"])); if(effect.contains("kind")) result.kind = Kind(int(effect["kind"]));
if(effect.contains("element")) result.element = RitualElement(int(effect["element"])); if(effect.contains("element")) result.element = Element(int(effect["element"]));
} }
} }
return result; return result;
} }
void RitualAction::dump() { void Action::dump() {
fmt::print("ritual has damage {}, prob: {}, kind: {}, element: {}; named: ", fmt::print("ritual has damage {}, prob: {}, kind: {}, element: {}; named: ",
damage, probability, int(kind), int(element)); damage, probability, int(kind), int(element));
@ -114,19 +131,71 @@ namespace combat {
fmt::println("\n"); fmt::println("\n");
} }
RitualAction& RitualBelt::get(int index) { Action& Belt::get(int index) {
return equipped.at(index); return equipped.at(index);
} }
void RitualBelt::equip(int index, RitualAction& action) { void Belt::equip(int index, Action& action) {
equipped.insert_or_assign(index, action); equipped.insert_or_assign(index, action);
} }
bool RitualBelt::has(int index) { bool Belt::has(int index) {
return equipped.contains(index); return equipped.contains(index);
} }
void RitualBelt::unequip(int index) { void Belt::unequip(int index) {
equipped.erase(index); equipped.erase(index);
} }
int Belt::next() {
int slot = next_slot % max_slots;
next_slot++;
return slot;
}
DinkyECS::Entity Blanket::add(JunkItem name) {
DinkyECS::Entity id = ++entity_counter;
contents.insert_or_assign(id, name);
return id;
}
std::string& Blanket::get(DinkyECS::Entity ent) {
return contents.at(ent);
}
bool Blanket::has(DinkyECS::Entity ent) {
return contents.contains(ent);
}
void Blanket::remove(DinkyECS::Entity ent) {
contents.erase(ent);
}
void Blanket::select(DinkyECS::Entity ent) {
selected.insert_or_assign(ent, true);
}
void Blanket::deselect(DinkyECS::Entity ent) {
selected.erase(ent);
}
bool Blanket::is_selected(DinkyECS::Entity ent) {
return selected.contains(ent) && selected.at(ent);
}
void Blanket::reset() {
selected.clear();
}
bool Blanket::no_selections() {
return selected.size() == 0;
}
void Blanket::consume_crafting() {
for(auto [item_id, setting] : selected) {
contents.erase(item_id);
}
}
} }

@ -3,71 +3,97 @@
#include "ai.hpp" #include "ai.hpp"
#include "config.hpp" #include "config.hpp"
#include "dinkyecs.hpp" #include "dinkyecs.hpp"
#include "components.hpp"
namespace combat { namespace ritual {
struct RitualAI { using JunkItem = std::string;
struct JunkPile {
std::vector<JunkItem> contents;
};
enum class Element {
NONE=0, FIRE=1, LIGHTNING=2
};
enum class Kind {
NONE=0, PHYSICAL=1, MAGICK=2
};
struct CraftingState {
std::string script; std::string script;
ai::State start; ai::State start;
ai::State original; ai::State original;
ai::State goal; ai::State goal;
ai::ActionPlan plan; ai::ActionPlan plan;
RitualAI(std::string script, ai::State start, ai::State goal) : CraftingState(std::string script, ai::State start, ai::State goal) :
script(script), start(start), original(start), goal(goal) script(script), start(start), original(start), goal(goal)
{ {
} }
RitualAI() {}; CraftingState() {};
bool will_do(std::string name); bool will_do(std::string name);
void dump(); void dump();
ai::Action pop(); ai::Action pop();
bool is_combined(); bool is_combined();
void reset();
}; };
enum class RitualElement { struct Action {
NONE=0, FIRE=1, LIGHTNING=2
};
enum class RitualKind {
NONE=0, PHYSICAL=1, MAGICK=2
};
struct RitualAction {
float probability = 1.0f; float probability = 1.0f;
int damage = 0; int damage = 0;
RitualKind kind{RitualKind::NONE}; Kind kind{Kind::NONE};
RitualElement element{RitualElement::NONE}; Element element{Element::NONE};
std::vector<std::string> names; std::vector<std::string> names;
void dump(); void dump();
}; };
struct RitualEngine { struct Engine {
Config $config; Config $config;
ai::AIProfile $profile; ai::AIProfile $profile;
std::unordered_map<std::string, ai::Action> $actions; std::unordered_map<std::string, ai::Action> $actions;
std::unordered_map<std::string, ai::State> $states; std::unordered_map<std::string, ai::State> $states;
std::unordered_map<std::string, std::vector<ai::Action>> $scripts; std::unordered_map<std::string, std::vector<ai::Action>> $scripts;
RitualEngine(std::string config_path); Engine(std::string config_path="assets/rituals.json");
ai::State load_state(std::string name); ai::State load_state(std::string name);
ai::Action load_action(std::string name); ai::Action load_action(std::string name);
RitualAI start(); CraftingState start();
void reset(RitualAI& ritual); void set_state(CraftingState& ritual, std::string name, bool setting);
void set_state(RitualAI& ritual, std::string name, bool setting); void plan(CraftingState& ritual);
void plan(RitualAI& ritual); Action finalize(CraftingState& ritual);
RitualAction finalize(RitualAI& ritual); void load_junk(CraftingState& ritual, const JunkItem& item);
}; };
struct RitualBelt { struct Belt {
std::unordered_map<int, RitualAction> equipped; int next_slot = 0;
int max_slots = 6;
std::unordered_map<int, Action> equipped;
RitualAction& get(int index); Action& get(int index);
void equip(int index, RitualAction& action); void equip(int index, Action& action);
bool has(int index); bool has(int index);
void unequip(int index); void unequip(int index);
int next();
};
struct Blanket {
size_t entity_counter = 0;
std::unordered_map<DinkyECS::Entity, JunkItem> contents;
std::unordered_map<DinkyECS::Entity, bool> selected;
DinkyECS::Entity add(JunkItem name);
JunkItem& get(DinkyECS::Entity ent);
bool has(DinkyECS::Entity ent);
void remove(DinkyECS::Entity ent);
void select(DinkyECS::Entity ent);
void deselect(DinkyECS::Entity ent);
void reset();
bool is_selected(DinkyECS::Entity ent);
bool no_selections();
void consume_crafting();
}; };
} }

@ -140,6 +140,29 @@ void System::motion(GameLevel &level) {
}); });
} }
void System::distribute_loot(DinkyECS::World &world, DinkyECS::Entity& ent, nlohmann::json& entity_data) {
int inventory_count = entity_data["inventory_count"];
world.set<InventoryItem>(ent, {inventory_count, entity_data});
// use the inventory_level to fill the blanket with new items
Config config("assets/rituals.json");
ritual::JunkPile pile;
auto& junk = config["junk"];
ritual::JunkPile select_from;
for(auto& el : junk.items()) {
select_from.contents.push_back(el.key());
}
for(int i = 0; i < inventory_count; i++) {
size_t max_junk = select_from.contents.size();
auto& item = select_from.contents.at(Random::uniform(size_t(0), max_junk-1));
pile.contents.push_back(item);
}
world.set<ritual::JunkPile>(ent, pile);
}
void System::death(GameLevel &level, components::ComponentMap& components) { void System::death(GameLevel &level, components::ComponentMap& components) {
auto &world = *level.world; auto &world = *level.world;
auto player = world.get_the<Player>(); auto player = world.get_the<Player>();
@ -187,8 +210,7 @@ void System::death(GameLevel &level, components::ComponentMap& components) {
auto entity_data = config.items["GRAVE_STONE"]; auto entity_data = config.items["GRAVE_STONE"];
components::configure_entity(components, world, ent, entity_data["components"]); components::configure_entity(components, world, ent, entity_data["components"]);
if(entity_data["inventory_count"] > 0) { if(entity_data["inventory_count"] > 0) {
// right here use a std::any that's already converted instead? System::distribute_loot(world, ent, entity_data);
world.set<InventoryItem>(ent, {entity_data["inventory_count"], entity_data});
} }
} }
} }
@ -208,10 +230,9 @@ void System::combat(GameLevel &level, int attack_id) {
auto &collider = *level.collision; auto &collider = *level.collision;
auto &world = *level.world; auto &world = *level.world;
auto player = world.get_the<Player>(); auto player = world.get_the<Player>();
auto& the_belt = world.get<combat::RitualBelt>(player.entity); auto& the_belt = world.get_the<ritual::Belt>();
dbc::check(the_belt.has(attack_id), if(!the_belt.has(attack_id)) return;
fmt::format("the_belt does not have an attack with id={}", attack_id));
auto& ritual = the_belt.get(attack_id); auto& ritual = the_belt.get(attack_id);
const auto& player_position = world.get<Position>(player.entity); const auto& player_position = world.get<Position>(player.entity);
@ -243,7 +264,7 @@ void System::combat(GameLevel &level, int attack_id) {
}; };
if(result.player_did > 0) { if(result.player_did > 0) {
using enum combat::RitualElement; using enum ritual::Element;
if(ritual.element == FIRE || ritual.element == LIGHTNING) { if(ritual.element == FIRE || ritual.element == LIGHTNING) {
auto effect = shaders::get( auto effect = shaders::get(
@ -293,17 +314,20 @@ void System::collision(GameLevel &level) {
world.remove<LightSource>(entity); world.remove<LightSource>(entity);
} }
if(world.has<ritual::JunkPile>(entity)) {
auto& pile = world.get<ritual::JunkPile>(entity);
auto& blanket = world.get_the<ritual::Blanket>();
for(auto& junk : pile.contents) {
fmt::println("adding {} to blanket", junk);
blanket.add(junk);
}
}
if(world.has<Weapon>(entity)) { if(world.has<Weapon>(entity)) {
inventory.add(item); inventory.add(item);
world.remove<Weapon>(entity); world.remove<Weapon>(entity);
} }
if(world.has<Loot>(entity)) {
auto &loot = world.get<Loot>(entity);
inventory.gold += loot.amount;
world.remove<Loot>(entity);
}
if(world.has<Curative>(entity)) { if(world.has<Curative>(entity)) {
inventory.add(item); inventory.add(item);
world.remove<Curative>(entity); world.remove<Curative>(entity);
@ -408,6 +432,22 @@ std::wstring System::draw_map(GameLevel level, size_t view_x, size_t view_y, int
return result; return result;
} }
void System::player_status(GameLevel &level) {
auto& combat = level.world->get<Combat>(level.player);
float percent = float(combat.hp) / float(combat.max_hp);
if(percent > 0.8) {
sound::play("hp_status_80");
} else if(percent > 0.6) {
sound::play("hp_status_60");
} else if(percent > 0.3) {
sound::play("hp_status_30");
} else if(percent > 0.1) {
sound::play("hp_status_10");
} else {
sound::play("hp_status_00");
}
}
std::shared_ptr<sf::Shader> System::sprite_effect(GameLevel &level, DinkyECS::Entity entity) { std::shared_ptr<sf::Shader> System::sprite_effect(GameLevel &level, DinkyECS::Entity entity) {
if(level.world->has<SpriteEffect>(entity)) { if(level.world->has<SpriteEffect>(entity)) {

@ -23,5 +23,7 @@ namespace System {
void combat(GameLevel &level, int attack_id); void combat(GameLevel &level, int attack_id);
std::shared_ptr<sf::Shader> sprite_effect(GameLevel &level, DinkyECS::Entity entity); std::shared_ptr<sf::Shader> sprite_effect(GameLevel &level, DinkyECS::Entity entity);
void player_status(GameLevel &level);
void distribute_loot(DinkyECS::World &world, DinkyECS::Entity& ent, nlohmann::json& entity_data);
} }

@ -2,7 +2,7 @@
#include <iostream> #include <iostream>
#include "rituals.hpp" #include "rituals.hpp"
#include "battle.hpp" #include "battle.hpp"
#include "fsm.hpp" #include "simplefsm.hpp"
#include "dinkyecs.hpp" #include "dinkyecs.hpp"
using namespace combat; using namespace combat;

@ -1,7 +1,7 @@
#include <catch2/catch_test_macros.hpp> #include <catch2/catch_test_macros.hpp>
#include <fmt/core.h> #include <fmt/core.h>
#include <string> #include <string>
#include "../fsm.hpp" #include "simplefsm.hpp"
using namespace fmt; using namespace fmt;
using std::string; using std::string;

@ -1,33 +0,0 @@
#include <catch2/catch_test_macros.hpp>
#include <fmt/core.h>
#include "constants.hpp"
#include "guecs.hpp"
#include "textures.hpp"
#include <fmt/xchar.h>
using namespace guecs;
TEST_CASE("prototype one gui", "[ecs-gui]") {
guecs::UI gui;
textures::init();
gui.position(0, 0, 1000, 500);
gui.layout("[test1|test2|test3][test4|_|test5]");
for(auto& [name, cell] : gui.cells()) {
auto& world = gui.world();
auto button = gui.entity(name);
world.set<lel::Cell>(button, cell);
world.set<Rectangle>(button, {});
world.set<Clickable>(button, {});
world.set<Textual>(button, {L"whatever"});
}
gui.init();
// at this point it's mostly ready but I'd need to render it to a window real quick
sf::RenderWindow window;
window.setSize({SCREEN_WIDTH, SCREEN_HEIGHT});
gui.render(window);
window.display();
}

@ -1,52 +0,0 @@
#include "lel.hpp"
#include <catch2/catch_test_macros.hpp>
#include <fmt/core.h>
#include <string>
TEST_CASE("test basic ops", "[lel]") {
lel::Parser parser(0, 0, 500, 500);
bool good = parser.parse(
"[ label_1 | label3 | test1]"
"[ *(300,300)text1 | %(150)people | ^test2]"
"[ >label2 | _ | .test3]"
"[ =message | buttons | test4]");
REQUIRE(good);
for(size_t rowcount = 0; rowcount < parser.grid.size(); rowcount++) {
auto& row = parser.grid[rowcount];
for(size_t colcount = 0; colcount < row.size(); colcount++) {
auto &name = row[colcount];
if(name == "_") {
REQUIRE(!parser.cells.contains(name));
} else {
auto &cell = parser.cells.at(name);
REQUIRE(cell.row == int(rowcount));
REQUIRE(cell.col == int(colcount));
}
}
}
REQUIRE(parser.cells.size() == 11);
REQUIRE(parser.cells.at("label2").right == true);
REQUIRE(parser.cells.at("text1").expand == true);
REQUIRE(parser.cells.at("text1").w == 300);
REQUIRE(parser.cells.at("text1").h == 300);
REQUIRE(parser.cells.at("people").expand == false);
REQUIRE(parser.cells.at("message").expand == false);
REQUIRE(parser.cells.at("message").center == true);
for(auto& [name, cell] : parser.cells) {
REQUIRE(cell.w > 0);
REQUIRE(cell.h > 0);
}
auto hit = parser.hit(10, 10);
REQUIRE(*hit == "label_1");
auto nohit = parser.hit(1000, 1000);
REQUIRE(!nohit);
REQUIRE(nohit == std::nullopt);
}

@ -1,87 +1,88 @@
#include <catch2/catch_test_macros.hpp> #include <catch2/catch_test_macros.hpp>
#include <iostream> #include <iostream>
#include "rituals.hpp" #include "rituals.hpp"
#include "fsm.hpp" #include "simplefsm.hpp"
#include "dinkyecs.hpp" #include "dinkyecs.hpp"
#include "levelmanager.hpp" #include "levelmanager.hpp"
#include "ai_debug.hpp"
using namespace combat; TEST_CASE("ritual::Engine basic tests", "[rituals]") {
ritual::Engine re("assets/rituals.json");
auto craft_state = re.start();
TEST_CASE("RitualEngine basic tests", "[rituals]") { re.set_state(craft_state, "has_spikes", true);
RitualEngine re("assets/rituals.json"); re.plan(craft_state);
auto blanket = re.start();
re.set_state(blanket, "has_spikes", true);
re.plan(blanket);
fmt::println("\n\n------------ TEST WILL DO PIERCE"); fmt::println("\n\n------------ TEST WILL DO PIERCE");
blanket.dump(); craft_state.dump();
REQUIRE(blanket.will_do("pierce_type")); REQUIRE(craft_state.will_do("pierce_type"));
REQUIRE(blanket.start != blanket.original); REQUIRE(craft_state.start != craft_state.original);
re.reset(blanket); craft_state.reset();
REQUIRE(blanket.start == blanket.original); REQUIRE(craft_state.start == craft_state.original);
re.set_state(blanket, "has_magick", true); re.set_state(craft_state, "has_magick", true);
re.set_state(blanket, "has_spikes", true); re.set_state(craft_state, "has_spikes", true);
re.plan(blanket); re.plan(craft_state);
fmt::println("\n\n------------ TEST WILL DO MAGICK TOO"); fmt::println("\n\n------------ TEST WILL DO MAGICK TOO");
blanket.dump(); craft_state.dump();
REQUIRE(blanket.will_do("pierce_type")); REQUIRE(craft_state.will_do("pierce_type"));
blanket.pop(); craft_state.pop();
REQUIRE(blanket.will_do("magick_type")); REQUIRE(craft_state.will_do("magick_type"));
re.reset(blanket); craft_state.reset();
re.set_state(blanket, "has_magick", true); re.set_state(craft_state, "has_magick", true);
re.set_state(blanket, "has_spikes", true); re.set_state(craft_state, "has_spikes", true);
re.set_state(blanket, "shiny_bauble", true); re.set_state(craft_state, "shiny_bauble", true);
REQUIRE(blanket.is_combined()); re.plan(craft_state);
re.plan(blanket);
REQUIRE(craft_state.is_combined());
fmt::println("\n\n------------ TEST WILL DO DAMAGE BOOST"); fmt::println("\n\n------------ TEST WILL DO DAMAGE BOOST");
blanket.dump(); craft_state.dump();
re.reset(blanket); craft_state.reset();
re.set_state(blanket, "has_magick", true); re.set_state(craft_state, "has_magick", true);
re.set_state(blanket, "cursed_item", true); re.set_state(craft_state, "cursed_item", true);
re.set_state(blanket, "shiny_bauble", true); re.set_state(craft_state, "shiny_bauble", true);
REQUIRE(blanket.is_combined()); re.plan(craft_state);
re.plan(blanket); REQUIRE(craft_state.is_combined());
fmt::println("\n\n------------ TEST WILL DO LARGE DAMAGE BOOST"); fmt::println("\n\n------------ TEST WILL DO LARGE DAMAGE BOOST");
blanket.dump(); craft_state.dump();
} }
TEST_CASE("blanket can be finalized for the end result", "[rituals]") { TEST_CASE("craft_state can be finalized for the end result", "[rituals]") {
RitualEngine re("assets/rituals.json"); ritual::Engine re("assets/rituals.json");
auto blanket = re.start(); auto craft_state = re.start();
re.set_state(blanket, "has_magick", true); re.set_state(craft_state, "has_magick", true);
re.set_state(blanket, "cursed_item", true); re.set_state(craft_state, "cursed_item", true);
re.set_state(blanket, "shiny_bauble", true); re.set_state(craft_state, "shiny_bauble", true);
re.plan(blanket); re.plan(craft_state);
REQUIRE(blanket.is_combined()); REQUIRE(craft_state.is_combined());
fmt::println("\n\n------------ CYCLES AVOIDED"); fmt::println("\n\n------------ CYCLES AVOIDED");
blanket.dump(); craft_state.dump();
auto ritual = re.finalize(blanket); auto ritual = re.finalize(craft_state);
ritual.dump(); ritual.dump();
} }
TEST_CASE("the ritual belt works", "[rituals-belt]") { TEST_CASE("the ritual belt works", "[rituals]") {
RitualBelt the_belt; ritual::Belt the_belt;
RitualEngine re("assets/rituals.json"); ritual::Engine re;
auto blanket = re.start(); auto craft_state = re.start();
re.set_state(blanket, "has_magick", true); re.set_state(craft_state, "has_magick", true);
re.plan(blanket); re.plan(craft_state);
REQUIRE(blanket.is_combined()); REQUIRE(craft_state.is_combined());
blanket.dump(); craft_state.dump();
{ {
auto ritual = re.finalize(blanket); auto ritual = re.finalize(craft_state);
the_belt.equip(0, ritual); the_belt.equip(0, ritual);
REQUIRE(the_belt.has(0)); REQUIRE(the_belt.has(0));
} }
@ -95,20 +96,44 @@ TEST_CASE("the ritual belt works", "[rituals-belt]") {
the_belt.unequip(0); the_belt.unequip(0);
REQUIRE(!the_belt.has(0)); REQUIRE(!the_belt.has(0));
} }
}
TEST_CASE("LevelManager makes a temp belt", "[rituals-belt]") {
LevelManager lm;
lm.temp_create_player_rituals();
auto& level = lm.current();
auto& the_belt = level.world->get<RitualBelt>(level.player);
REQUIRE(the_belt.has(0)); craft_state.reset();
auto& ritual = the_belt.get(0); REQUIRE(craft_state.plan.script.size() == 0);
REQUIRE(ritual.damage > 0); REQUIRE(!craft_state.plan.complete);
REQUIRE(craft_state.start == craft_state.original);
REQUIRE(!craft_state.is_combined());
}
REQUIRE(the_belt.has(1)); TEST_CASE("ritual blanket basic operations", "[rituals-blanket]") {
ritual = the_belt.get(1); ritual::Blanket blanket;
REQUIRE(ritual.damage > 1);
DinkyECS::Entity other = blanket.add("rusty_nails");
DinkyECS::Entity ent = blanket.add("severed_finger");
auto& name = blanket.get(ent);
REQUIRE(name == "severed_finger");
REQUIRE(blanket.has(ent));
blanket.remove(ent);
REQUIRE(!blanket.has(ent));
REQUIRE(blanket.has(other));
REQUIRE(!blanket.is_selected(ent));
REQUIRE(!blanket.is_selected(other));
blanket.select(ent);
REQUIRE(blanket.is_selected(ent));
REQUIRE(!blanket.is_selected(other));
blanket.deselect(ent);
REQUIRE(!blanket.is_selected(ent));
blanket.select(ent);
blanket.select(other);
REQUIRE(blanket.is_selected(ent));
REQUIRE(blanket.is_selected(other));
blanket.reset();
REQUIRE(!blanket.is_selected(ent));
REQUIRE(!blanket.is_selected(other));
REQUIRE(blanket.selected.empty());
REQUIRE(blanket.selected.size() == 0);
} }

@ -25,7 +25,7 @@ namespace textures {
int height = settings["frame_height"]; int height = settings["frame_height"];
sprite->setTextureRect({{0,0}, {width, height}}); sprite->setTextureRect({{0,0}, {width, height}});
TMGR.sprite_textures.try_emplace(name, name, sprite, texture); TMGR.sprite_textures.try_emplace(name, sprite, texture);
} }
TMGR.floor = load_image(assets["sprites"]["floor"]["path"]); TMGR.floor = load_image(assets["sprites"]["floor"]["path"]);
@ -53,7 +53,7 @@ namespace textures {
} }
} }
SpriteTexture get(std::string name) { SpriteTexture get(const std::string& name) {
dbc::check(initialized, "you forgot to call textures::init()"); dbc::check(initialized, "you forgot to call textures::init()");
dbc::check(TMGR.sprite_textures.contains(name), dbc::check(TMGR.sprite_textures.contains(name),
fmt::format("!!!!! texture pack does not contain {} sprite", name)); fmt::format("!!!!! texture pack does not contain {} sprite", name));
@ -68,7 +68,7 @@ namespace textures {
return result; return result;
} }
sf::Image load_image(std::string filename) { sf::Image load_image(const std::string& filename) {
sf::Image texture; sf::Image texture;
bool good = texture.loadFromFile(filename); bool good = texture.loadFromFile(filename);
dbc::check(good, fmt::format("failed to load {}", filename)); dbc::check(good, fmt::format("failed to load {}", filename));

@ -10,7 +10,6 @@
namespace textures { namespace textures {
struct SpriteTexture { struct SpriteTexture {
std::string name;
std::shared_ptr<sf::Sprite> sprite = nullptr; std::shared_ptr<sf::Sprite> sprite = nullptr;
std::shared_ptr<sf::Texture> texture = nullptr; std::shared_ptr<sf::Texture> texture = nullptr;
}; };
@ -25,9 +24,9 @@ namespace textures {
void init(); void init();
SpriteTexture get(std::string name); SpriteTexture get(const std::string& name);
sf::Image load_image(std::string filename); sf::Image load_image(const std::string& filename);
const uint32_t* get_surface(size_t num); const uint32_t* get_surface(size_t num);

@ -5,6 +5,7 @@
using nlohmann::json; using nlohmann::json;
using components::Tile; using components::Tile;
using std::string;
TileMap::TileMap(size_t width, size_t height) : TileMap::TileMap(size_t width, size_t height) :
$config("./assets/tiles.json"), $config("./assets/tiles.json"),

@ -241,6 +241,16 @@ void WorldBuilder::place_stairs(DinkyECS::World& world, GameConfig& config) {
configure_entity_in_map(world, entity_data, last_room); configure_entity_in_map(world, entity_data, last_room);
} }
void WorldBuilder::configure_starting_items(DinkyECS::World &world) {
auto& blanket = world.get_the<ritual::Blanket>();
Config config("assets/rituals.json");
for(auto& el : config["starting_junk"]) {
ritual::JunkItem name = el;
blanket.add(name);
};
}
void WorldBuilder::place_entities(DinkyECS::World &world) { void WorldBuilder::place_entities(DinkyECS::World &world) {
auto &config = world.get_the<GameConfig>(); auto &config = world.get_the<GameConfig>();
// configure a player as a fact of the world // configure a player as a fact of the world
@ -258,7 +268,9 @@ void WorldBuilder::place_entities(DinkyECS::World &world) {
// configure player in the world // configure player in the world
Player player{player_ent}; Player player{player_ent};
world.set_the<Player>(player); world.set_the<Player>(player);
world.set<combat::RitualBelt>(player.entity, {}); world.set_the<ritual::Belt>({});
world.set_the<ritual::Blanket>({});
configure_starting_items(world);
world.set<Inventory>(player.entity, {5}); world.set<Inventory>(player.entity, {5});
world.make_constant(player.entity); world.make_constant(player.entity);
} }

@ -21,7 +21,7 @@ class WorldBuilder {
void place_rooms(); void place_rooms();
void tunnel_doors(PointList &holes, Room &src, Room &target); void tunnel_doors(PointList &holes, Room &src, Room &target);
void update_door(Point &at, int wall_or_space); void update_door(Point &at, int wall_or_space);
void stylize_room(int room, string tile_name, float size); void stylize_room(int room, std::string tile_name, float size);
void generate_rooms(); void generate_rooms();
void generate_map(); void generate_map();
@ -30,4 +30,5 @@ class WorldBuilder {
void generate(DinkyECS::World &world); void generate(DinkyECS::World &world);
void randomize_entities(DinkyECS::World &world, components::GameConfig &config); void randomize_entities(DinkyECS::World &world, components::GameConfig &config);
void place_stairs(DinkyECS::World& world, components::GameConfig& config); void place_stairs(DinkyECS::World& world, components::GameConfig& config);
void configure_starting_items(DinkyECS::World &world);
}; };

@ -0,0 +1,9 @@
[wrap-git]
directory=lel-guecs-0.2.0
url=https://git.learnjsthehardway.com/learn-code-the-hard-way/lel-guecs.git
revision=HEAD
depth=1
method=meson
[provide]
lel_guecs = lel_guecs_dep
Loading…
Cancel
Save