Started working on this 'arena tester' tool that would let me load an enemy and test them, but then realized I could just make it so I can spawn enemies in the game. I'm keeping the arena around as it will be useful later as a scriptable testing tool, but for now just spawn and test.
This commit is contained in:
parent
b6c1eba1b3
commit
4f090159ab
14 changed files with 524 additions and 58 deletions
|
@ -13,6 +13,4 @@ namespace components {
|
||||||
|
|
||||||
return my_dmg;
|
return my_dmg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
64
guecs.cpp
64
guecs.cpp
|
@ -1,6 +1,70 @@
|
||||||
#include "guecs.hpp"
|
#include "guecs.hpp"
|
||||||
|
|
||||||
namespace guecs {
|
namespace guecs {
|
||||||
|
|
||||||
|
void Textual::init(lel::Cell &cell, shared_ptr<sf::Font> font_ptr) {
|
||||||
|
dbc::check(font_ptr != nullptr, "you failed to initialize this WideText");
|
||||||
|
if(font == nullptr) font = font_ptr;
|
||||||
|
if(text == nullptr) text = make_shared<sf::Text>(*font, content, size);
|
||||||
|
text->setFillColor(color);
|
||||||
|
|
||||||
|
if(centered) {
|
||||||
|
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});
|
||||||
|
} else {
|
||||||
|
text->setPosition({float(cell.x + padding * 2), float(cell.y + padding * 2)});
|
||||||
|
}
|
||||||
|
|
||||||
|
text->setCharacterSize(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Textual::update(std::wstring& new_content) {
|
||||||
|
content = new_content;
|
||||||
|
text->setString(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Sprite::init(lel::Cell &cell) {
|
||||||
|
auto sprite_texture = textures::get(name);
|
||||||
|
texture = sprite_texture.texture;
|
||||||
|
sprite = make_shared<sf::Sprite>(*texture);
|
||||||
|
|
||||||
|
sprite->setPosition({
|
||||||
|
float(cell.x + padding),
|
||||||
|
float(cell.y + padding)});
|
||||||
|
|
||||||
|
auto bounds = sprite->getGlobalBounds();
|
||||||
|
|
||||||
|
sprite->setScale({
|
||||||
|
float(cell.w - padding * 2) / bounds.size.x,
|
||||||
|
float(cell.h - padding * 2) / bounds.size.y});
|
||||||
|
}
|
||||||
|
|
||||||
|
void Rectangle::init(lel::Cell& cell) {
|
||||||
|
sf::Vector2f size{float(cell.w) - padding * 2, float(cell.h) - padding * 2};
|
||||||
|
if(shape == nullptr) shape = make_shared<sf::RectangleShape>(size);
|
||||||
|
shape->setPosition({float(cell.x + padding), float(cell.y + padding)});
|
||||||
|
shape->setFillColor(color);
|
||||||
|
shape->setOutlineColor(border_color);
|
||||||
|
shape->setOutlineThickness(border_px);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Meter::init(lel::Cell& cell) {
|
||||||
|
bar.init(cell);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Background::init() {
|
||||||
|
sf::Vector2f size{float(w), float(h)};
|
||||||
|
if(shape == nullptr) shape = make_shared<sf::RectangleShape>(size);
|
||||||
|
shape->setPosition({float(x), float(y)});
|
||||||
|
shape->setFillColor(color);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
UI::UI() {
|
UI::UI() {
|
||||||
$font = make_shared<sf::Font>(FONT_FILE_NAME);
|
$font = make_shared<sf::Font>(FONT_FILE_NAME);
|
||||||
}
|
}
|
||||||
|
|
61
guecs.hpp
61
guecs.hpp
|
@ -24,33 +24,11 @@ namespace guecs {
|
||||||
shared_ptr<sf::Font> font = nullptr;
|
shared_ptr<sf::Font> font = nullptr;
|
||||||
shared_ptr<sf::Text> text = nullptr;
|
shared_ptr<sf::Text> text = nullptr;
|
||||||
|
|
||||||
void init(lel::Cell &cell, shared_ptr<sf::Font> font_ptr) {
|
void init(lel::Cell &cell, shared_ptr<sf::Font> font_ptr);
|
||||||
dbc::check(font_ptr != nullptr, "you failed to initialize this WideText");
|
void update(std::wstring& new_content);
|
||||||
if(font == nullptr) font = font_ptr;
|
|
||||||
if(text == nullptr) text = make_shared<sf::Text>(*font, content, size);
|
|
||||||
text->setFillColor(color);
|
|
||||||
|
|
||||||
if(centered) {
|
|
||||||
dbc::log("TEXTUAL IS CENTERED");
|
|
||||||
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});
|
|
||||||
} else {
|
|
||||||
text->setPosition({float(cell.x + padding * 2), float(cell.y + padding * 2)});
|
|
||||||
}
|
|
||||||
|
|
||||||
text->setCharacterSize(size);
|
|
||||||
}
|
|
||||||
|
|
||||||
void update(std::wstring& new_content) {
|
|
||||||
content = new_content;
|
|
||||||
text->setString(content);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Label : public Textual {
|
struct Label : public Textual {
|
||||||
|
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
Label(Args... args) : Textual(args...)
|
Label(Args... args) : Textual(args...)
|
||||||
{
|
{
|
||||||
|
@ -75,19 +53,7 @@ namespace guecs {
|
||||||
std::shared_ptr<sf::Sprite> sprite = nullptr;
|
std::shared_ptr<sf::Sprite> sprite = nullptr;
|
||||||
std::shared_ptr<sf::Texture> texture = nullptr;
|
std::shared_ptr<sf::Texture> texture = nullptr;
|
||||||
|
|
||||||
void init(lel::Cell &cell) {
|
void init(lel::Cell &cell);
|
||||||
auto sprite_texture = textures::get(name);
|
|
||||||
texture = sprite_texture.texture;
|
|
||||||
sprite = make_shared<sf::Sprite>(*texture);
|
|
||||||
sprite->setPosition({
|
|
||||||
float(cell.x + padding),
|
|
||||||
float(cell.y + padding)});
|
|
||||||
|
|
||||||
auto size = texture->getSize();
|
|
||||||
sprite->setScale({
|
|
||||||
float(cell.w - padding * 2) / size.x,
|
|
||||||
float(cell.h - padding * 2) / size.y});
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Rectangle {
|
struct Rectangle {
|
||||||
|
@ -97,23 +63,14 @@ namespace guecs {
|
||||||
int border_px = GUECS_BORDER_PX;
|
int border_px = GUECS_BORDER_PX;
|
||||||
shared_ptr<sf::RectangleShape> shape = nullptr;
|
shared_ptr<sf::RectangleShape> shape = nullptr;
|
||||||
|
|
||||||
void init(lel::Cell& cell) {
|
void init(lel::Cell& cell);
|
||||||
sf::Vector2f size{float(cell.w) - padding * 2, float(cell.h) - padding * 2};
|
|
||||||
if(shape == nullptr) shape = make_shared<sf::RectangleShape>(size);
|
|
||||||
shape->setPosition({float(cell.x + padding), float(cell.y + padding)});
|
|
||||||
shape->setFillColor(color);
|
|
||||||
shape->setOutlineColor(border_color);
|
|
||||||
shape->setOutlineThickness(border_px);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Meter {
|
struct Meter {
|
||||||
float percent = 1.0f;
|
float percent = 1.0f;
|
||||||
Rectangle bar;
|
Rectangle bar;
|
||||||
|
|
||||||
void init(lel::Cell& cell) {
|
void init(lel::Cell& cell);
|
||||||
bar.init(cell);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ActionData {
|
struct ActionData {
|
||||||
|
@ -130,7 +87,6 @@ namespace guecs {
|
||||||
float w = 0.0f;
|
float w = 0.0f;
|
||||||
float h = 0.0f;
|
float h = 0.0f;
|
||||||
sf::Color color = GUECS_BG_COLOR;
|
sf::Color color = GUECS_BG_COLOR;
|
||||||
|
|
||||||
shared_ptr<sf::RectangleShape> shape = nullptr;
|
shared_ptr<sf::RectangleShape> shape = nullptr;
|
||||||
|
|
||||||
Background(lel::Parser& parser, sf::Color bg_color=GUECS_BG_COLOR) :
|
Background(lel::Parser& parser, sf::Color bg_color=GUECS_BG_COLOR) :
|
||||||
|
@ -143,12 +99,7 @@ namespace guecs {
|
||||||
|
|
||||||
Background() {}
|
Background() {}
|
||||||
|
|
||||||
void init() {
|
void init();
|
||||||
sf::Vector2f size{float(w), float(h)};
|
|
||||||
if(shape == nullptr) shape = make_shared<sf::RectangleShape>(size);
|
|
||||||
shape->setPosition({float(x), float(y)});
|
|
||||||
shape->setFillColor(color);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class UI {
|
class UI {
|
||||||
|
|
|
@ -282,6 +282,11 @@ namespace gui {
|
||||||
case KEY::O:
|
case KEY::O:
|
||||||
autowalking = true;
|
autowalking = true;
|
||||||
break;
|
break;
|
||||||
|
case KEY::Equal:
|
||||||
|
$levels.spawn_enemy("KNIGHT");
|
||||||
|
$main_ui.update_level($level);
|
||||||
|
run_systems();
|
||||||
|
break;
|
||||||
case KEY::L:
|
case KEY::L:
|
||||||
event(Event::STAIRS_DOWN);
|
event(Event::STAIRS_DOWN);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -51,6 +51,36 @@ shared_ptr<gui::BossFightUI> LevelManager::create_bossfight(shared_ptr<DinkyECS:
|
||||||
return make_shared<gui::BossFightUI>(world, boss_id);
|
return make_shared<gui::BossFightUI>(world, boss_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DinkyECS::Entity LevelManager::spawn_enemy(std::string named) {
|
||||||
|
(void)named;
|
||||||
|
auto& level = current();
|
||||||
|
|
||||||
|
auto &config = level.world->get_the<GameConfig>();
|
||||||
|
auto entity_data = config.enemies[named];
|
||||||
|
|
||||||
|
WorldBuilder builder(*level.map, $components);
|
||||||
|
|
||||||
|
auto entity_id = builder.configure_entity_in_map(*level.world, entity_data, 0);
|
||||||
|
|
||||||
|
auto& entity_pos = level.world->get<Position>(entity_id);
|
||||||
|
auto player_pos = level.world->get<Position>(level.player);
|
||||||
|
|
||||||
|
for(matrix::box it{level.map->walls(),
|
||||||
|
player_pos.location.x, player_pos.location.y, 1}; it.next();)
|
||||||
|
{
|
||||||
|
if(level.map->can_move({it.x, it.y})) {
|
||||||
|
// this is where we move it closer to the player
|
||||||
|
entity_pos.location.x = it.x;
|
||||||
|
entity_pos.location.y = it.y;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
level.collision->insert(entity_pos.location, entity_id);
|
||||||
|
|
||||||
|
return entity_id;
|
||||||
|
}
|
||||||
|
|
||||||
size_t LevelManager::create_level(shared_ptr<DinkyECS::World> prev_world) {
|
size_t LevelManager::create_level(shared_ptr<DinkyECS::World> prev_world) {
|
||||||
auto world = clone_load_world(prev_world);
|
auto world = clone_load_world(prev_world);
|
||||||
auto scaling = scale_level();
|
auto scaling = scale_level();
|
||||||
|
|
|
@ -41,4 +41,6 @@ class LevelManager {
|
||||||
size_t current_index() { return $current_level; }
|
size_t current_index() { return $current_level; }
|
||||||
GameLevel &get(size_t index);
|
GameLevel &get(size_t index);
|
||||||
LevelScaling scale_level();
|
LevelScaling scale_level();
|
||||||
|
|
||||||
|
DinkyECS::Entity spawn_enemy(std::string named);
|
||||||
};
|
};
|
||||||
|
|
11
meson.build
11
meson.build
|
@ -161,3 +161,14 @@ executable('zedcaster',
|
||||||
link_args: link_args,
|
link_args: link_args,
|
||||||
override_options: exe_defaults,
|
override_options: exe_defaults,
|
||||||
dependencies: dependencies)
|
dependencies: dependencies)
|
||||||
|
|
||||||
|
|
||||||
|
executable('arena',
|
||||||
|
sources + [
|
||||||
|
'tools/arena.cpp',
|
||||||
|
'tools/arena_ui.cpp',
|
||||||
|
'tools/arena_fsm.cpp' ],
|
||||||
|
cpp_args: cpp_args,
|
||||||
|
link_args: link_args,
|
||||||
|
override_options: exe_defaults,
|
||||||
|
dependencies: dependencies)
|
||||||
|
|
11
textures.cpp
11
textures.cpp
|
@ -52,9 +52,18 @@ namespace textures {
|
||||||
}
|
}
|
||||||
|
|
||||||
SpriteTexture get(std::string name) {
|
SpriteTexture get(std::string name) {
|
||||||
|
dbc::check(initialized, "you forgot to call textures::init()");
|
||||||
dbc::check(TMGR.sprite_textures.contains(name),
|
dbc::check(TMGR.sprite_textures.contains(name),
|
||||||
fmt::format("!!!!! texture pack does not contain {} sprite", name));
|
fmt::format("!!!!! texture pack does not contain {} sprite", name));
|
||||||
return TMGR.sprite_textures.at(name);
|
|
||||||
|
auto result = TMGR.sprite_textures.at(name);
|
||||||
|
|
||||||
|
dbc::check(result.sprite != nullptr,
|
||||||
|
fmt::format("bad sprite from textures::get named {}", name));
|
||||||
|
dbc::check(result.texture != nullptr,
|
||||||
|
fmt::format("bad texture from textures::get named {}", name));
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
sf::Image load_image(std::string filename) {
|
sf::Image load_image(std::string filename) {
|
||||||
|
|
45
tools/arena.cpp
Normal file
45
tools/arena.cpp
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
#include "arena_fsm.hpp"
|
||||||
|
#include "textures.hpp"
|
||||||
|
#include "sound.hpp"
|
||||||
|
#include "ai.hpp"
|
||||||
|
#include "animation.hpp"
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
int main(int argc, char* argv[]) {
|
||||||
|
try {
|
||||||
|
dbc::check(argc == 2, "USAGE: arena enemy_name");
|
||||||
|
std::string enemy_name{argv[1]};
|
||||||
|
|
||||||
|
textures::init();
|
||||||
|
sound::init();
|
||||||
|
ai::init("assets/ai.json");
|
||||||
|
animation::init();
|
||||||
|
|
||||||
|
sound::mute(false);
|
||||||
|
sound::play("ambient_1", true);
|
||||||
|
|
||||||
|
arena::FSM main(enemy_name);
|
||||||
|
|
||||||
|
main.event(arena::Event::STARTED);
|
||||||
|
|
||||||
|
while(main.active()) {
|
||||||
|
main.render();
|
||||||
|
|
||||||
|
// ZED: need to sort out how to deal with this in the FSM
|
||||||
|
if(main.in_state(arena::State::IDLE)) {
|
||||||
|
main.event(arena::Event::TICK);
|
||||||
|
}
|
||||||
|
|
||||||
|
main.keyboard_mouse();
|
||||||
|
|
||||||
|
main.handle_world_events();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
} catch(const std::system_error& e) {
|
||||||
|
std::cout << "WARNING: On OSX you'll get this error on shutdown.\n";
|
||||||
|
std::cout << "Caught system_error with code "
|
||||||
|
"[" << e.code() << "] meaning "
|
||||||
|
"[" << e.what() << "]\n";
|
||||||
|
}
|
||||||
|
}
|
120
tools/arena_fsm.cpp
Normal file
120
tools/arena_fsm.cpp
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
#include "gui_fsm.hpp"
|
||||||
|
#include <iostream>
|
||||||
|
#include <chrono>
|
||||||
|
#include <numeric>
|
||||||
|
#include <functional>
|
||||||
|
#include "components.hpp"
|
||||||
|
#include <numbers>
|
||||||
|
#include "systems.hpp"
|
||||||
|
#include "events.hpp"
|
||||||
|
#include "sound.hpp"
|
||||||
|
#include <fmt/xchar.h>
|
||||||
|
#include "arena_fsm.hpp"
|
||||||
|
|
||||||
|
namespace arena {
|
||||||
|
using namespace components;
|
||||||
|
|
||||||
|
FSM::FSM(std::string enemy_name) :
|
||||||
|
$enemy_name(enemy_name),
|
||||||
|
$window(sf::VideoMode({SCREEN_WIDTH, SCREEN_HEIGHT}), "Arena Battle Tester"),
|
||||||
|
$font{FONT_FILE_NAME}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void FSM::event(Event ev) {
|
||||||
|
switch($state) {
|
||||||
|
FSM_STATE(State, START, ev);
|
||||||
|
FSM_STATE(State, IDLE, ev);
|
||||||
|
FSM_STATE(State, END, ev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FSM::START(Event ) {
|
||||||
|
run_systems();
|
||||||
|
$level = $level_mgr.current();
|
||||||
|
|
||||||
|
auto entity_id = $level_mgr.spawn_enemy($enemy_name);
|
||||||
|
|
||||||
|
$arena_ui = make_shared<ArenaUI>($level.world, entity_id);
|
||||||
|
$arena_ui->init();
|
||||||
|
state(State::IDLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FSM::END(Event ev) {
|
||||||
|
dbc::log(fmt::format("END: received event after done: {}", int(ev)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void FSM::IDLE(Event ev) {
|
||||||
|
using enum Event;
|
||||||
|
|
||||||
|
switch(ev) {
|
||||||
|
case QUIT:
|
||||||
|
$window.close();
|
||||||
|
state(State::END);
|
||||||
|
return; // done
|
||||||
|
case CLOSE:
|
||||||
|
dbc::log("Nothing to close.");
|
||||||
|
break;
|
||||||
|
case TICK:
|
||||||
|
// do nothing
|
||||||
|
break;
|
||||||
|
case ATTACK:
|
||||||
|
dbc::log("ATTACK!");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
dbc::sentinel("unhandled event in IDLE");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FSM::keyboard_mouse() {
|
||||||
|
while(const auto ev = $window.pollEvent()) {
|
||||||
|
if(ev->is<sf::Event::Closed>()) {
|
||||||
|
event(Event::QUIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(const auto* mouse = ev->getIf<sf::Event::MouseButtonPressed>()) {
|
||||||
|
if(mouse->button == sf::Mouse::Button::Left) {
|
||||||
|
sf::Vector2f pos = $window.mapPixelToCoords(mouse->position);
|
||||||
|
(void)pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(const auto* key = ev->getIf<sf::Event::KeyPressed>()) {
|
||||||
|
using KEY = sf::Keyboard::Scan;
|
||||||
|
|
||||||
|
switch(key->scancode) {
|
||||||
|
case KEY::Escape:
|
||||||
|
event(Event::CLOSE);
|
||||||
|
break;
|
||||||
|
case KEY::Space:
|
||||||
|
event(Event::ATTACK);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break; // ignored
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FSM::draw_gui() {
|
||||||
|
if($arena_ui != nullptr) {
|
||||||
|
$arena_ui->render($window);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FSM::render() {
|
||||||
|
$window.clear();
|
||||||
|
draw_gui();
|
||||||
|
$window.display();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FSM::run_systems() {
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FSM::active() {
|
||||||
|
return !in_state(State::END);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FSM::handle_world_events() {
|
||||||
|
}
|
||||||
|
}
|
52
tools/arena_fsm.hpp
Normal file
52
tools/arena_fsm.hpp
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
#pragma once
|
||||||
|
#include "constants.hpp"
|
||||||
|
#include "stats.hpp"
|
||||||
|
#include "levelmanager.hpp"
|
||||||
|
#include "fsm.hpp"
|
||||||
|
#include "main_ui.hpp"
|
||||||
|
#include "combat_ui.hpp"
|
||||||
|
#include "status_ui.hpp"
|
||||||
|
#include "arena_ui.hpp"
|
||||||
|
#include "map_view.hpp"
|
||||||
|
#include "mini_map.hpp"
|
||||||
|
|
||||||
|
namespace arena {
|
||||||
|
enum class State {
|
||||||
|
START,
|
||||||
|
IDLE,
|
||||||
|
END
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class Event {
|
||||||
|
STARTED=0,
|
||||||
|
TICK=1,
|
||||||
|
CLOSE = 7,
|
||||||
|
ATTACK = 10,
|
||||||
|
QUIT = 14
|
||||||
|
};
|
||||||
|
|
||||||
|
class FSM : public DeadSimpleFSM<State, Event> {
|
||||||
|
public:
|
||||||
|
std::string $enemy_name;
|
||||||
|
sf::RenderWindow $window;
|
||||||
|
sf::Font $font;
|
||||||
|
LevelManager $level_mgr;
|
||||||
|
GameLevel $level;
|
||||||
|
shared_ptr<arena::ArenaUI> $arena_ui = nullptr;
|
||||||
|
|
||||||
|
FSM(std::string enemy_name);
|
||||||
|
|
||||||
|
void event(Event ev);
|
||||||
|
void START(Event );
|
||||||
|
void IDLE(Event ev);
|
||||||
|
void END(Event ev);
|
||||||
|
|
||||||
|
void try_move(int dir, bool strafe);
|
||||||
|
void keyboard_mouse();
|
||||||
|
void draw_gui();
|
||||||
|
void render();
|
||||||
|
bool active();
|
||||||
|
void run_systems();
|
||||||
|
void handle_world_events();
|
||||||
|
};
|
||||||
|
}
|
133
tools/arena_ui.cpp
Normal file
133
tools/arena_ui.cpp
Normal file
|
@ -0,0 +1,133 @@
|
||||||
|
#include "arena_ui.hpp"
|
||||||
|
#include "easings.hpp"
|
||||||
|
#include "sound.hpp"
|
||||||
|
#include <fmt/xchar.h>
|
||||||
|
|
||||||
|
namespace arena {
|
||||||
|
using namespace guecs;
|
||||||
|
|
||||||
|
ArenaUI::ArenaUI(shared_ptr<DinkyECS::World> world, DinkyECS::Entity entity_id)
|
||||||
|
: $world(world),
|
||||||
|
$entity_id(entity_id),
|
||||||
|
$config(world->get_the<components::GameConfig>())
|
||||||
|
{
|
||||||
|
$status.position(0, 0, BOSS_VIEW_X, SCREEN_HEIGHT);
|
||||||
|
$status.layout(
|
||||||
|
"[main_status]"
|
||||||
|
"[(150)status_3|(150)status_4]"
|
||||||
|
"[(150)status_5|(150)status_6]"
|
||||||
|
"[(150)status_7|(150)status_8]");
|
||||||
|
|
||||||
|
$overlay.position(BOSS_VIEW_X, BOSS_VIEW_Y,
|
||||||
|
BOSS_VIEW_WIDTH, BOSS_VIEW_HEIGHT);
|
||||||
|
|
||||||
|
$overlay.layout("[_|=*%(200)enemy|_|_]");
|
||||||
|
|
||||||
|
$sounds = $world->get<components::Sound>($entity_id);
|
||||||
|
$combat = $world->get<components::Combat>($entity_id);
|
||||||
|
$sprite_config = $world->get<components::Sprite>($entity_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ArenaUI::configure_sprite() {
|
||||||
|
$animation = $world->get<components::Animation>($entity_id);
|
||||||
|
$animation.frame_width = $sprite_config.width;
|
||||||
|
|
||||||
|
auto enemy_id = $overlay.entity("enemy");
|
||||||
|
auto& enemy_image = $overlay.get<Sprite>(enemy_id);
|
||||||
|
|
||||||
|
sf::IntRect frame_rect{{0,0},{$sprite_config.width, $sprite_config.height}};
|
||||||
|
enemy_image.sprite->setTextureRect(frame_rect);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ArenaUI::configure_background() {
|
||||||
|
if($world->has<components::BossFight>($entity_id)) {
|
||||||
|
auto& boss = $world->get<components::BossFight>($entity_id);
|
||||||
|
|
||||||
|
$entity_background = textures::get(boss.background);
|
||||||
|
$entity_background.sprite->setPosition({BOSS_VIEW_X, BOSS_VIEW_Y});
|
||||||
|
$status.world().set_the<Background>({$status.$parser});
|
||||||
|
|
||||||
|
$entity_has_stage = true;
|
||||||
|
|
||||||
|
if(boss.stage) {
|
||||||
|
$entity_stage = textures::get(*boss.stage);
|
||||||
|
} else {
|
||||||
|
$entity_stage = textures::get("devils_fingers_background");
|
||||||
|
}
|
||||||
|
|
||||||
|
$entity_stage.sprite->setPosition({BOSS_VIEW_X, BOSS_VIEW_Y});
|
||||||
|
} else {
|
||||||
|
$entity_has_stage = false;
|
||||||
|
$entity_background = textures::get("devils_fingers_background");
|
||||||
|
$entity_background.sprite->setPosition({BOSS_VIEW_X, BOSS_VIEW_Y});
|
||||||
|
$status.world().set_the<Background>({$status.$parser});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ArenaUI::configure_gui() {
|
||||||
|
for(auto& [name, cell] : $status.cells()) {
|
||||||
|
auto button = $status.entity(name);
|
||||||
|
$status.set<Rectangle>(button, {});
|
||||||
|
$status.set<Clickable>(button, {
|
||||||
|
[this, name](auto, auto){
|
||||||
|
dbc::log(fmt::format("STATUS: {}", name));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if(name == "main_status") {
|
||||||
|
$status.set<Textual>(button, {fmt::format(L"HP: {}", $combat.hp)});
|
||||||
|
} else {
|
||||||
|
$status.set<Label>(button, {L"Attack"});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$status.init();
|
||||||
|
|
||||||
|
for(auto& [name, cell] : $overlay.cells()) {
|
||||||
|
auto region = $overlay.entity(name);
|
||||||
|
$overlay.set<Clickable>(region, {
|
||||||
|
[this, name](auto, auto){
|
||||||
|
dbc::log(fmt::format("OVERLAY: {}", name));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if(name == "enemy") {
|
||||||
|
$overlay.set<Sprite>(region, {$sprite_config.name, 20});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$overlay.init();
|
||||||
|
|
||||||
|
configure_sprite();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ArenaUI::init() {
|
||||||
|
// background must come first
|
||||||
|
configure_background();
|
||||||
|
configure_gui();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ArenaUI::render(sf::RenderWindow& window) {
|
||||||
|
window.draw(*$entity_background.sprite);
|
||||||
|
|
||||||
|
if($entity_has_stage) {
|
||||||
|
window.draw(*$entity_stage.sprite);
|
||||||
|
}
|
||||||
|
|
||||||
|
$status.render(window);
|
||||||
|
$overlay.render(window);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ArenaUI::mouse(float x, float y) {
|
||||||
|
if($status.mouse(x, y)) {
|
||||||
|
dbc::log("STATUS button pressed");
|
||||||
|
}
|
||||||
|
|
||||||
|
if($overlay.mouse(x, y)) {
|
||||||
|
$animation.play();
|
||||||
|
sound::play("Sword_Hit_1");
|
||||||
|
$entity_hit = !$entity_hit;
|
||||||
|
$combat.hp--;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
45
tools/arena_ui.hpp
Normal file
45
tools/arena_ui.hpp
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
#pragma once
|
||||||
|
#include <SFML/Graphics/RenderWindow.hpp>
|
||||||
|
#include <SFML/Graphics/Font.hpp>
|
||||||
|
#include "guecs.hpp"
|
||||||
|
#include "textures.hpp"
|
||||||
|
#include "components.hpp"
|
||||||
|
#include <SFML/System/Clock.hpp>
|
||||||
|
|
||||||
|
// aspect ratio of art is 3/2 so 1.5
|
||||||
|
// possible sizes: 900/600; 1620/1080; 1800/1200
|
||||||
|
// To calculate it do short side * 1.5 so 1080 * 1.5 == 1620
|
||||||
|
//
|
||||||
|
// Side panel = 300/1080
|
||||||
|
|
||||||
|
namespace arena {
|
||||||
|
using std::string, std::shared_ptr;
|
||||||
|
|
||||||
|
class ArenaUI {
|
||||||
|
public:
|
||||||
|
sf::Clock $clock;
|
||||||
|
bool $entity_hit = false;
|
||||||
|
sf::Vector2f $entity_pos;
|
||||||
|
components::Combat $combat;
|
||||||
|
components::Sprite $sprite_config;
|
||||||
|
components::Sound $sounds;
|
||||||
|
components::Animation $animation;
|
||||||
|
guecs::UI $status;
|
||||||
|
guecs::UI $overlay;
|
||||||
|
textures::SpriteTexture $entity_background;
|
||||||
|
bool $entity_has_stage = false;
|
||||||
|
textures::SpriteTexture $entity_stage;
|
||||||
|
std::shared_ptr<DinkyECS::World> $world = nullptr;
|
||||||
|
DinkyECS::Entity $entity_id;
|
||||||
|
components::GameConfig& $config;
|
||||||
|
|
||||||
|
ArenaUI(shared_ptr<DinkyECS::World> world, DinkyECS::Entity entity_id);
|
||||||
|
|
||||||
|
void init();
|
||||||
|
void render(sf::RenderWindow& window);
|
||||||
|
bool mouse(float x, float y);
|
||||||
|
void configure_sprite();
|
||||||
|
void configure_background();
|
||||||
|
void configure_gui();
|
||||||
|
};
|
||||||
|
}
|
|
@ -253,6 +253,7 @@ void WorldBuilder::place_entities(DinkyECS::World &world) {
|
||||||
} else {
|
} else {
|
||||||
auto player_data = config.enemies["PLAYER_TILE"];
|
auto player_data = config.enemies["PLAYER_TILE"];
|
||||||
auto player_ent = configure_entity_in_map(world, player_data, 0);
|
auto player_ent = configure_entity_in_map(world, player_data, 0);
|
||||||
|
|
||||||
// configure player in the world
|
// configure player in the world
|
||||||
Player player{player_ent};
|
Player player{player_ent};
|
||||||
world.set_the<Player>(player);
|
world.set_the<Player>(player);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue