Event system now accepts any data and the GUI receives simpler events with data for them.

main
Zed A. Shaw 2 weeks ago
parent 0e79288afc
commit ed9d0de8e0
  1. 6
      dinkyecs.hpp
  2. 7
      events.hpp
  3. 35
      gui.cpp
  4. 4
      status.txt
  5. 31
      systems.cpp
  6. 2
      systems.hpp
  7. 15
      tests/dinkyecs.cpp

@ -18,6 +18,7 @@ namespace DinkyECS {
struct Event { struct Event {
int event = 0; int event = 0;
Entity entity = 0; Entity entity = 0;
std::any data;
}; };
typedef std::queue<Event> EventQueue; typedef std::queue<Event> EventQueue;
@ -105,9 +106,9 @@ namespace DinkyECS {
} }
template<typename Comp> template<typename Comp>
void send(Comp event, Entity entity) { void send(Comp event, Entity entity, std::any data) {
EventQueue &queue = queue_map_for<Comp>(); EventQueue &queue = queue_map_for<Comp>();
queue.push({event, entity}); queue.push({event, entity, data});
} }
template<typename Comp> template<typename Comp>
@ -115,6 +116,7 @@ namespace DinkyECS {
EventQueue &queue = queue_map_for<Comp>(); EventQueue &queue = queue_map_for<Comp>();
Event evt = queue.front(); Event evt = queue.front();
queue.pop(); queue.pop();
// I could use tie here to auto extract the any
return evt; return evt;
} }

@ -2,6 +2,11 @@
namespace Events { namespace Events {
enum GUI { enum GUI {
START, HIT, MISS, DEAD, LOOT START, COMBAT, LOOT
};
struct Combat {
int player_did;
int enemy_did;
}; };
} }

@ -104,31 +104,30 @@ void GUI::handle_world_events() {
auto player = $world.get_the<Player>(); auto player = $world.get_the<Player>();
while($world.has_event<eGUI>()) { while($world.has_event<eGUI>()) {
auto [evt, entity] = $world.recv<eGUI>(); auto [evt, entity, data] = $world.recv<eGUI>();
switch(evt) { switch(evt) {
case eGUI::HIT: { case eGUI::COMBAT: {
auto combat = $world.get<Combat>(entity); auto &damage = std::any_cast<Events::Combat&>(data);
auto enemy_combat = $world.get<Combat>(entity);
if(entity == player.entity) { if(damage.enemy_did > 0) {
$log.log(format("Enemy HIT YOU, you have {} HP!", combat.hp)); $log.log(format("Enemy HIT YOU for {} damage!", damage.enemy_did));
$log.log(format("-- Enemy has {} HP left.", enemy_combat.hp));
$sounds.play("hit"); $sounds.play("hit");
} else { } else {
$log.log(format("You HIT enemy, they have {} HP!", combat.hp)); $log.log("Enemy MISSED YOU.");
$sounds.play("hit");
} }
} break;
case eGUI::MISS: if(damage.player_did > 0) {
if(entity == player.entity) { $log.log(format("You HIT enemy for {} damage!", damage.player_did));
$log.log("You MISSED the enemy."); $sounds.play("hit");
} else { } else {
$log.log("Enemy MISSED YOU."); $log.log("You MISSED the enemy.");
} }
break; } break;
case eGUI::DEAD:
$log.log("--- ENEMY DEAD!");
break;
case eGUI::LOOT: { case eGUI::LOOT: {
auto loot = $world.get<Loot>(entity); auto &loot = std::any_cast<Loot&>(data);
auto inventory = $world.get<Inventory>(player.entity); auto inventory = $world.get<Inventory>(player.entity);
$log.log(format("You found {} gold. You have {} now.", $log.log(format("You found {} gold. You have {} now.",
loot.amount, inventory.gold)); loot.amount, inventory.gold));
@ -182,7 +181,7 @@ void GUI::run_systems() {
auto player = $world.get_the<Player>(); auto player = $world.get_the<Player>();
System::enemy_pathing($world, $game_map, player); System::enemy_pathing($world, $game_map, player);
System::motion($world, $game_map); System::motion($world, $game_map);
System::combat($world, player); System::collision($world, player);
System::death($world); System::death($world);
} }

@ -2,9 +2,7 @@ NOTES:
TODO: TODO:
* Save file isn't saving gold.
* Event system could take additional data so that the events can be more coarse and simpler.
* Simplify the combat/collision system so that it's not a bunch of if-cases.
* Inventory needs to be better, but need some kinds of "weapons" or other loot to get and not just gold. * Inventory needs to be better, but need some kinds of "weapons" or other loot to get and not just gold.
* Run the ansi_parser on the whole UI so I can use colors and other glyphs. * Run the ansi_parser on the whole UI so I can use colors and other glyphs.
* Create a few more enemy types to fight. * Create a few more enemy types to fight.

@ -91,7 +91,7 @@ void System::death(DinkyECS::World &world) {
}); });
} }
void System::combat(DinkyECS::World &world, Player &player) { void System::collision(DinkyECS::World &world, Player &player) {
auto& collider = world.get_the<spatial_map>(); auto& collider = world.get_the<spatial_map>();
const auto& player_position = world.get<Position>(player.entity); const auto& player_position = world.get<Position>(player.entity);
auto& player_combat = world.get<Combat>(player.entity); auto& player_combat = world.get<Combat>(player.entity);
@ -100,36 +100,23 @@ void System::combat(DinkyECS::World &world, Player &player) {
auto [found, nearby] = collider.neighbors(player_position.location); auto [found, nearby] = collider.neighbors(player_position.location);
if(found) { if(found) {
// save some keystrokes
using eGUI = Events::GUI;
for(auto entity : nearby) { for(auto entity : nearby) {
if(world.has<Combat>(entity)) { if(world.has<Combat>(entity)) {
auto& enemy_combat = world.get<Combat>(entity); auto& enemy_combat = world.get<Combat>(entity);
int player_dmg = player_combat.attack(enemy_combat);
if(player_dmg > 0) { Events::Combat result {
world.send<eGUI>(eGUI::HIT, entity); player_combat.attack(enemy_combat),
} else { enemy_combat.attack(player_combat)
world.send<eGUI>(eGUI::MISS, entity); };
}
if(enemy_combat.hp > 0) {
int enemy_dmg = enemy_combat.attack(player_combat);
if(enemy_dmg > 0) { world.send<Events::GUI>(Events::GUI::COMBAT, entity, result);
world.send<eGUI>(eGUI::HIT, player.entity);
} else {
world.send<eGUI>(eGUI::MISS, player.entity);
}
} else {
world.send<eGUI>(eGUI::DEAD, entity);
}
} else if(world.has<Loot>(entity)) { } else if(world.has<Loot>(entity)) {
world.send<eGUI>(eGUI::LOOT, entity); auto loot = world.get<Loot>(entity);
auto &loot = world.get<Loot>(entity);
auto &loot_pos = world.get<Position>(entity); auto &loot_pos = world.get<Position>(entity);
auto &inventory = world.get<Inventory>(player.entity); auto &inventory = world.get<Inventory>(player.entity);
world.send<Events::GUI>(Events::GUI::LOOT, entity, loot);
inventory.gold += loot.amount; inventory.gold += loot.amount;
collider.remove(loot_pos.location); collider.remove(loot_pos.location);
} else { } else {

@ -8,7 +8,7 @@ using namespace components;
namespace System { namespace System {
void motion(DinkyECS::World &world, Map &game_map); void motion(DinkyECS::World &world, Map &game_map);
void combat(DinkyECS::World &world, Player &player); void collision(DinkyECS::World &world, Player &player);
void death(DinkyECS::World &world); void death(DinkyECS::World &world);
void enemy_pathing(DinkyECS::World &world, Map &game_map, Player &player); void enemy_pathing(DinkyECS::World &world, Map &game_map, Player &player);
void draw_map(DinkyECS::World &world, Map &game_map, ftxui::Canvas &canvas, size_t view_x, size_t view_y); void draw_map(DinkyECS::World &world, Map &game_map, ftxui::Canvas &canvas, size_t view_x, size_t view_y);

@ -5,9 +5,10 @@
using namespace fmt; using namespace fmt;
using DinkyECS::Entity; using DinkyECS::Entity;
using std::string;
struct Player { struct Player {
std::string name; string name;
Entity eid; Entity eid;
}; };
@ -129,22 +130,18 @@ enum GUIEvent {
TEST_CASE("confirm that the event system works", "[ecs]") { TEST_CASE("confirm that the event system works", "[ecs]") {
DinkyECS::World world; DinkyECS::World world;
DinkyECS::Entity gui_ent = world.entity();
DinkyECS::Entity player = world.entity(); DinkyECS::Entity player = world.entity();
GUIEvent gui{GUIEvent::HIT};
world.set<GUIEvent>(gui_ent, gui); world.send<GUIEvent>(GUIEvent::HIT, player, string{"hello"});
auto &gui_test = world.get<GUIEvent>(gui_ent);
REQUIRE(gui == gui_test);
world.send<GUIEvent>(GUIEvent::HIT, player);
bool ready = world.has_event<GUIEvent>(); bool ready = world.has_event<GUIEvent>();
REQUIRE(ready == true); REQUIRE(ready == true);
auto [event, entity] = world.recv<GUIEvent>(); auto [event, entity, data] = world.recv<GUIEvent>();
REQUIRE(event == GUIEvent::HIT); REQUIRE(event == GUIEvent::HIT);
REQUIRE(entity == player); REQUIRE(entity == player);
auto &str_data = std::any_cast<string&>(data);
REQUIRE(string{"hello"} == str_data);
ready = world.has_event<GUIEvent>(); ready = world.has_event<GUIEvent>();
REQUIRE(ready == false); REQUIRE(ready == false);

Loading…
Cancel
Save