Boss fight is now working. Turned out to be something really dumb.

This commit is contained in:
Zed A. Shaw 2025-12-19 12:04:51 -05:00
parent 4958f24c1d
commit 4b4f9b3916
8 changed files with 154 additions and 127 deletions

Binary file not shown.

View file

@ -10,18 +10,18 @@
], ],
"beats": [ "beats": [
["00:00", "a","pan"], ["00:00", "a","pan"],
["00:06", "a","pan"], ["00:01", "a","pan"],
["00:13", "b","pan"], ["00:02", "b","pan"],
["00:19", "g","pan"], ["00:03", "g","pan"],
["00:25", "h","pan"], ["00:04", "h","pan"],
["00:29", "h","bounce"], ["00:05", "h","bounce"],
["00:31", "c1","pan"], ["00:06", "c1","pan"],
["00:34", "c2","pan"], ["00:07", "c2","pan"],
["00:39", "d","bounce"], ["00:08", "d","bounce"],
["00:44", "e","shake"], ["00:09", "e","shake"],
["00:48", "i","pan"], ["00:10", "i","pan"],
["00:53", "i","shake"], ["00:11", "i","shake"],
["00:55", "i","bounce"] ["00:12", "i","bounce"]
] ]
} }
} }

View file

@ -4,8 +4,11 @@
#include "game_level.hpp" #include "game_level.hpp"
#include <iostream> #include <iostream>
#include "rand.hpp" #include "rand.hpp"
#include "events.hpp"
namespace boss { namespace boss {
using namespace DinkyECS;
Fight::Fight(shared_ptr<World> world, Entity boss_id, Entity player_id) : Fight::Fight(shared_ptr<World> world, Entity boss_id, Entity player_id) :
$world(world), $world(world),
$boss_id(boss_id), $boss_id(boss_id),
@ -19,9 +22,10 @@ namespace boss {
$ui.init(); $ui.init();
} }
bool Fight::event(game::Event ev, std::any data) { void Fight::event(game::Event ev, std::any data) {
// if the mouse event is handled the done, this may be wrong later // if the mouse event is handled the done, this may be wrong later
if(handle_mouse(ev)) return in_state(State::END); if(handle_mouse(ev)) return;
switch($state) { switch($state) {
FSM_STATE(State, START, ev, data); FSM_STATE(State, START, ev, data);
@ -30,10 +34,6 @@ namespace boss {
FSM_STATE(State, EXEC_PLAN, ev, data); FSM_STATE(State, EXEC_PLAN, ev, data);
FSM_STATE(State, END, ev, data); FSM_STATE(State, END, ev, data);
} }
run_systems();
return in_state(State::END);
} }
void Fight::START(game::Event ev, std::any) { void Fight::START(game::Event ev, std::any) {
@ -41,10 +41,8 @@ namespace boss {
switch(ev) { switch(ev) {
// this is only if using the debug X key to skip it // this is only if using the debug X key to skip it
case BOSS_START:
state(State::END);
break;
case TICK: case TICK:
case BOSS_START:
$ui.status(L"PLAYER REQUESTS"); $ui.status(L"PLAYER REQUESTS");
$battle.ap_refresh(); $battle.ap_refresh();
state(State::PLAYER_REQUESTS); state(State::PLAYER_REQUESTS);
@ -169,7 +167,6 @@ namespace boss {
} }
void Fight::render(sf::RenderWindow& window) { void Fight::render(sf::RenderWindow& window) {
window.clear();
$ui.play_animations(); $ui.play_animations();
$ui.render(window); $ui.render(window);
} }
@ -179,10 +176,10 @@ namespace boss {
switch(ev) { switch(ev) {
case MOUSE_CLICK: { case MOUSE_CLICK: {
$ui.mouse(mouse_pos.x, mouse_pos.y, guecs::NO_MODS); $ui.mouse($router.position.x, $router.position.y, guecs::NO_MODS);
} break; } break;
case MOUSE_MOVE: { case MOUSE_MOVE: {
$ui.mouse(mouse_pos.x, mouse_pos.y, {1 << guecs::ModBit::hover}); $ui.mouse($router.position.x, $router.position.y, {1 << guecs::ModBit::hover});
} break; } break;
case MOUSE_DRAG: case MOUSE_DRAG:
dbc::log("mouse drag"); dbc::log("mouse drag");
@ -210,33 +207,47 @@ namespace boss {
run_systems(); run_systems();
} }
void Fight::handle_world_events() { void Fight::set_window(sf::RenderWindow* window) {
fmt::println(">>>>>>>>>>>>>>>>> BOSS FIGHT EVENTS"); $window = window;
dbc::check($world->has_event<game::Event>(), "World doesn't have GUI events."); }
while($world->has_event<game::Event>()) { bool Fight::handle_keyboard_mouse() {
auto [evt, entity, data] = $world->recv<game::Event>(); dbc::check($window != nullptr, "you didn't set_window");
fmt::println("boss fight received event", int(evt));
switch(evt) { while(const auto ev = $window->pollEvent()) {
case game::Event::ATTACK: auto gui_ev = $router.process_event(ev);
fmt::println("sending attack");
event(game::Event::ATTACK, data); if(gui_ev == game::Event::KEY_PRESS) {
break; using KEY = sf::Keyboard::Scan;
case game::Event::COMBAT_START:
fmt::println("sending combat start {}", data.type().name()); switch($router.scancode) {
event(game::Event::COMBAT_START, data); // REALLY? just go to state end or use another event
break; case KEY::X:
case game::Event::COMBAT: event(game::Event::BOSS_START, {});
fmt::println("sending combat {}", data.type().name());
event(game::Event::COMBAT, data);
break; break;
default: default:
fmt::println("GUI EVENT: {} entity={}", int(evt), entity); fmt::println("key press!");
}
} else if(gui_ev == game::Event::QUIT) {
return true;
} else {
event(gui_ev, {});
} }
} }
dbc::check(!$world->has_event<game::Event>(), "World still has events!"); return in_state(State::END);
fmt::println("<<<<<<<<<<<<<<<< BOSS FIGHT EVENTS"); }
bool Fight::handle_world_events() {
if($world->has_event<game::Event>()) {
while($world->has_event<game::Event>()) {
auto [evt, entity, data] = $world->recv<game::Event>();
event(game::Event(evt), data);
run_systems();
}
}
return in_state(State::END);
} }
} }

View file

@ -5,35 +5,39 @@
#include "boss/ui.hpp" #include "boss/ui.hpp"
#include "events.hpp" #include "events.hpp"
#include "battle.hpp" #include "battle.hpp"
#include "gui/event_router.hpp"
#include <memory> #include <memory>
#include <any> #include <any>
namespace boss { namespace boss {
using namespace DinkyECS;
using std::shared_ptr; using std::shared_ptr;
enum class State { enum class State {
START=0, START=__LINE__,
PLAYER_REQUESTS=1, PLAYER_REQUESTS=__LINE__,
PLAN_BATTLE=2, PLAN_BATTLE=__LINE__,
EXEC_PLAN=3, EXEC_PLAN=__LINE__,
END=4 END=__LINE__
}; };
class Fight : public DeadSimpleFSM<State, game::Event> { class Fight : public DeadSimpleFSM<State, game::Event> {
public: public:
shared_ptr<World> $world = nullptr; sf::RenderWindow* $window = nullptr;
DinkyECS::Entity $boss_id = NONE; shared_ptr<DinkyECS::World> $world = nullptr;
DinkyECS::Entity $boss_id = DinkyECS::NONE;
combat::BattleEngine $battle; combat::BattleEngine $battle;
boss::UI $ui; boss::UI $ui;
sf::Vector2f mouse_pos{0,0}; DinkyECS::Entity $host = DinkyECS::NONE;
Entity $host = NONE;
components::Combat* $host_combat = nullptr; components::Combat* $host_combat = nullptr;
gui::routing::Router $router;
Fight(shared_ptr<World> world, Entity boss_id, Entity player_id); Fight(shared_ptr<DinkyECS::World> world,
DinkyECS::Entity boss_id,
DinkyECS::Entity player_id);
void set_window(sf::RenderWindow* window);
bool handle_mouse(game::Event ev); bool handle_mouse(game::Event ev);
bool event(game::Event ev, std::any data); void event(game::Event ev, std::any data);
void START(game::Event ev, std::any data); void START(game::Event ev, std::any data);
void PLAYER_REQUESTS(game::Event ev, std::any data); void PLAYER_REQUESTS(game::Event ev, std::any data);
@ -42,12 +46,14 @@ namespace boss {
void END(game::Event ev, std::any data); void END(game::Event ev, std::any data);
void render(sf::RenderWindow& window); void render(sf::RenderWindow& window);
void handle_world_events(); bool handle_world_events();
void run_systems(); void run_systems();
bool player_dead(); bool player_dead();
void init_fight(); void init_fight();
void do_combat(std::any data); void do_combat(std::any data);
void next_combat_dumb_name(); void next_combat_dumb_name();
sf::Vector2f mouse_position();
bool handle_keyboard_mouse();
}; };
} }

View file

@ -39,6 +39,7 @@ namespace gui {
FSM_STATE(State, IDLE, ev, data); FSM_STATE(State, IDLE, ev, data);
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, CUT_SCENE_PLAYING, ev, data);
FSM_STATE(State, BOSS_FIGHT, ev, data); FSM_STATE(State, BOSS_FIGHT, ev, data);
FSM_STATE(State, LOOTING, ev, data); FSM_STATE(State, LOOTING, ev, data);
FSM_STATE(State, END, ev); FSM_STATE(State, END, ev);
@ -175,7 +176,7 @@ namespace gui {
case BOSS_START: case BOSS_START:
sound::stop("ambient"); sound::stop("ambient");
next_level(true); next_level(true);
state(State::BOSS_FIGHT); state(State::CUT_SCENE_PLAYING);
break; break;
case LOOT_ITEM: case LOOT_ITEM:
$dnd_loot.event(Event::LOOT_ITEM); $dnd_loot.event(Event::LOOT_ITEM);
@ -211,26 +212,20 @@ namespace gui {
} }
} }
void FSM::BOSS_FIGHT(Event ev, std::any data) { void FSM::CUT_SCENE_PLAYING(Event ev, std::any data) {
dbc::check($boss_fight != nullptr, "$boss_fight not initialized");
if($boss_scene->playing()) { if($boss_scene->playing()) {
if(ev == game::Event::MOUSE_CLICK) { if(ev == game::Event::MOUSE_CLICK) {
dbc::log("exiting cut scene");
$boss_scene->mouse(0,0, 0); $boss_scene->mouse(0,0, 0);
state(State::BOSS_FIGHT);
}
} else {
state(State::BOSS_FIGHT);
} }
return;
} }
$boss_fight->mouse_pos = mouse_position(); void FSM::BOSS_FIGHT(Event ev, std::any data) {
dbc::log("this should not run, it's handled in handle_boss_fight_events");
if(ev == game::Event::QUIT) {
// just epsilon straight to the exit
IDLE(ev, data);
} if($boss_fight->event(ev, data)) {
sound::play("ambient");
next_level(false);
state(State::IDLE);
}
} }
void FSM::IN_COMBAT(Event ev) { void FSM::IN_COMBAT(Event ev) {
@ -406,18 +401,20 @@ namespace gui {
} }
void FSM::render() { void FSM::render() {
if(in_state(State::BOSS_FIGHT)) {
$window.clear(); $window.clear();
if($boss_scene->playing()) {
if(in_state(State::BOSS_FIGHT)) {
$boss_fight->render($window);
// this clears any attack animations, like fire
System::clear_attack();
} else if(in_state(State::CUT_SCENE_PLAYING)) {
$boss_scene->render($window); $boss_scene->render($window);
} else { } else {
$boss_fight->render($window); // this clears any attack animations, like fire
} System::clear_attack();
} else {
draw_gui(); draw_gui();
} }
System::clear_attack();
$window.display(); $window.display();
} }
@ -435,6 +432,19 @@ namespace gui {
return !in_state(State::END); return !in_state(State::END);
} }
void FSM::handle_boss_fight_events() {
dbc::check($boss_fight != nullptr, "$boss_fight not initialized");
// true means State::END in $boss_fight
if($boss_fight->handle_world_events() || $boss_fight->handle_keyboard_mouse()) {
dbc::log("boss fight ended, transition to IDLE");
// fight is over, go back to regular game
sound::play("ambient");
next_level(false);
state(State::IDLE);
}
}
void FSM::handle_world_events() { void FSM::handle_world_events() {
using eGUI = game::Event; using eGUI = game::Event;
auto world = GameDB::current_world(); auto world = GameDB::current_world();
@ -529,7 +539,9 @@ namespace gui {
} }
} break; } break;
default: default:
$map_ui.log(fmt::format(L"INVALID EVENT! {},{}", evt, entity)); dbc::log(fmt::format("Unhandled event: evt={}; enemy={}; data={}",
evt, entity, data.type().name()));
event(game::Event(evt), data);
} }
} }
} }
@ -547,8 +559,11 @@ namespace gui {
void FSM::next_level(bool bossfight) { void FSM::next_level(bool bossfight) {
if(bossfight) { if(bossfight) {
$boss_scene = std::make_shared<storyboard::UI>("rat_king"); $boss_scene = std::make_shared<storyboard::UI>("rat_king");
$boss_fight = boss::System::create_bossfight();
$boss_scene->init(); $boss_scene->init();
$boss_fight = boss::System::create_bossfight();
$boss_fight->set_window(&$window);
dbc::check($boss_scene->playing(), "boss scene doesn't play"); dbc::check($boss_scene->playing(), "boss scene doesn't play");
} else { } else {
GameDB::create_level(); GameDB::create_level();

View file

@ -17,16 +17,17 @@
namespace gui { namespace gui {
enum class State { enum class State {
START=0, START=__LINE__,
MOVING=1, MOVING=__LINE__,
IN_COMBAT=2, IN_COMBAT=__LINE__,
COMBAT_ROTATE=3, COMBAT_ROTATE=__LINE__,
ATTACKING=4, ATTACKING=__LINE__,
ROTATING=5, ROTATING=__LINE__,
BOSS_FIGHT=6, BOSS_FIGHT=__LINE__,
LOOTING=7, LOOTING=__LINE__,
IDLE=8, IDLE=__LINE__,
END=9 CUT_SCENE_PLAYING=__LINE__,
END=__LINE__,
}; };
class FSM : public DeadSimpleFSM<State, game::Event> { class FSM : public DeadSimpleFSM<State, game::Event> {
@ -63,6 +64,7 @@ namespace gui {
void COMBAT_ROTATE(game::Event ev); void COMBAT_ROTATE(game::Event ev);
void BOSS_FIGHT(game::Event ev, std::any data); void BOSS_FIGHT(game::Event ev, std::any data);
void LOOTING(game::Event ev, std::any data); void LOOTING(game::Event ev, std::any data);
void CUT_SCENE_PLAYING(game::Event ev, std::any data);
void END(game::Event ev); void END(game::Event ev);
void try_move(int dir, bool strafe); void try_move(int dir, bool strafe);
@ -74,6 +76,7 @@ namespace gui {
bool active(); bool active();
void run_systems(); void run_systems();
void handle_world_events(); void handle_world_events();
void handle_boss_fight_events();
void next_level(bool bossfight); void next_level(bool bossfight);
void debug_render(); void debug_render();
void take_screenshot(); void take_screenshot();

View file

@ -35,10 +35,13 @@ int main(int argc, char* argv[]) {
while(main.active()) { while(main.active()) {
main.render(); main.render();
if(main.in_state(gui::State::BOSS_FIGHT)) {
main.handle_boss_fight_events();
} else {
// BUG: need to sort out how to deal with this in the FSM // BUG: 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::BOSS_FIGHT)
|| main.in_state(gui::State::LOOTING) || main.in_state(gui::State::LOOTING)
|| main.in_state(gui::State::CUT_SCENE_PLAYING)
|| main.in_state(gui::State::IN_COMBAT)) || main.in_state(gui::State::IN_COMBAT))
{ {
if(main.autowalking) { if(main.autowalking) {
@ -52,6 +55,7 @@ int main(int argc, char* argv[]) {
main.handle_world_events(); main.handle_world_events();
} }
}
return 0; return 0;
} catch(const std::system_error& e) { } catch(const std::system_error& e) {

View file

@ -1,4 +1,3 @@
#include "gui/fsm.hpp"
#include "textures.hpp" #include "textures.hpp"
#include "sound.hpp" #include "sound.hpp"
#include "autowalker.hpp" #include "autowalker.hpp"
@ -36,8 +35,6 @@ int main(int, char*[]) {
if(FRAME_LIMIT) window.setFramerateLimit(FRAME_LIMIT); if(FRAME_LIMIT) window.setFramerateLimit(FRAME_LIMIT);
window.setPosition({0,0}); window.setPosition({0,0});
gui::routing::Router router;
sound::mute(true); sound::mute(true);
sound::play("ambient_1", true); sound::play("ambient_1", true);
@ -45,25 +42,16 @@ int main(int, char*[]) {
craft_weapon(); craft_weapon();
auto main = boss::System::create_bossfight(); auto main = boss::System::create_bossfight();
main->set_window(&window);
auto world = GameDB::current_world(); auto world = GameDB::current_world();
dbc::check(main->$world == world, "GameDB::current_world doesn't match boss fight world."); dbc::check(main->$world == world, "GameDB::current_world doesn't match boss fight world.");
while(!main->in_state(boss::State::END)) { while(!main->in_state(boss::State::END)) {
main->mouse_pos = window.mapPixelToCoords(router.position); if(main->handle_keyboard_mouse() ||
main->handle_world_events())
while(const auto ev = window.pollEvent()) { {
auto gui_ev = router.process_event(ev);
if(gui_ev == game::Event::QUIT || main->event(gui_ev, {})) {
return 0; return 0;
} else {
main->event(game::Event::TICK, {});
}
}
if(main->$world->has_event<game::Event>()) {
main->handle_world_events();
dbc::check(!main->$world->has_event<game::Event>(), "You didn't eat all the events.");
} }
main->render(window); main->render(window);