Refactored the GUECS system to have its own namespace then got Meter to work.

master
Zed A. Shaw 3 weeks ago
parent 9c66e870d2
commit 722d55d948
  1. 20
      combat_ui.cpp
  2. 6
      combat_ui.hpp
  3. 78
      ecs_gui.cpp
  4. 84
      ecs_gui.hpp
  5. 86
      guecs.cpp
  6. 91
      guecs.hpp
  7. 8
      gui.cpp
  8. 4
      meson.build
  9. 6
      tests/guecs.cpp

@ -3,13 +3,15 @@
#include "color.hpp"
namespace gui {
using namespace guecs;
CombatUI::CombatUI(GameLevel level) :
$level(level)
{
$gui.position(RAY_VIEW_X, RAY_VIEW_HEIGHT, RAY_VIEW_WIDTH, SCREEN_HEIGHT - RAY_VIEW_HEIGHT);
$gui.layout(
"[*%(100,150)button_attack1 | *%(100,150)button_attack2 | *%(100,150)button_attack3 | *%(100,150)button_heal]"
"[ >.%(100,50)label_hp | *%.(100,50)bar_hp | _ ]");
"[ >.%(100,50)label_hp | *%.(190,50)bar_hp | _ ]");
}
void CombatUI::render(TexturePack& textures) {
@ -21,12 +23,12 @@ namespace gui {
world.set<lel::Cell>(button, cell);
world.set<Sprite>(button, {"trash_button"});
world.set<Clickable>(button, {100});
world.set<Textual>(button, {name});
world.set<Textual>(button, {"Button"});
} else if(name.starts_with("bar_")) {
auto meter = $gui.entity(name);
world.set<lel::Cell>(meter, cell);
world.set<Rectangle>(meter, {});
world.set<Meter>(meter, {});
$meter = $gui.entity(name);
world.set<lel::Cell>($meter, cell);
world.set<Rectangle>($meter, {});
world.set<Meter>($meter, {});
} else {
// ignored, it's just space
$gui.entity(name);
@ -39,4 +41,10 @@ namespace gui {
void CombatUI::draw(sf::RenderWindow& window) {
$gui.render(window);
}
void CombatUI::set_damage(float percent) {
fmt::println("combat ui sets damage: {}", percent);
auto& meter = $gui.world().get<Meter>($meter);
meter.percent = percent;
}
}

@ -3,12 +3,13 @@
#include "levelmanager.hpp"
#include <SFML/Graphics/RenderWindow.hpp>
#include <SFML/Graphics/Font.hpp>
#include "ecs_gui.hpp"
#include "guecs.hpp"
namespace gui {
class CombatUI {
public:
GUECS $gui;
guecs::UI $gui;
DinkyECS::Entity $meter;
GameLevel $level;
CombatUI(GameLevel level);
@ -17,5 +18,6 @@ namespace gui {
void draw(sf::RenderWindow& window);
void update_level(GameLevel &level) { $level = level; }
void click(int x, int y);
void set_damage(float percent);
};
}

@ -1,78 +0,0 @@
#include "ecs_gui.hpp"
#include "constants.hpp"
GUECS::GUECS() {
$font = make_shared<sf::Font>(FONT_FILE_NAME);
}
void GUECS::position(int x, int y, int width, int height) {
$parser.position(x, y, width, height);
}
void GUECS::layout(std::string grid) {
$grid = grid;
$parser.parse($grid);
}
DinkyECS::Entity GUECS::entity(std::string name) {
auto entity = $world.entity();
$world.set<CellName>(entity, {name});
return entity;
}
void GUECS::init(TexturePack& textures) {
$world.query<lel::Cell, Rectangle>([](auto, auto& cell, auto& rect) {
rect.init(cell);
});
$world.query<lel::Cell, Textual>([this](auto, auto& cell, auto& text) {
text.init(cell, $font);
});
$world.query<lel::Cell, Sprite>([&](auto, auto &cell, auto &sprite) {
auto sprite_texture = textures.get(sprite.name);
sprite.texture = sprite_texture.texture;
sprite.sprite = make_shared<sf::Sprite>(*sprite.texture);
sprite.sprite->setPosition({float(cell.x + 5), float(cell.y + 5)});
auto size = sprite.texture->getSize();
sprite.sprite->setScale({float(cell.w - 10) / size.x, float(cell.h - 10) / size.y});
});
}
void GUECS::render(sf::RenderWindow& window) {
$world.query<lel::Cell, Meter>([&](auto ent, auto& cell, const auto &meter) {
if($world.has<Rectangle>(ent)) {
float level = meter.percent * float(cell.w);
auto& target = $world.get<Rectangle>(ent);
// ZED: this 6 is a border width, make it a thing
target.shape->setSize({std::max(level, 0.0f), float(cell.h - 6)});
}
});
$world.query<Rectangle>([&](auto, auto& rect) {
window.draw(*rect.shape);
});
$world.query<Sprite>([&](auto, auto& sprite) {
window.draw(*sprite.sprite);
});
$world.query<Textual>([&](auto, auto& text) {
window.draw(*text.text);
});
}
void GUECS::mouse(sf::RenderWindow &window) {
if(sf::Mouse::isButtonPressed(sf::Mouse::Button::Left)) {
sf::Vector2f pos = window.mapPixelToCoords(sf::Mouse::getPosition(window));
$world.query<lel::Cell, Clickable>([&](auto ent, auto& cell, auto &clicked) {
if((pos.x >= cell.x && pos.x <= cell.x + cell.w) &&
(pos.y >= cell.y && pos.y <= cell.y + cell.h))
{
auto& cn = $world.get<CellName>(ent);
fmt::println("clicked on entity {} with name {} and event {}",
ent, cn.name, clicked.event);
}
});
}
}

@ -1,84 +0,0 @@
#pragma once
#include "color.hpp"
#include "dinkyecs.hpp"
#include "lel.hpp"
#include <string>
#include <memory>
#include <SFML/Graphics.hpp>
#include "texture.hpp"
using std::shared_ptr, std::make_shared;
struct Textual {
std::string label;
shared_ptr<sf::Font> font = nullptr;
shared_ptr<sf::Text> text = nullptr;
void init(lel::Cell &cell, shared_ptr<sf::Font> font_ptr) {
font = font_ptr;
text = make_shared<sf::Text>(*font, label);
auto bounds = text->getLocalBounds();
auto text_cell = lel::center(bounds.size.x, bounds.size.y, cell);
// this stupid / 2 is because SFML renders from baseline rather than from the claimed bounding box
text->setPosition({float(text_cell.x), float(text_cell.y) - text_cell.h / 2});
}
};
struct Clickable {
int event = 0;
};
struct Sprite {
std::string name;
std::shared_ptr<sf::Sprite> sprite = nullptr;
std::shared_ptr<sf::Texture> texture = nullptr;
};
struct Rectangle {
shared_ptr<sf::RectangleShape> shape = nullptr;
void init(lel::Cell& cell) {
sf::Vector2f size{(float)cell.w, (float)cell.h};
if(shape == nullptr) shape = make_shared<sf::RectangleShape>(size);
shape->setPosition({float(cell.x + 3), float(cell.y + 3)});
shape->setSize({float(cell.w - 6), float(cell.h - 6)});
shape->setFillColor(ColorValue::DARK_MID);
shape->setOutlineColor(ColorValue::MID);
shape->setOutlineThickness(1);
}
};
struct Meter {
float percent = 100.0;
};
struct CellName {
std::string name;
};
class GUECS {
public:
DinkyECS::World $world;
shared_ptr<sf::Font> $font = nullptr;
lel::Parser $parser;
std::string $grid = "";
GUECS();
void position(int x, int y, int width, int height);
void layout(std::string grid);
DinkyECS::Entity entity(std::string name);
inline lel::CellMap& cells() {
return $parser.cells;
}
inline DinkyECS::World& world() {
return $world;
}
void init(TexturePack& textures);
void render(sf::RenderWindow& window);
void mouse(sf::RenderWindow &window);
};

@ -0,0 +1,86 @@
#include "guecs.hpp"
#include "constants.hpp"
namespace guecs {
UI::UI() {
$font = make_shared<sf::Font>(FONT_FILE_NAME);
}
void UI::position(int x, int y, int width, int height) {
$parser.position(x, y, width, height);
}
void UI::layout(std::string grid) {
$grid = grid;
$parser.parse($grid);
}
DinkyECS::Entity UI::entity(std::string name) {
auto entity = $world.entity();
$world.set<CellName>(entity, {name});
return entity;
}
void UI::init(TexturePack& textures) {
$world.query<lel::Cell, Rectangle>([](auto, auto& cell, auto& rect) {
rect.init(cell);
});
$world.query<Rectangle, Meter>([](auto, auto& bg, auto &) {
bg.shape->setFillColor(ColorValue::BLACK);
});
$world.query<lel::Cell, Meter>([](auto, auto &cell, auto& meter) {
meter.init(cell);
});
$world.query<lel::Cell, Textual>([this](auto, auto& cell, auto& text) {
text.init(cell, $font);
});
$world.query<lel::Cell, Sprite>([&](auto, auto &cell, auto &sprite) {
auto sprite_texture = textures.get(sprite.name);
sprite.texture = sprite_texture.texture;
sprite.sprite = make_shared<sf::Sprite>(*sprite.texture);
sprite.sprite->setPosition({float(cell.x + 5), float(cell.y + 5)});
auto size = sprite.texture->getSize();
sprite.sprite->setScale({float(cell.w - 10) / size.x, float(cell.h - 10) / size.y});
});
}
void UI::render(sf::RenderWindow& window) {
$world.query<Rectangle>([&](auto, auto& rect) {
window.draw(*rect.shape);
});
$world.query<lel::Cell, Meter>([&](auto, auto& cell, const auto &meter) {
float level = std::clamp(meter.percent, 0.0f, 1.0f) * float(cell.w);
// ZED: this 6 is a border width, make it a thing
meter.bar.shape->setSize({std::max(level, 0.0f), float(cell.h - 6)});
window.draw(*meter.bar.shape);
});
$world.query<Sprite>([&](auto, auto& sprite) {
window.draw(*sprite.sprite);
});
$world.query<Textual>([&](auto, auto& text) {
window.draw(*text.text);
});
}
void UI::mouse(sf::RenderWindow &window) {
if(sf::Mouse::isButtonPressed(sf::Mouse::Button::Left)) {
sf::Vector2f pos = window.mapPixelToCoords(sf::Mouse::getPosition(window));
$world.query<lel::Cell, Clickable>([&](auto ent, auto& cell, auto &clicked) {
if((pos.x >= cell.x && pos.x <= cell.x + cell.w) &&
(pos.y >= cell.y && pos.y <= cell.y + cell.h))
{
auto& cn = $world.get<CellName>(ent);
fmt::println("clicked on entity {} with name {} and event {}",
ent, cn.name, clicked.event);
}
});
}
}
}

@ -0,0 +1,91 @@
#pragma once
#include "color.hpp"
#include "dinkyecs.hpp"
#include "lel.hpp"
#include <string>
#include <memory>
#include <SFML/Graphics.hpp>
#include "texture.hpp"
namespace guecs {
using std::shared_ptr, std::make_shared;
struct Textual {
std::string label;
shared_ptr<sf::Font> font = nullptr;
shared_ptr<sf::Text> text = nullptr;
void init(lel::Cell &cell, shared_ptr<sf::Font> font_ptr) {
font = font_ptr;
text = make_shared<sf::Text>(*font, label);
auto bounds = text->getLocalBounds();
auto text_cell = lel::center(bounds.size.x, bounds.size.y, cell);
// this stupid / 2 is because SFML renders from baseline rather than from the claimed bounding box
text->setPosition({float(text_cell.x), float(text_cell.y) - text_cell.h / 2});
}
};
struct Clickable {
int event = 0;
};
struct Sprite {
std::string name;
std::shared_ptr<sf::Sprite> sprite = nullptr;
std::shared_ptr<sf::Texture> texture = nullptr;
};
struct Rectangle {
shared_ptr<sf::RectangleShape> shape = nullptr;
void init(lel::Cell& cell) {
sf::Vector2f size{(float)cell.w, (float)cell.h};
if(shape == nullptr) shape = make_shared<sf::RectangleShape>(size);
shape->setPosition({float(cell.x + 3), float(cell.y + 3)});
shape->setSize({float(cell.w - 6), float(cell.h - 6)});
shape->setFillColor(ColorValue::DARK_MID);
shape->setOutlineColor(ColorValue::MID);
shape->setOutlineThickness(1);
}
};
struct Meter {
float percent = 1.0f;
Rectangle bar;
void init(lel::Cell& cell) {
bar.init(cell);
}
};
struct CellName {
std::string name;
};
class UI {
public:
DinkyECS::World $world;
shared_ptr<sf::Font> $font = nullptr;
lel::Parser $parser;
std::string $grid = "";
UI();
void position(int x, int y, int width, int height);
void layout(std::string grid);
DinkyECS::Entity entity(std::string name);
inline lel::CellMap& cells() {
return $parser.cells;
}
inline DinkyECS::World& world() {
return $world;
}
void init(TexturePack& textures);
void render(sf::RenderWindow& window);
void mouse(sf::RenderWindow &window);
};
}

@ -159,7 +159,7 @@ namespace gui {
state(State::MAPPING);
break;
case ATTACK:
fmt::println("ATTACK IGNORED");
state(State::ATTACKING);
break;
case START_COMBAT:
state(State::IN_COMBAT);
@ -269,6 +269,7 @@ namespace gui {
auto player = $level.world->get_the<Player>();
auto& player_combat = $level.world->get<Combat>(player.entity);
player_combat.hp = player_combat.max_hp;
$combat_view.set_damage(float(player_combat.hp) / float(player_combat.max_hp));
} break;
default:
break; // ignored
@ -398,6 +399,9 @@ namespace gui {
if(damage.enemy_did > 0) {
$status_view.log(fmt::format("Enemy HIT YOU for {} damage!", damage.enemy_did));
$status_view.log(fmt::format("-- Enemy has {} HP left.", enemy_combat.hp));
auto player = world.get_the<Player>();
auto player_combat = world.get<Combat>(player.entity);
$combat_view.set_damage(float(player_combat.hp) / float(player_combat.max_hp));
} else {
$status_view.log("Enemy MISSED YOU.");
}
@ -407,8 +411,6 @@ namespace gui {
} else {
$status_view.log("You MISSED the enemy.");
}
// this is where we tell the combat ui about the damage
}
break;
case eGUI::COMBAT_START:

@ -61,7 +61,7 @@ sources = [
'config.cpp',
'dbc.cpp',
'devices.cpp',
'ecs_gui.cpp',
'guecs.cpp',
'gui.cpp',
'inventory.cpp',
'lel.cpp',
@ -92,7 +92,7 @@ executable('runtests', sources + [
'tests/base.cpp',
'tests/dbc.cpp',
'tests/dinkyecs.cpp',
'tests/ecs_gui.cpp',
'tests/guecs.cpp',
'tests/fsm.cpp',
'tests/inventory.cpp',
'tests/lel.cpp',

@ -1,12 +1,14 @@
#include <catch2/catch_test_macros.hpp>
#include <fmt/core.h>
#include "constants.hpp"
#include "ecs_gui.hpp"
#include "guecs.hpp"
#include "texture.hpp"
using namespace guecs;
TEST_CASE("prototype one gui", "[ecs-gui]") {
GUECS gui;
guecs::UI gui;
TexturePack textures;
textures.load_sprites();
Loading…
Cancel
Save