Refactored the FSM so that it uses a generic registry of systems to do what it needs.

This commit is contained in:
Zed A. Shaw 2026-03-23 12:47:16 -04:00
parent cbff127b40
commit e742b8772d
7 changed files with 150 additions and 118 deletions

82
src/game/registry.hpp Normal file
View file

@ -0,0 +1,82 @@
#pragma once
#include <functional>
#include "game/components.hpp"
namespace System {
using MovingFunc = std::function<void(components::Position& move_to)>;
using CombatFunc = std::function<void(int attack_id)>;
using RenderFunc = std::function<void()>;
using UpdateFunc = std::function<void()>;
using UseItemFunc = std::function<void(const std::string& slot_name)>;
using PickupFunc = std::function<void()>;
struct Registry {
std::vector<MovingFunc> $moving;
std::vector<CombatFunc> $combat;
std::vector<RenderFunc> $render;
std::vector<UpdateFunc> $update;
std::vector<UseItemFunc> $use_item;
std::vector<PickupFunc> $pickup;
void addMoving(MovingFunc action) {
$moving.emplace_back(action);
}
void addCombat(CombatFunc action) {
$combat.emplace_back(action);
}
void addRender(RenderFunc action) {
$render.emplace_back(action);
}
void addUpdate(UpdateFunc action) {
$update.emplace_back(action);
}
void addUseItem(UseItemFunc action) {
$use_item.emplace_back(action);
}
void addPickup(PickupFunc action) {
$pickup.emplace_back(action);
}
void runMoving(components::Position move_to) {
for(auto func : $moving) {
func(move_to);
}
}
void runCombat(int attack_id) {
for(auto func : $combat) {
func(attack_id);
}
}
void runRender() {
for(auto func : $render) {
func();
}
}
void runUpdate() {
for(auto func : $update) {
func();
}
}
void runUseItem(const std::string& slot_name) {
for(auto func : $use_item) {
func(slot_name);
}
}
void runPickup() {
for(auto func : $pickup) {
func();
}
}
};
}

View file

@ -474,14 +474,14 @@ bool System::inventory_occupied(Entity container_id, const std::string& name) {
return inventory.has(name);
}
bool System::use_item(const string& slot_name) {
void System::use_item(const string& slot_name) {
auto& level = GameDB::current_level();
auto& world = *level.world;
auto& inventory = world.get<inventory::Model>(level.player);
auto& player_combat = world.get<Combat>(level.player);
if(player_combat.hp >= player_combat.max_hp) return false;
if(!inventory.has(slot_name)) return false;
if(player_combat.hp >= player_combat.max_hp) return;
if(!inventory.has(slot_name)) return;
auto what = inventory.get(slot_name);
@ -498,10 +498,8 @@ bool System::use_item(const string& slot_name) {
player_combat.hp));
world.remove<Curative>(what);
return true;
} else {
dbc::log($F("no usable item at {}", what));
return false;
}
}
@ -541,3 +539,19 @@ void System::clear_attack() {
void System::spawn_attack(World& world, int attack_id, DinkyECS::Entity enemy) {
}
void System::init(Registry& reg) {
reg.addRender(System::clear_attack);
reg.addUseItem(System::use_item);
reg.addMoving(System::move_player);
reg.addCombat(System::combat);
reg.addPickup(System::pickup);
reg.addUpdate(System::generate_paths);
reg.addUpdate(System::enemy_ai_initialize);
reg.addUpdate(System::enemy_pathing);
reg.addUpdate(System::motion);
reg.addUpdate(System::collision);
reg.addUpdate(System::lighting);
reg.addUpdate(System::death);
}

View file

@ -5,8 +5,10 @@
#include "algos/spatialmap.hpp"
#include "game/level.hpp"
#include "events.hpp"
#include "game/registry.hpp"
namespace System {
using namespace components;
using namespace DinkyECS;
using std::string, matrix::Matrix;
@ -32,15 +34,15 @@ namespace System {
void pickup();
// BUG: these might need to go somewhere else....
bool place_in_container(Entity cont_id, const string& name, Entity world_entity);
void remove_from_container(Entity cont_id, const std::string& name);
void remove_from_world(Entity entity);
void inventory_swap(Entity container_id, const std::string& a_name, const std::string &b_name);
bool inventory_occupied(Entity container_id, const std::string& name);
void set_position(DinkyECS::World& world, SpatialMap& collision, Entity entity, Position pos);
bool use_item(const std::string& slot_name);
void use_item(const std::string& slot_name);
game::Event shortest_rotate(Point player_at, Point aiming_at, Point turning_to);
@ -67,4 +69,6 @@ namespace System {
void clear_attack();
void spawn_attack(World& world, int attack_id, DinkyECS::Entity enemy);
void init(Registry& reg);
}

View file

@ -58,7 +58,7 @@ namespace gui {
void FSM::MOVING(Event ) {
// this should be an optional that returns a point
if(auto move_to = $main_ui.play_move()) {
System::move_player(*move_to);
$systems.runMoving(*move_to);
run_systems();
$main_ui.dirty();
state(State::IDLE);
@ -70,7 +70,7 @@ namespace gui {
switch(ev) {
case TICK: {
dbc::log("!!!!!! FIX System::combat(0) doesn't use any weapons, only first");
System::combat(0);
$systems.runCombat(0);
run_systems();
state(State::IN_COMBAT);
} break;
@ -79,7 +79,7 @@ namespace gui {
break;
case ATTACK: {
int attack_id = std::any_cast<int>(data);
System::combat(attack_id);
$systems.runCombat(attack_id);
run_systems();
} break;
default:
@ -174,10 +174,8 @@ namespace gui {
case USE_ITEM: {
auto gui_id = std::any_cast<guecs::Entity>(data);
auto& slot_name = $status_ui.$gui.name_for(gui_id);
if(System::use_item(slot_name)) {
$status_ui.update();
}
$systems.runUseItem(slot_name);
$status_ui.update();
} break;
case MOUSE_CLICK:
mouse_action(guecs::NO_MODS);
@ -186,7 +184,7 @@ namespace gui {
mouse_action({1 << guecs::ModBit::hover});
} break;
case AIM_CLICK:
System::pickup();
$systems.runPickup();
break;
default:
break; // ignore everything else
@ -312,9 +310,6 @@ namespace gui {
$debug_ui.debug();
shaders::reload();
break;
case KEY::O:
autowalking = true;
break;
case KEY::L:
// This will go away as soon as containers work
$loot_ui.set_target($loot_ui.$temp_loot);
@ -360,10 +355,8 @@ namespace gui {
}
void FSM::render() {
$window.clear();
// this clears any attack animations, like fire
System::clear_attack();
$systems.runRender();
// BUG: this is the render for this class, and where I add an update
draw_gui();
@ -371,13 +364,7 @@ namespace gui {
}
void FSM::run_systems() {
System::generate_paths();
System::enemy_ai_initialize();
System::enemy_pathing();
System::motion();
System::collision();
System::lighting();
System::death();
$systems.runUpdate();
}
bool FSM::active() {

View file

@ -1,6 +1,7 @@
#pragma once
#include "constants.hpp"
#include "game/registry.hpp"
#include "algos/simplefsm.hpp"
#include "gui/debug_ui.hpp"
#include "gui/main_ui.hpp"
@ -35,12 +36,11 @@ namespace gui {
LootUI $loot_ui;
gui::routing::Router $router;
DNDLoot $dnd_loot;
System::Registry $systems;
FSM();
void event(game::Event ev, std::any data={});
void autowalk();
void start_autowalk(double rot_speed);
void START(game::Event ev);
void MOVING(game::Event ev);

View file

@ -7,6 +7,7 @@
#include "gui/backend.hpp"
#include "game/level.hpp"
#include "graphics/camera.hpp"
#include "game/systems.hpp"
int main(int argc, char* argv[]) {
try {
@ -21,6 +22,7 @@ int main(int argc, char* argv[]) {
sound::mute(true);
gui::FSM main;
System::init(main.$systems);
main.event(game::Event::START);
sound::play("ambient_1", true);

View file

@ -3,7 +3,9 @@
#include "game/systems.hpp"
#include <cmath>
#include <numbers>
#include "game/registry.hpp"
using components::Position;
TEST_CASE("figure out best rotation direction", "[systems-rotate]") {
Matrix map = matrix::make(3, 3);
@ -35,104 +37,45 @@ TEST_CASE("figure out best rotation direction", "[systems-rotate]") {
}
}
using MovingFunc = std::function<void()>;
using CombatFunc = std::function<void(bool)>;
using RenderFunc = std::function<void()>;
using UpdateFunc = std::function<void()>;
using UseFunc = std::function<void()>;
using PickupFunc = std::function<void()>;
struct Engine {
std::vector<MovingFunc> $moving;
std::vector<CombatFunc> $combat;
std::vector<RenderFunc> $render;
std::vector<UpdateFunc> $update;
std::vector<UseFunc> $use;
std::vector<PickupFunc> $pickup;
void addMoving(MovingFunc action) {
$moving.emplace_back(action);
}
void addCombat(CombatFunc action) {
$combat.emplace_back(action);
}
void addRender(RenderFunc action) {
$render.emplace_back(action);
}
void addUpdate(UpdateFunc action) {
$update.emplace_back(action);
}
void addUse(UseFunc action) {
$use.emplace_back(action);
}
void addPickup(PickupFunc action) {
$pickup.emplace_back(action);
}
void runMoving() {
for(auto func : $moving) {
func();
}
}
void runCombat(bool attr) {
for(auto func : $combat) {
func(attr);
}
}
void runRender() {
for(auto func : $render) {
func();
}
}
void runUpdate() {
for(auto func : $update) {
func();
}
}
void runUse() {
for(auto func : $use) {
func();
}
}
void runPickup() {
for(auto func : $pickup) {
func();
}
}
};
void test_system_1() {
fmt::println("TEST 1");
void test_moving(Position& move_to) {
fmt::println("MOVING: {},{}", move_to.location.x, move_to.location.y);
}
void test_combat(bool what) {
fmt::println("TEST 2: {}", what);
void test_combat(int attack_id) {
fmt::println("ATTACK: {}", attack_id);
}
void test_render() {
fmt::println("RENDER");
}
void test_update() {
fmt::println("UPDATE");
}
void test_use_item(const std::string& slot_name) {
fmt::println("USE ITEM {}", slot_name);
}
void test_pickup() {
fmt::println("PICKUP");
}
TEST_CASE("new system running engine thing", "[systems-engine]") {
Engine systems;
System::Registry systems;
systems.addMoving(test_system_1);
systems.addMoving(test_moving);
systems.addCombat(test_combat);
systems.addRender(test_system_1);
systems.addUpdate(test_system_1);
systems.addUse(test_system_1);
systems.addPickup(test_system_1);
systems.addRender(test_render);
systems.addUpdate(test_update);
systems.addUseItem(test_use_item);
systems.addPickup(test_pickup);
systems.runCombat(false);
systems.runMoving();
systems.runCombat(1);
systems.runMoving({1,1});
systems.runRender();
systems.runUpdate();
systems.runUse();
systems.runUseItem("hand_l");
systems.runPickup();
}