Boss fight now reuses the combat_ui.cpp to do the combat panel, so next is making it work with the mechanics I want but no art.

This commit is contained in:
Zed A. Shaw 2025-09-24 14:22:24 -04:00
parent 6ec43026b6
commit 8a828fbd31
11 changed files with 107 additions and 91 deletions

View file

@ -101,7 +101,7 @@ void Autowalker::path_fail(const std::string& msg, Matrix& bad_paths, Point pos)
status(L"PATH FAIL");
matrix::dump("MOVE FAIL PATHS", bad_paths, pos.x, pos.y);
log(L"Autowalk failed to find a path.");
send_event(gui::Event::STAIRS_DOWN);
send_event(gui::Event::BOSS_START);
}
bool Autowalker::path_player(Pathing& paths, Point& target_out) {
@ -176,9 +176,9 @@ void Autowalker::update_state(ai::EntityAI& player_ai) {
void Autowalker::handle_boss_fight() {
// skip the boss fight for now
if(fsm.in_state(gui::State::NEXT_LEVEL)) {
if(fsm.in_state(gui::State::BOSS_FIGHT)) {
// eventually we'll have AI handle this too
send_event(gui::Event::STAIRS_DOWN);
send_event(gui::Event::BOSS_END);
face_enemy();
}
}

View file

@ -7,6 +7,7 @@ namespace boss {
UI::UI(shared_ptr<World> world, Entity boss_id) :
$world(world),
$boss_id(boss_id),
$combat_ui(true),
$boss_sprite(textures::get_sprite("test_boss")),
$player_sprite(textures::get_sprite("test_player")),
$floor_sprite(textures::get_sprite("test_floor"))
@ -30,33 +31,27 @@ namespace boss {
position_sprite($boss_sprite, "boss2", 0.7, true);
position_sprite($player_sprite, "player1", 0.5, true);
position_sprite($floor_sprite, "floor2", 1.0, false);
position_sprite($floor_sprite, "floor1", 1.0, false);
$arena.init();
$actions.position(0,0, SCREEN_WIDTH-BOSS_VIEW_WIDTH, SCREEN_HEIGHT);
$actions.layout(
"[action1|action2]"
"[action3|action4]"
"[action5|action6]"
"[*(200)dodge]"
"[*(200,400)combat|_]"
"[_|_]"
"[_|_]"
"[_|_]"
"[*(200,300)stats]"
"[_]"
"[_]");
for(auto& [name, cell] : $actions.cells()) {
auto id = $actions.entity(name);
$actions.set<Rectangle>(id, {});
$actions.set<Text>(id, {guecs::to_wstring(name)});
if(name != "stats") {
$actions.set<Clickable>(id, {
[name](auto){ fmt::println("clicked {}", name); }
});
}
}
auto stats = $actions.entity("stats");
$actions.set<Rectangle>(stats, {});
$actions.set<Text>(stats, {L"stats"});
$actions.init();
auto& cell = $actions.cell_for("combat");
$combat_ui.init(cell.x, cell.y, cell.w, cell.h);
}
void UI::position_sprite(textures::SpriteTexture& st, const std::string& name, float scale, bool at_mid) {
@ -71,6 +66,7 @@ namespace boss {
void UI::render(sf::RenderWindow& window) {
$arena.render(window);
$actions.render(window);
$combat_ui.render(window);
window.draw(*$floor_sprite.sprite);
window.draw(*$boss_sprite.sprite);
window.draw(*$player_sprite.sprite);

View file

@ -4,6 +4,7 @@
#include <SFML/Graphics/RenderWindow.hpp>
#include <guecs/ui.hpp>
#include "textures.hpp"
#include "gui/combat_ui.hpp"
namespace boss {
using std::shared_ptr;
@ -13,6 +14,7 @@ namespace boss {
struct UI {
shared_ptr<World> $world = nullptr;
Entity $boss_id = NONE;
gui::CombatUI $combat_ui;
SpriteTexture $boss_sprite;
SpriteTexture $player_sprite;
SpriteTexture $floor_sprite;

View file

@ -1,32 +1,27 @@
#include "gui/combat_ui.hpp"
#include "constants.hpp"
#include "rituals.hpp"
#include <fmt/xchar.h>
#include "gui/guecstra.hpp"
#include "inventory.hpp"
#include "game_level.hpp"
#include "gui/combat_ui.hpp"
namespace gui {
using guecs::THEME;
using namespace guecs;
CombatUI::CombatUI()
CombatUI::CombatUI(bool boss_style) :
$boss_style(boss_style)
{
$gui.position(COMBAT_UI_X, COMBAT_UI_Y, COMBAT_UI_WIDTH, COMBAT_UI_HEIGHT);
$gui.layout(
"[button_0 | button_1 | button_2 | button_3"
"|button_4 | button_5 | button_6 | =healing_button | =hp_gauge ]"
);
}
guecs::Entity CombatUI::make_button(
std::string name,
guecs::Entity button,
Events::GUI event,
int action,
const std::string &icon_name,
const std::string &sound,
const std::string &effect_name)
{
auto button = $gui.entity(name);
$gui.set<Sprite>(button, {icon_name});
$gui.set<Sound>(button, {sound});
$gui.set<Effect>(button, {.duration=0.5f, .name=effect_name});
@ -36,49 +31,68 @@ namespace gui {
return button;
}
void CombatUI::init() {
void CombatUI::init(int x, int y, int w, int h) {
$gui.position(x, y, w, h);
if($boss_style) {
$gui.layout(
"[action0|action1]"
"[action2|action3]"
"[action4|action5]"
"[action6|action7]");
} else {
$gui.layout(
"[action0 | action1 | action2 | action3"
"|action4 | action5 | action6 | action7 | =hp_gauge ]");
}
auto world = GameDB::current_world();
using guecs::THEME;
$gui.set<Background>($gui.MAIN, {$gui.$parser, THEME.DARK_MID});
auto& the_belt = world->get_the<ritual::Belt>();
for(int slot = 0; slot < the_belt.max_slots; slot++) {
fmt::println("combat ui belt slot {}", slot);
if(the_belt.has(slot)) {
std::string name = fmt::format("button_{}", slot);
auto button = $gui.entity("action", slot);
fmt::println("combat ui has belt slot {} with id {}", slot, button);
auto& ritual = the_belt.get(slot);
using enum ritual::Element;
switch(ritual.element) {
case FIRE:
make_button(name, Events::GUI::ATTACK,
make_button(button, Events::GUI::ATTACK,
slot, "broken_yoyo", "fireball_01", "flame");
break;
case LIGHTNING:
make_button(name, Events::GUI::ATTACK,
make_button(button, Events::GUI::ATTACK,
slot, "pocket_watch", "electric_shock_01", "lightning");
break;
default:
make_button(name, Events::GUI::ATTACK,
make_button(button, Events::GUI::ATTACK,
slot, "severed_finger", "punch_cartoony", "ui_shader");
}
}
}
auto hp_gauge = $gui.entity("hp_gauge");
$gui.set<Sprite>(hp_gauge, {"stone_doll_cursed"});
$gui.set<Clickable>(hp_gauge,
guecs::make_action(hp_gauge, Events::GUI::HP_STATUS, {}));
if(!$boss_style) {
auto hp_gauge = $gui.entity("hp_gauge");
$gui.set<Sprite>(hp_gauge, {"stone_doll_cursed"});
$gui.set<Clickable>(hp_gauge,
guecs::make_action(hp_gauge, Events::GUI::HP_STATUS, {}));
}
$gui.init();
}
void CombatUI::render(sf::RenderWindow& window) {
$gui.render(window);
// $gui.debug_layout(window);
}
void CombatUI::update_level() {
init();
init(COMBAT_UI_X, COMBAT_UI_Y, COMBAT_UI_WIDTH, COMBAT_UI_HEIGHT);
}
bool CombatUI::mouse(float x, float y, guecs::Modifiers mods) {

View file

@ -7,15 +7,16 @@
namespace gui {
class CombatUI {
public:
bool $boss_style;
guecs::UI $gui;
CombatUI();
CombatUI(bool boss_style);
void init();
void init(int x, int y, int w, int h);
void render(sf::RenderWindow& window);
void update_level();
bool mouse(float x, float y, guecs::Modifiers mods);
guecs::Entity make_button(std::string name, Events::GUI event,
guecs::Entity make_button(guecs::Entity button, Events::GUI event,
int action, const std::string &icon_name,
const std::string &sound, const std::string &effect_name);
};

View file

@ -20,6 +20,7 @@ namespace gui {
FSM::FSM() :
$window(sf::VideoMode({SCREEN_WIDTH, SCREEN_HEIGHT}), "Zed's Raycaster Thing"),
$main_ui($window),
$combat_ui(false),
$font{FONT_FILE_NAME},
$dnd_loot($status_ui, $loot_ui, $window, $router)
{
@ -35,7 +36,7 @@ namespace gui {
FSM_STATE(State, IDLE, ev, data);
FSM_STATE(State, IN_COMBAT, ev);
FSM_STATE(State, COMBAT_ROTATE, ev);
FSM_STATE(State, NEXT_LEVEL, ev);
FSM_STATE(State, BOSS_FIGHT, ev);
FSM_STATE(State, END, ev);
FSM_STATE(State, LOOTING, ev, data);
}
@ -50,12 +51,8 @@ namespace gui {
auto cell = $main_ui.overlay_cell("left");
$debug_ui.init(cell);
$combat_ui.init();
$combat_ui.init(COMBAT_UI_X, COMBAT_UI_Y, COMBAT_UI_WIDTH, COMBAT_UI_HEIGHT);
$status_ui.init();
$boss_fight_ui = GameDB::create_bossfight();
$boss_fight_ui->init();
$map_ui.init();
$map_ui.log(L"Welcome to the game!");
@ -169,9 +166,10 @@ namespace gui {
case CLOSE:
dbc::log("Nothing to close.");
break;
case STAIRS_DOWN:
case BOSS_START:
sound::stop("ambient");
state(State::NEXT_LEVEL);
next_level(true);
state(State::BOSS_FIGHT);
break;
case LOOT_ITEM:
$dnd_loot.event(Event::LOOT_ITEM);
@ -207,13 +205,16 @@ namespace gui {
}
}
void FSM::NEXT_LEVEL(Event ev) {
void FSM::BOSS_FIGHT(Event ev) {
dbc::check($boss_fight_ui != nullptr, "$boss_fight_ui not initialized");
using enum Event;
switch(ev) {
case STAIRS_DOWN:
// this is only if using the debug X key to skip it
case BOSS_START:
case BOSS_END:
sound::play("ambient");
next_level();
next_level(false);
state(State::IDLE);
break;
case MOUSE_CLICK: {
@ -221,7 +222,7 @@ namespace gui {
$boss_fight_ui->mouse(pos.x, pos.y, guecs::NO_MODS);
if($boss_fight_ui->boss_dead()) {
event(Event::STAIRS_DOWN);
event(Event::BOSS_END);
}
} break;
default:
@ -362,7 +363,7 @@ namespace gui {
$main_ui.toggle_mind_reading();
break;
case KEY::X:
event(Event::STAIRS_DOWN);
event(Event::BOSS_START);
break;
case KEY::F5:
take_screenshot();
@ -384,7 +385,7 @@ namespace gui {
}
void FSM::draw_gui() {
if(in_state(State::NEXT_LEVEL)) {
if(in_state(State::BOSS_FIGHT)) {
$boss_fight_ui->render($window);
} else {
if($debug_ui.active) {
@ -406,7 +407,7 @@ namespace gui {
}
void FSM::render() {
if(in_state(State::NEXT_LEVEL)) {
if(in_state(State::BOSS_FIGHT)) {
$window.clear();
$boss_fight_ui->render($window);
} else {
@ -502,14 +503,14 @@ namespace gui {
System::player_status();
break;
case eGUI::NEW_RITUAL:
$combat_ui.init();
$combat_ui.init(COMBAT_UI_X, COMBAT_UI_Y, COMBAT_UI_WIDTH, COMBAT_UI_HEIGHT);
break;
case eGUI::ATTACK:
$temp_attack_id = std::any_cast<int>(data);
event(Event::ATTACK);
break;
case eGUI::STAIRS_DOWN:
event(Event::STAIRS_DOWN);
event(Event::BOSS_START);
break;
case eGUI::DEATH: {
$status_ui.update();
@ -541,17 +542,17 @@ namespace gui {
dbc::check(worked, "Failed to write screenshot.png");
}
void FSM::next_level() {
dbc::log("current_level: Yep, next is called...");
GameDB::create_level();
$status_ui.update_level();
$combat_ui.update_level();
$main_ui.update_level();
$loot_ui.update_level();
$boss_fight_ui = GameDB::create_bossfight();
$boss_fight_ui->init();
void FSM::next_level(bool bossfight) {
if(bossfight) {
$boss_fight_ui = GameDB::create_bossfight();
$boss_fight_ui->init();
} else {
GameDB::create_level();
$status_ui.update_level();
$combat_ui.update_level();
$main_ui.update_level();
$loot_ui.update_level();
}
run_systems();
}

View file

@ -21,7 +21,7 @@ namespace gui {
COMBAT_ROTATE=3,
ATTACKING=4,
ROTATING=5,
NEXT_LEVEL=6,
BOSS_FIGHT=6,
LOOTING=7,
IDLE=8,
END=9
@ -59,7 +59,7 @@ namespace gui {
void IDLE(Event ev, std::any data);
void IN_COMBAT(Event ev);
void COMBAT_ROTATE(Event ev);
void NEXT_LEVEL(Event ev);
void BOSS_FIGHT(Event ev);
void LOOTING(Event ev, std::any data);
void END(Event ev);
@ -72,7 +72,7 @@ namespace gui {
bool active();
void run_systems();
void handle_world_events();
void next_level();
void next_level(bool bossfight);
void debug_render();
void take_screenshot();
};

View file

@ -15,19 +15,20 @@ namespace gui {
ATTACK = 10,
START_COMBAT = 11,
STOP_COMBAT = 12,
STAIRS_DOWN = 13,
LOOT_OPEN=14,
LOOT_ITEM=15,
LOOT_SELECT=16,
INV_SELECT=17,
USE_ITEM=18,
QUIT = 19,
MOUSE_CLICK=20,
MOUSE_MOVE=21,
MOUSE_DRAG=22,
MOUSE_DRAG_START=23,
MOUSE_DROP=24,
KEY_PRESS=25,
AIM_CLICK=26
BOSS_START = 13,
BOSS_END = 14,
LOOT_OPEN=15,
LOOT_ITEM=16,
LOOT_SELECT=17,
INV_SELECT=18,
USE_ITEM=19,
QUIT = 20,
MOUSE_CLICK=21,
MOUSE_MOVE=22,
MOUSE_DRAG=23,
MOUSE_DRAG_START=24,
MOUSE_DROP=25,
KEY_PRESS=26,
AIM_CLICK=27
};
}

View file

@ -1,3 +1,4 @@
#pragma once
#include "dinkyecs.hpp"
#include <unordered_map>

View file

@ -35,7 +35,7 @@ int main(int argc, char* argv[]) {
// BUG: need to sort out how to deal with this in the FSM
if(main.in_state(gui::State::IDLE)
|| main.in_state(gui::State::NEXT_LEVEL)
|| main.in_state(gui::State::BOSS_FIGHT)
|| main.in_state(gui::State::LOOTING)
|| main.in_state(gui::State::IN_COMBAT))
{

View file

@ -71,7 +71,7 @@ namespace ritual {
struct Belt {
int next_slot = 0;
int max_slots = 6;
int max_slots = 8;
std::unordered_map<int, Action> equipped;
Action& get(int index);