From 8ee3e8736f29d98a7be10e290731b40b6dcae91b Mon Sep 17 00:00:00 2001 From: "Zed A. Shaw" Date: Wed, 19 Nov 2025 13:26:21 -0500 Subject: [PATCH] Simple battle system included in the boss fight but I need to figure out how it should operate. --- assets/bosses.json | 5 ++-- battle.cpp | 4 +-- battle.hpp | 4 +-- boss/fight.cpp | 4 +-- boss/system.cpp | 65 +++++++++++++++++++++++++++++++++++++++++++--- boss/system.hpp | 4 +-- gui/fsm.cpp | 2 +- systems.cpp | 4 +-- 8 files changed, 75 insertions(+), 17 deletions(-) diff --git a/assets/bosses.json b/assets/bosses.json index b0f3c19..5a8fa5a 100644 --- a/assets/bosses.json +++ b/assets/bosses.json @@ -62,8 +62,9 @@ } ] }, - {"_type": "Combat", "hp": 20, "max_hp": 20, "damage": 20, "dead": false}, - {"_type": "Sound", "attack": "Marmot_Scream_1", "death": "Creature_Death_1"} + {"_type": "Combat", "hp": 200, "max_hp": 200, "damage": 20, "dead": false}, + {"_type": "Sound", "attack": "Marmot_Scream_1", "death": "Creature_Death_1"}, + {"_type": "EnemyConfig", "ai_script": "Enemy::actions", "ai_start_name": "Enemy::initial_state", "ai_goal_name": "Enemy::final_state"} ] } } diff --git a/battle.cpp b/battle.cpp index e6066e7..9808ce8 100644 --- a/battle.cpp +++ b/battle.cpp @@ -40,13 +40,13 @@ namespace combat { } } - void BattleEngine::set(DinkyECS::Entity entity, std::string state, bool setting) { + void BattleEngine::set(DinkyECS::Entity entity, const std::string& state, bool setting) { dbc::check(combatants.contains(entity), "invalid combatant given to BattleEngine"); auto& action = combatants.at(entity); action.ai.set_state(state, setting); } - void BattleEngine::set_all(std::string state, bool setting) { + void BattleEngine::set_all(const std::string& state, bool setting) { for(auto& [ent, action] : combatants) { action.ai.set_state(state, setting); } diff --git a/battle.hpp b/battle.hpp index 2aeb6f5..e01d3d6 100644 --- a/battle.hpp +++ b/battle.hpp @@ -30,8 +30,8 @@ namespace combat { bool plan(); std::optional next(); void dump(); - void set(DinkyECS::Entity entity, std::string state, bool setting); - void set_all(std::string state, bool setting); + void set(DinkyECS::Entity entity, const std::string& state, bool setting); + void set_all(const std::string& state, bool setting); void queue(DinkyECS::Entity entity, BattleAction action); }; } diff --git a/boss/fight.cpp b/boss/fight.cpp index d564f3a..ddb1373 100644 --- a/boss/fight.cpp +++ b/boss/fight.cpp @@ -87,7 +87,7 @@ namespace boss { const std::string& player_pos = run % 10 < 5 ? "player1" : "player2"; $ui.move_actor("player", player_pos); int attack_id = std::any_cast(data); - boss::System::combat(attack_id); + boss::System::combat($world, $boss_id, attack_id); state(State::PLAYER_TURN); } break; default: @@ -110,7 +110,7 @@ namespace boss { $ui.move_actor("boss", boss_at); $ui.animate_actor("boss"); int attack_id = std::any_cast(data); - boss::System::combat(attack_id); + boss::System::combat($world, $boss_id, attack_id); state(State::BOSS_TURN); } break; default: diff --git a/boss/system.cpp b/boss/system.cpp index 60d3b95..8018147 100644 --- a/boss/system.cpp +++ b/boss/system.cpp @@ -2,6 +2,8 @@ #include #include "components.hpp" #include "game_level.hpp" +#include "ai.hpp" +#include "battle.hpp" namespace boss { using namespace components; @@ -10,6 +12,22 @@ namespace boss { fmt::println("load it"); } + void System::initialize_boss_ai(DinkyECS::World& world, DinkyECS::Entity boss_id) { + dbc::check(world.has(boss_id), "boss doesn't have an AI EnemyConfig"); + + auto& config = world.get(boss_id); + + auto ai_start = ai::load_state(config.ai_start_name); + + auto ai_goal = ai::load_state(config.ai_goal_name); + + ai::EntityAI boss_ai(config.ai_script, ai_start, ai_goal); + boss_ai.set_state("tough_personality", true); + boss_ai.set_state("detect_enemy", true); + + world.set(boss_id, boss_ai); + } + shared_ptr System::create_bossfight() { auto& level = GameDB::current_level(); auto prev_world = GameDB::current_world(); @@ -24,13 +42,52 @@ namespace boss { auto boss_id = world->entity(); components::configure_entity(*world, boss_id, boss_data["components"]); + initialize_boss_ai(*world, boss_id); + + dbc::check(world->has(boss_id), "boss doesn't have an AI"); + return make_shared(world, boss_id); } - void System::combat(int attack_id) { - (void)attack_id; - } + void System::combat(std::shared_ptr world, DinkyECS::Entity boss_id, int attack_id) { + // get the player from the previous level, but should I just make the boss fights a level? + auto& level = GameDB::current_level(); + dbc::check(world->has(boss_id), "boss doesn't have an AI"); - void System::ai_initialize() { + auto& player_combat = world->get(level.player); + auto& boss_ai = world->get(boss_id); + auto& boss_combat = world->get(boss_id); + + combat::BattleEngine battle; + battle.add_enemy({boss_id, boss_ai, boss_combat}); + + battle.set_all("enemy_found", true); + battle.set_all("in_combat", true); + battle.plan(); + + battle.dump(); + + while(auto act = battle.next()) { + auto [enemy, enemy_action] = *act; + + Events::Combat result { + player_combat.attack(enemy.combat), 0 + }; + + if(result.player_did > 0) { + auto& the_belt = world->get_the(); + + dbc::check(the_belt.has(attack_id), "STOP passing invalid attack IDs to the system."); + fmt::println("player did damage"); + } + + if(enemy_action == combat::BattleAction::ATTACK) { + result.enemy_did = enemy.combat.attack(player_combat); + fmt::println("enemy did damage"); + } + + // need to replicate this in the boss UI + world->send(Events::GUI::COMBAT, enemy.entity, result); + } } } diff --git a/boss/system.hpp b/boss/system.hpp index 9bcfbb9..cea496f 100644 --- a/boss/system.hpp +++ b/boss/system.hpp @@ -7,7 +7,7 @@ namespace boss { namespace System { void load_config(); std::shared_ptr create_bossfight(); - void combat(int attack_id); - void ai_initialize(); + void combat(std::shared_ptr world, DinkyECS::Entity boss_id, int attack_id); + void initialize_boss_ai(DinkyECS::World& world, DinkyECS::Entity boss_id); } } diff --git a/gui/fsm.cpp b/gui/fsm.cpp index f4f6fcc..fcd6814 100644 --- a/gui/fsm.cpp +++ b/gui/fsm.cpp @@ -78,7 +78,7 @@ namespace gui { using enum Event; switch(ev) { case TICK: { - dbc::log("!!!!!! FIX System::combat"); + dbc::log("!!!!!! FIX System::combat(0) doesn't use any weapons, only first"); System::combat(0); run_systems(); state(State::IN_COMBAT); diff --git a/systems.cpp b/systems.cpp index 9e5beaf..176cdd9 100644 --- a/systems.cpp +++ b/systems.cpp @@ -210,8 +210,6 @@ void System::death() { }); // this goes through everything that died and changes them to a gravestone - // NOTE: this could be a separate system but also could be a function in - // components:: for(auto ent : dead_things) { if(auto snd = world.get_if(ent)) { sound::stop(snd->attack); @@ -255,6 +253,8 @@ void System::combat(int attack_id) { battle.plan(); } + battle.dump(); + while(auto act = battle.next()) { auto [enemy, enemy_action] = *act;