Did a full code coverage review and improved many of the tests and a bunch of code. I'll do one more final walk through all the code before getting back to work on the new combat system.

master
Zed A. Shaw 2 weeks ago
parent 113a4a3b3e
commit d3158291f7
  1. 2
      Makefile
  2. 376
      ansi_parser.cpp
  3. 23
      ansi_parser.hpp
  4. 167
      ansi_parser.rl
  5. 4
      assets/bosses.json
  6. 11
      autowalker.cpp
  7. 6
      components.cpp
  8. 2
      components.hpp
  9. 7
      config.cpp
  10. 1
      config.hpp
  11. 4
      easings.hpp
  12. 11
      gui_fsm.cpp
  13. 2
      gui_fsm.hpp
  14. 51
      map_view.cpp
  15. 9
      map_view.hpp
  16. 15
      meson.build
  17. 64
      panel.cpp
  18. 60
      panel.hpp
  19. 275
      render.cpp
  20. 80
      render.hpp
  21. 21
      save.cpp
  22. 24
      save.hpp
  23. 5
      scripts/coverage_report.ps1
  24. 16
      systems.cpp
  25. 3
      systems.hpp
  26. 64
      tests/components.cpp
  27. 2
      tests/config.cpp
  28. 17
      tests/easings.cpp
  29. 15
      wraps/ftxui.wrap

@ -6,7 +6,7 @@ reset:
%.cpp : %.rl
ragel -o $@ $<
build: ansi_parser.cpp lel_parser.cpp
build: lel_parser.cpp
meson compile -j 10 -C builddir
release_build:

@ -1,376 +0,0 @@
#line 1 "ansi_parser.rl"
#include <fmt/core.h>
#include <string_view>
#include "dbc.hpp"
#include <SFML/Graphics.hpp>
#include "ansi_parser.hpp"
#include <iostream>
using namespace fmt;
#line 122 "ansi_parser.rl"
#line 13 "ansi_parser.cpp"
static const char _ansi_parser_actions[] = {
0, 1, 0, 1, 3, 1, 4, 1,
5, 1, 6, 1, 7, 1, 8, 1,
9, 1, 10, 1, 11, 1, 15, 1,
16, 2, 1, 12, 2, 1, 13, 2,
6, 7, 2, 16, 5, 3, 1, 14,
2
};
static const char _ansi_parser_key_offsets[] = {
0, 0, 1, 2, 11, 12, 14, 17,
18, 22, 23, 27, 28, 29, 30, 31,
33, 36, 38, 41, 43, 46, 47, 50,
51, 52, 53, 54, 55
};
static const int _ansi_parser_trans_keys[] = {
27, 91, 48, 49, 50, 51, 52, 55,
57, 53, 54, 109, 48, 109, 34, 48,
55, 109, 50, 52, 55, 109, 109, 49,
56, 57, 109, 109, 59, 50, 59, 48,
57, 59, 48, 57, 48, 57, 59, 48,
57, 48, 57, 109, 48, 57, 109, 56,
57, 109, 59, 50, 109, 109, 27, 27,
0
};
static const char _ansi_parser_single_lengths[] = {
0, 1, 1, 7, 1, 2, 3, 1,
4, 1, 4, 1, 1, 1, 1, 0,
1, 0, 1, 0, 1, 1, 3, 1,
1, 1, 1, 1, 1
};
static const char _ansi_parser_range_lengths[] = {
0, 0, 0, 1, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 1,
1, 1, 1, 1, 1, 0, 0, 0,
0, 0, 0, 0, 0
};
static const char _ansi_parser_index_offsets[] = {
0, 0, 2, 4, 13, 15, 18, 22,
24, 29, 31, 36, 38, 40, 42, 44,
46, 49, 51, 54, 56, 59, 61, 65,
67, 69, 71, 73, 75
};
static const char _ansi_parser_trans_targs[] = {
2, 1, 3, 0, 4, 5, 8, 10,
22, 26, 6, 7, 0, 28, 0, 6,
28, 0, 7, 7, 7, 0, 28, 0,
7, 7, 9, 28, 0, 28, 0, 11,
12, 21, 28, 0, 28, 0, 13, 0,
14, 0, 15, 0, 16, 0, 17, 16,
0, 18, 0, 19, 18, 0, 20, 0,
28, 20, 0, 28, 0, 23, 25, 28,
0, 24, 0, 14, 0, 28, 0, 28,
0, 2, 1, 2, 1, 0
};
static const char _ansi_parser_trans_actions[] = {
0, 7, 0, 0, 21, 21, 21, 21,
21, 21, 21, 21, 0, 31, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 17, 0, 15, 0, 0,
0, 0, 0, 0, 19, 0, 0, 0,
3, 0, 0, 0, 1, 0, 25, 0,
0, 1, 0, 28, 0, 0, 1, 0,
37, 0, 0, 9, 0, 0, 0, 0,
0, 0, 0, 5, 0, 11, 0, 13,
0, 0, 7, 23, 34, 0
};
static const char _ansi_parser_eof_actions[] = {
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 23
};
static const int ansi_parser_start = 27;
static const int ansi_parser_first_final = 27;
static const int ansi_parser_error = 0;
static const int ansi_parser_en_main = 27;
#line 125 "ansi_parser.rl"
#include <ftxui/screen/terminal.hpp>
ANSIParser::ANSIParser(sf::Color default_fg, sf::Color default_bg) :
$default_fg(default_fg),
$default_bg(default_bg)
{
}
bool ANSIParser::parse(std::wstring_view codes, ColorCB color_cb, WriteCB write_cb) {
const wchar_t *start = nullptr;
int cs = 0;
unsigned int value = 0;
const wchar_t *p = codes.data();
const wchar_t *pe = p + codes.size();
const wchar_t *eof = pe;
sf::Color bgcolor($default_bg);
sf::Color color($default_fg);
sf::Color* target = &color;
#line 120 "ansi_parser.cpp"
{
cs = ansi_parser_start;
}
#line 146 "ansi_parser.rl"
#line 123 "ansi_parser.cpp"
{
int _klen;
unsigned int _trans;
const char *_acts;
unsigned int _nacts;
const int *_keys;
if ( p == pe )
goto _test_eof;
if ( cs == 0 )
goto _out;
_resume:
_keys = _ansi_parser_trans_keys + _ansi_parser_key_offsets[cs];
_trans = _ansi_parser_index_offsets[cs];
_klen = _ansi_parser_single_lengths[cs];
if ( _klen > 0 ) {
const int *_lower = _keys;
const int *_mid;
const int *_upper = _keys + _klen - 1;
while (1) {
if ( _upper < _lower )
break;
_mid = _lower + ((_upper-_lower) >> 1);
if ( (*p) < *_mid )
_upper = _mid - 1;
else if ( (*p) > *_mid )
_lower = _mid + 1;
else {
_trans += (unsigned int)(_mid - _keys);
goto _match;
}
}
_keys += _klen;
_trans += _klen;
}
_klen = _ansi_parser_range_lengths[cs];
if ( _klen > 0 ) {
const int *_lower = _keys;
const int *_mid;
const int *_upper = _keys + (_klen<<1) - 2;
while (1) {
if ( _upper < _lower )
break;
_mid = _lower + (((_upper-_lower) >> 1) & ~1);
if ( (*p) < _mid[0] )
_upper = _mid - 2;
else if ( (*p) > _mid[1] )
_lower = _mid + 2;
else {
_trans += (unsigned int)((_mid - _keys)>>1);
goto _match;
}
}
_trans += _klen;
}
_match:
cs = _ansi_parser_trans_targs[_trans];
if ( _ansi_parser_trans_actions[_trans] == 0 )
goto _again;
_acts = _ansi_parser_actions + _ansi_parser_trans_actions[_trans];
_nacts = (unsigned int) *_acts++;
while ( _nacts-- > 0 )
{
switch ( *_acts++ )
{
case 0:
#line 14 "ansi_parser.rl"
{
start = p;
}
break;
case 1:
#line 18 "ansi_parser.rl"
{
value = 0;
size_t len = p - start;
dbc::check(start[0] != '-', "negative numbers not supported");
switch(len) {
case 10: value += (start[len-10] - '0') * 1000000000; [[fallthrough]];
case 9: value += (start[len- 9] - '0') * 100000000; [[fallthrough]];
case 8: value += (start[len- 8] - '0') * 10000000; [[fallthrough]];
case 7: value += (start[len- 7] - '0') * 1000000; [[fallthrough]];
case 6: value += (start[len- 6] - '0') * 100000; [[fallthrough]];
case 5: value += (start[len- 5] - '0') * 10000; [[fallthrough]];
case 4: value += (start[len- 4] - '0') * 1000; [[fallthrough]];
case 3: value += (start[len- 3] - '0') * 100; [[fallthrough]];
case 2: value += (start[len- 2] - '0') * 10; [[fallthrough]];
case 1: value += (start[len- 1] - '0');
break;
default:
dbc::sentinel("can't process > 10 digits");
}
}
break;
case 2:
#line 40 "ansi_parser.rl"
{
color_cb(color, bgcolor);
}
break;
case 3:
#line 43 "ansi_parser.rl"
{
target = &color;
}
break;
case 4:
#line 46 "ansi_parser.rl"
{
target = &bgcolor;
}
break;
case 5:
#line 50 "ansi_parser.rl"
{
write_cb((*p));
}
break;
case 6:
#line 54 "ansi_parser.rl"
{
color = $default_fg;
color_cb(color, bgcolor);
}
break;
case 7:
#line 58 "ansi_parser.rl"
{
bgcolor = $default_bg;
color_cb(color, bgcolor);
}
break;
case 8:
#line 62 "ansi_parser.rl"
{
color = $default_bg;
bgcolor = $default_fg;
color_cb(color, bgcolor);
}
break;
case 9:
#line 67 "ansi_parser.rl"
{
color = $default_fg;
bgcolor = $default_bg;
color_cb(color, bgcolor);
}
break;
case 10:
#line 72 "ansi_parser.rl"
{
color = sf::Color(100,100,100);
color_cb(color, bgcolor);
}
break;
case 11:
#line 76 "ansi_parser.rl"
{
color = sf::Color::Red;
color_cb(color, bgcolor);
}
break;
case 12:
#line 81 "ansi_parser.rl"
{ target->r = value; }
break;
case 13:
#line 82 "ansi_parser.rl"
{ target->g = value; }
break;
case 14:
#line 83 "ansi_parser.rl"
{ target->b = value; }
break;
case 15:
#line 84 "ansi_parser.rl"
{ value = 0; }
break;
case 16:
#line 85 "ansi_parser.rl"
{}
break;
#line 296 "ansi_parser.cpp"
}
}
_again:
if ( cs == 0 )
goto _out;
if ( ++p != pe )
goto _resume;
_test_eof: {}
if ( p == eof )
{
const char *__acts = _ansi_parser_actions + _ansi_parser_eof_actions[cs];
unsigned int __nacts = (unsigned int) *__acts++;
while ( __nacts-- > 0 ) {
switch ( *__acts++ ) {
case 16:
#line 85 "ansi_parser.rl"
{}
break;
#line 314 "ansi_parser.cpp"
}
}
}
_out: {}
}
#line 147 "ansi_parser.rl"
bool good = pe - p == 0;
if(!good) {
p -= 10;
// dear cthuhlu, save me from the pain that is wstring
for(int i = 0; i < 100; i++) {
try {
print("{}", p[i] == 0x1B ? '^' : char(p[i]));
} catch(...) {
print("?=", int(p[i]));
}
}
}
(void)ansi_parser_first_final;
(void)ansi_parser_error;
(void)ansi_parser_en_main;
return good;
}

@ -1,23 +0,0 @@
#pragma once
#include <string_view>
#include <SFML/Graphics.hpp>
#include <codecvt>
#include <functional>
typedef std::function<void(sf::Color bgcolor, sf::Color color)> ColorCB;
typedef std::function<void(wchar_t ch)> WriteCB;
class ANSIParser {
sf::Color $default_fg;
sf::Color $default_bg;
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> $converter;
public:
ANSIParser(sf::Color default_fg, sf::Color default_bg);
// disable copying
ANSIParser(ANSIParser& ap) = delete;
bool parse(std::wstring_view codes, ColorCB color_cb, WriteCB write_cb);
};

@ -1,167 +0,0 @@
#include <fmt/core.h>
#include <string_view>
#include "dbc.hpp"
#include <SFML/Graphics.hpp>
#include "ansi_parser.hpp"
#include <iostream>
using namespace fmt;
%%{
machine ansi_parser;
alphtype int;
action tstart {
start = fpc;
}
action number {
value = 0;
size_t len = fpc - start;
dbc::check(start[0] != '-', "negative numbers not supported");
switch(len) {
case 10: value += (start[len-10] - '0') * 1000000000; [[fallthrough]];
case 9: value += (start[len- 9] - '0') * 100000000; [[fallthrough]];
case 8: value += (start[len- 8] - '0') * 10000000; [[fallthrough]];
case 7: value += (start[len- 7] - '0') * 1000000; [[fallthrough]];
case 6: value += (start[len- 6] - '0') * 100000; [[fallthrough]];
case 5: value += (start[len- 5] - '0') * 10000; [[fallthrough]];
case 4: value += (start[len- 4] - '0') * 1000; [[fallthrough]];
case 3: value += (start[len- 3] - '0') * 100; [[fallthrough]];
case 2: value += (start[len- 2] - '0') * 10; [[fallthrough]];
case 1: value += (start[len- 1] - '0');
break;
default:
dbc::sentinel("can't process > 10 digits");
}
}
action color_out {
color_cb(color, bgcolor);
}
action is_fg {
target = &color;
}
action is_bg {
target = &bgcolor;
}
action out {
write_cb(fc);
}
action reset_fg {
color = $default_fg;
color_cb(color, bgcolor);
}
action reset_bg {
bgcolor = $default_bg;
color_cb(color, bgcolor);
}
action invert {
color = $default_bg;
bgcolor = $default_fg;
color_cb(color, bgcolor);
}
action reset_invert {
color = $default_fg;
bgcolor = $default_bg;
color_cb(color, bgcolor);
}
action half_bright {
color = sf::Color(100,100,100);
color_cb(color, bgcolor);
}
action red_text {
color = sf::Color::Red;
color_cb(color, bgcolor);
}
action red { target->r = value; }
action blue { target->g = value; }
action green { target->b = value; }
action start { value = 0; }
action end {}
action log { println("command {}", (char)fc); }
ESC = 0x1B;
start = ESC "[";
fg = "38;" %is_fg;
bg = "48;" %is_bg;
reset = ("39" %reset_fg | "49" %reset_bg);
num = digit+ >tstart %number;
color256 = "5;";
color24b = "2;";
ansi = (
start %start
(
reset |
"0" %reset_fg %reset_bg |
"1" |
"2" %half_bright |
"3" |
"4" |
"5" |
"6" |
"7" %invert |
"31" %red_text |
"22" |
"24" |
"27" %reset_invert |
"9" ["0"-"7"] |
"10" ["0"-"7"] |
(fg|bg) (color24b num %red ";" num %blue ";" num %green ) %color_out
) "m" %end
);
other = (any+ @out -- ESC)*;
main := (other :> ansi)**;
}%%
%% write data;
#include <ftxui/screen/terminal.hpp>
ANSIParser::ANSIParser(sf::Color default_fg, sf::Color default_bg) :
$default_fg(default_fg),
$default_bg(default_bg)
{
}
bool ANSIParser::parse(std::wstring_view codes, ColorCB color_cb, WriteCB write_cb) {
const wchar_t *start = nullptr;
int cs = 0;
unsigned int value = 0;
const wchar_t *p = codes.data();
const wchar_t *pe = p + codes.size();
const wchar_t *eof = pe;
sf::Color bgcolor($default_bg);
sf::Color color($default_fg);
sf::Color* target = &color;
%% write init;
%% write exec;
bool good = pe - p == 0;
if(!good) {
p -= 10;
// dear cthuhlu, save me from the pain that is wstring
for(int i = 0; i < 100; i++) {
try {
print("{}", p[i] == 0x1B ? '^' : char(p[i]));
} catch(...) {
print("?=", int(p[i]));
}
}
}
(void)ansi_parser_first_final;
(void)ansi_parser_error;
(void)ansi_parser_en_main;
return good;
}

@ -3,7 +3,7 @@
"components": [
{"_type": "BossFight",
"background": "boss_fight_background",
"stage": false,
"stage": null,
"weapon_sound": "Sword_Hit_2"
},
{"_type": "Combat", "hp": 20, "max_hp": 20, "damage": 20, "dead": false},
@ -24,7 +24,7 @@
"components": [
{"_type": "BossFight",
"background": "devils_fingers_background",
"stage": false,
"stage": "devils_fingers_stage",
"weapon_sound": "Sword_Hit_2"
},
{"_type": "Combat", "hp": 20, "max_hp": 20, "damage": 20, "dead": false},

@ -178,6 +178,8 @@ void Autowalker::rotate_player(Point current, Point target) {
facing = fsm.$main_ui.$compass_dir;
}
while(fsm.in_state(gui::State::ROTATING)) send_event(gui::Event::TICK);
dbc::check(fsm.$main_ui.$compass_dir == target_facing,
"player isn't facing the correct direction");
}
@ -229,6 +231,14 @@ void Autowalker::handle_player_walk(ai::State& start, ai::State& goal) {
send_event(gui::Event::ATTACK);
} else if(action.name == "kill_enemy") {
status("KILLING ENEMY");
// TODO: find the enemy and then rotate toward them
Point current = get_current_position();
if(fsm.in_state(gui::State::IN_COMBAT)) {
rotate_player(current, {current.x - 1, current.y - 1});
dbc::log("TODO: you should find the enemy and face them instead of THIS GARBAGE!");
}
process_combat();
} else if(action.name == "use_healing") {
status("USING HEALING");
@ -283,7 +293,6 @@ void Autowalker::process_move(Pathing& paths) {
}
rotate_player(current, target);
while(fsm.in_state(gui::State::ROTATING)) send_event(gui::Event::TICK);
send_event(gui::Event::MOVE_FORWARD);
while(fsm.in_state(gui::State::MOVING)) send_event(gui::Event::TICK);

@ -43,11 +43,11 @@ namespace components {
case ease::SINE:
return ease::sine(float(frames) / subframe * ease_rate);
case ease::OUT_CIRC:
return ease::sine(ease::out_circ(float(frames) / subframe * ease_rate));
return ease::out_circ(ease::sine(float(frames) / subframe * ease_rate));
case ease::OUT_BOUNCE:
return ease::sine(ease::out_bounce(float(frames) / subframe * ease_rate));
return ease::out_bounce(ease::sine(float(frames) / subframe * ease_rate));
case ease::IN_OUT_BACK:
return ease::sine(ease::in_out_back(float(frames) / subframe * ease_rate));
return ease::in_out_back(ease::sine(float(frames) / subframe * ease_rate));
default:
dbc::sentinel(
fmt::format("Invalid easing {} given to animation",

@ -120,7 +120,7 @@ namespace components {
bool stationary = false;
int current = 0;
bool playing = false;
float subframe = 0;
float subframe = 1.0f;
int texture_width = TEXTURE_WIDTH;
void play();

@ -15,13 +15,6 @@ json &Config::operator[](const std::string &key) {
return $config[key];
}
std::wstring Config::wstring(const std::string key) {
dbc::check($config.contains(key), fmt::format("ERROR wstring in config, key {} doesn't exist.", key));
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> $converter;
const std::string& str_val = $config[key];
return $converter.from_bytes(str_val);
}
std::wstring Config::wstring(const std::string main_key, const std::string sub_key) {
dbc::check($config.contains(main_key), fmt::format("ERROR wstring main/key in config, main_key {} doesn't exist.", main_key));
dbc::check($config[main_key].contains(sub_key), fmt::format("ERROR wstring in config, main_key/key {}/{} doesn't exist.", main_key, sub_key));

@ -14,7 +14,6 @@ struct Config {
nlohmann::json &operator[](const std::string &key);
nlohmann::json &json() { return $config; };
std::wstring wstring(const std::string main_key);
std::wstring wstring(const std::string main_key, const std::string sub_key);
std::vector<std::string> keys();
};

@ -4,7 +4,7 @@
namespace ease {
enum Style {
NONE, SINE, OUT_CIRC, OUT_BOUNCE, IN_OUT_BACK
NONE, SINE, OUT_CIRC, OUT_BOUNCE, IN_OUT_BACK, FUCKFACE
};
inline double sine(double x) {
@ -12,7 +12,7 @@ namespace ease {
}
inline double out_circ(double x) {
return std::sqrt(1.0f - std::pow(x - 1.0f, 2.0f));
return std::sqrt(1.0f - ((x - 1.0f) * (x - 1.0f)));
}
inline double out_bounce(double x) {

@ -15,7 +15,6 @@ namespace gui {
FSM::FSM() :
$window(sf::VideoMode({SCREEN_WIDTH, SCREEN_HEIGHT}), "Zed's Raycaster Thing"),
$main_ui($window),
$renderer($window),
$level($levels.current()),
$map_ui($level),
$combat_ui($level),
@ -53,11 +52,6 @@ namespace gui {
$boss_fight_ui = $levels.create_bossfight($level.world);
$boss_fight_ui->init();
$renderer.init_terminal();
$map_ui.create_render();
$map_ui.resize_canvas();
$renderer.resize_grid(BASE_MAP_FONT_SIZE, $map_ui);
run_systems();
state(State::IDLE);
}
@ -106,6 +100,7 @@ namespace gui {
break;
default:
dbc::log(fmt::format("In ATTACKING state, unhandled event {}", (int)ev));
state(State::IDLE);
}
}
@ -152,8 +147,6 @@ namespace gui {
state(State::ROTATING);
break;
case MAP_OPEN:
$renderer.resize_grid(BASE_MAP_FONT_SIZE, $map_ui);
$map_ui.resize_canvas();
state(State::MAPPING);
break;
case ATTACK:
@ -327,8 +320,6 @@ namespace gui {
void FSM::render() {
if(in_state(State::MAPPING)) {
$window.clear();
$map_ui.render();
$renderer.draw($map_ui);
} else if(in_state(State::NEXT_LEVEL)) {
$window.clear();
$boss_fight_ui->render($window);

@ -3,7 +3,6 @@
#include "stats.hpp"
#include "levelmanager.hpp"
#include "fsm.hpp"
#include "render.hpp"
#include "map_view.hpp"
#include "main_ui.hpp"
#include "combat_ui.hpp"
@ -49,7 +48,6 @@ namespace gui {
bool autowalking = false;
LevelManager $levels;
MainUI $main_ui;
SFMLRender $renderer;
GameLevel $level;
shared_ptr<BossFightUI> $boss_fight_ui = nullptr;
MapViewUI $map_ui;

@ -1,14 +1,11 @@
#include "map_view.hpp"
#include <functional>
#include <string>
#include "systems.hpp"
namespace gui {
using namespace components;
using ftxui::Color;
MapViewUI::MapViewUI(GameLevel &level) :
Panel(0, 0, 0, 0, true),
$level(level)
{
}
@ -18,54 +15,6 @@ namespace gui {
}
void MapViewUI::draw_map() {
const auto& debug = $level.world->get_the<Debug>();
const auto& player = $level.world->get_the<Player>();
const auto& player_position = $level.world->get<Position>(player.entity);
Point start = $level.map->center_camera(player_position.location, width, height);
auto &tiles = $level.map->tiles();
auto &paths = $level.map->paths();
auto &lighting = $level.lights->lighting();
// WARN: this is exploiting that -1 in size_t becomes largest
size_t end_x = std::min(size_t(width), $level.map->width() - start.x);
size_t end_y = std::min(size_t(height), $level.map->height() - start.y);
for(size_t y = 0; y < end_y; ++y) {
for(size_t x = 0; x < end_x; ++x)
{
const Tile& tile = tiles.at(start.x+x, start.y+y);
// light value is an integer that's a percent
float light_value = debug.LIGHT ? 80 * PERCENT : lighting[start.y+y][start.x+x] * PERCENT;
int dnum = debug.PATHS ? paths[start.y+y][start.x+x] : WALL_PATH_LIMIT;
if(debug.PATHS && dnum != WALL_PATH_LIMIT) {
string num = dnum > 15 ? "*" : fmt::format("{:x}", dnum);
$canvas.DrawText(x * 2, y * 4, num, [dnum, tile](auto &pixel) {
pixel.foreground_color = Color::HSV(dnum * 20, 150, 200);
pixel.background_color = Color::HSV(30, 20, tile.foreground[2] * 50 * PERCENT);
});
} else {
$canvas.DrawText(x * 2, y * 4, tile.display, [tile, light_value](auto &pixel) {
pixel.foreground_color = Color::HSV(tile.foreground[0], tile.foreground[1], tile.foreground[2] * light_value);
pixel.background_color = Color::HSV(tile.background[0], tile.background[1], tile.background[2] * light_value);
});
}
}
}
System::draw_entities(*$level.world, *$level.map, lighting, $canvas, start, width, height);
}
void MapViewUI::create_render() {
set_renderer(Renderer([&] {
draw_map();
return canvas($canvas);
}));
}
void MapViewUI::resize_canvas() {
// set canvas to best size
$canvas = Canvas(width * 2, height * 4);
}
}

@ -1,19 +1,12 @@
#pragma once
#include "panel.hpp"
#include <ftxui/dom/canvas.hpp>
#include "levelmanager.hpp"
using ftxui::Canvas;
namespace gui {
class MapViewUI : public Panel {
class MapViewUI {
public:
Canvas $canvas;
GameLevel $level;
MapViewUI(GameLevel &level);
void create_render();
void resize_canvas();
void draw_map();
void update_level(GameLevel &level);
};

@ -68,22 +68,17 @@ sfml_system = dependency('sfml_system')
sfml_window = dependency('sfml_window',
default_options: ['default_library=shared'])
ftxui_screen = dependency('ftxui-screen')
ftxui_dom = dependency('ftxui-dom')
ftxui_component = dependency('ftxui-component')
dependencies += [
fmt, json, freetype2,
flac, ogg, vorbis, vorbisfile, vorbisenc,
sfml_audio, sfml_graphics,
sfml_network, sfml_system,
sfml_window, ftxui_screen, ftxui_dom, ftxui_component
sfml_window
]
sources = [
'ai.cpp',
'ai_debug.cpp',
'ansi_parser.cpp',
'autowalker.cpp',
'boss_fight_ui.cpp',
'camera.cpp',
@ -106,11 +101,9 @@ sources = [
'matrix.cpp',
'matrix.cpp',
'overlay_ui.cpp',
'panel.cpp',
'pathing.cpp',
'rand.cpp',
'raycaster.cpp',
'render.cpp',
'rituals.cpp',
'save.cpp',
'shiterator.hpp',
@ -125,15 +118,14 @@ sources = [
]
executable('runtests', sources + [
'tests/ansi_parser.cpp',
'tests/ai.cpp',
'tests/base.cpp',
'tests/rituals.cpp',
'tests/components.cpp',
'tests/config.cpp',
'tests/dbc.cpp',
'tests/dinkyecs.cpp',
'tests/easings.cpp',
'tests/fsm.cpp',
'tests/ai.cpp',
'tests/guecs.cpp',
'tests/inventory.cpp',
'tests/lel.cpp',
@ -142,6 +134,7 @@ executable('runtests', sources + [
'tests/map.cpp',
'tests/matrix.cpp',
'tests/pathing.cpp',
'tests/rituals.cpp',
'tests/sound.cpp',
'tests/spatialmap.cpp',
'tests/textures.cpp',

@ -1,64 +0,0 @@
#include "panel.hpp"
#include "dbc.hpp"
void Panel::resize(int w, int h) {
$dirty = true;
width = w;
height = h;
$screen = Screen(width, height);
}
void Panel::set_renderer(Component renderer) {
$dirty = true;
$component = renderer;
}
void Panel::add(Component child) {
dbc::pre("must set_renderer first", $component != nullptr);
$dirty = true;
$component->Add(child);
}
void Panel::render() {
$dirty = true;
if(must_clear) $screen.Clear();
Render($screen, $component->Render());
}
const std::wstring& Panel::to_string() {
if($dirty) {
std::string as_text = $screen.ToString();
$screenout = $converter.from_bytes(as_text);
$dirty = false;
}
return $screenout;
}
void Panel::mouse_click(ftxui::Mouse::Button btn, Point pos) {
ftxui::Mouse mev{
.button=btn,
.motion=ftxui::Mouse::Motion::Pressed,
.x=int(pos.x), .y=int(pos.y)
};
$component->OnEvent(ftxui::Event::Mouse("", mev));
}
void Panel::mouse_release(ftxui::Mouse::Button btn, Point pos) {
ftxui::Mouse mev{
.button=btn,
.motion=ftxui::Mouse::Motion::Released,
.x=int(pos.x), .y=int(pos.y)
};
$component->OnEvent(ftxui::Event::Mouse("", mev));
}
const Screen& Panel::screen() {
return $screen;
}
void Panel::key_press(ftxui::Event event) {
$component->OnEvent(event);
}

@ -1,60 +0,0 @@
#pragma once
#include <ftxui/dom/node.hpp> // for Render
#include <ftxui/component/component.hpp>
#include <ftxui/component/mouse.hpp>
#include <ftxui/dom/canvas.hpp>
#include <ftxui/screen/screen.hpp>
#include <ftxui/dom/canvas.hpp>
#include <ftxui/screen/screen.hpp>
#include <ftxui/dom/canvas.hpp>
#include <SFML/Graphics/Color.hpp>
#include <locale>
#include <codecvt>
#include "color.hpp"
#include "point.hpp"
const int UI_PANEL_BORDER_PX=5;
using ftxui::Renderer, ftxui::Component, ftxui::Element, ftxui::Screen;
class Panel {
public:
int x;
int y;
int width;
int height;
bool has_border = false;
bool must_clear = true;
bool grid = false;
sf::Color default_bg = ColorValue::BLACK;
sf::Color default_fg = ColorValue::LIGHT_LIGHT;
sf::Color border_color = ColorValue::MID;
int border_px = UI_PANEL_BORDER_PX;
bool $dirty = true;
Component $component = nullptr;
Screen $screen;
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> $converter;
std::wstring $screenout;
Panel(int x, int y, int width, int height, bool is_grid=false) :
x(x),
y(y),
width(width),
height(height),
grid(is_grid),
$screen(Screen(width, height))
{
must_clear = !is_grid;
};
void resize(int width, int height);
void set_renderer(Component renderer);
void add(Component child);
void render();
void mouse_click(ftxui::Mouse::Button btn, Point pos);
void mouse_release(ftxui::Mouse::Button btn, Point pos);
void key_press(ftxui::Event event);
const std::wstring &to_string();
const Screen &screen();
};

@ -1,275 +0,0 @@
#include "render.hpp"
#include "ansi_parser.hpp"
#include <cmath>
#include <fmt/core.h>
#include <array>
#include "map.hpp"
#include <iostream>
#include "color.hpp"
#if defined(_WIN64) || defined(_WIN32)
#include <stdio.h>
#include <fcntl.h>
#include <io.h>
#endif
using namespace fmt;
SFMLRender::SFMLRender(sf::RenderWindow &window) :
$window(window),
$map_font_size(0),
$line_spacing(0),
$default_fg(ColorValue::LIGHT_MID),
$default_bg(ColorValue::BLACK),
$bg_sprite($font_texture),
$font(FONT_FILE_NAME),
$ui_text($font),
$ansi($default_fg, $default_bg)
{
// force true color, but maybe I want to support different color sets
$font.setSmooth(false);
$ui_text.setPosition({0,0});
$ui_text.setCharacterSize($config.ui_font_size);
$ui_text.setFillColor(ColorValue::LIGHT_MID);
sf::Glyph glyph = $font.getGlyph($config.ui_base_char, $config.ui_font_size, false);
$text_bounds = glyph.bounds;
$cells_w = std::ceil($config.video_x / $text_bounds.size.x);
$cells_h = std::ceil($config.video_y / $text_bounds.size.y);
}
sf::Sprite &SFMLRender::get_text_sprite(wchar_t tile) {
if(!$sprites.contains(tile)) {
sf::Glyph glyph = $font.getGlyph(tile, $map_font_size, false);
// WARNING! we actually have to do this here because SFML caches
// the glyphs on the font texture, so this gets loaded each time
// we get a new glyph from the font.
$font_texture = $font.getTexture($map_font_size);
$sprites.try_emplace(tile, $font_texture, glyph.textureRect);
}
return $sprites.at(tile);
}
void SFMLRender::clear_cache() {
$sprites.clear();
bool good = $font.openFromFile(FONT_FILE_NAME);
dbc::check(good, "Failed to load the font.");
$font.setSmooth(false);
$ui_text.setFont($font);
}
void SFMLRender::center_panel(Panel &panel) {
int cell_center_x = ($cells_w - panel.width) / 2;
int cell_center_y = ($cells_h - panel.height) / 2;
panel.x = cell_center_x * $text_bounds.size.x;
panel.y = cell_center_y * $text_bounds.size.y;
}
void SFMLRender::resize_grid(int new_size, Panel &panel_out) {
auto glyph = $font.getGlyph($config.bg_tile, new_size, false);
int view_x = std::ceil(($config.video_x - panel_out.x) / glyph.bounds.size.x);
int view_y = std::ceil(($config.video_y - panel_out.y) / glyph.bounds.size.y);
// looks good, set 'em all
$base_glyph = glyph;
$map_font_size = new_size;
$sprites.clear(); // need to reset the sprites for the new size
$line_spacing = $font.getLineSpacing($map_font_size);
$bg_sprite = get_text_sprite($config.bg_tile);
$grid_bounds = $bg_sprite.getLocalBounds();
panel_out.resize(view_x, view_y);
}
inline void configure_tile(const sf::Sprite &sprite, sf::FloatRect &sp_bounds, sf::FloatRect grid_bounds, float &width_delta, float &height_delta) {
// BUG: I think I could create a struct that kept this info for all sprites loaded
// should look into caching all this instead of calcing it each time
sp_bounds = sprite.getLocalBounds();
// calculate where to center the sprite, but only if it's smaller
width_delta = grid_bounds.size.x > sp_bounds.size.x ? (grid_bounds.size.x - sp_bounds.size.x) / 2 : 0;
height_delta = grid_bounds.size.y > sp_bounds.size.x ? (grid_bounds.size.y - sp_bounds.size.y) / 2 : 0;
}
void SFMLRender::render_grid(const std::wstring &text, sf::Color default_fg, sf::Color default_bg, float x, float y) {
wchar_t last_tile = $config.bg_tile;
sf::FloatRect sp_bounds;
float width_delta = 0;
float height_delta = 0;
sf::Sprite &sprite = get_text_sprite(last_tile);
const float start_x = x;
sf::Color cur_fg = default_fg;
sf::Color cur_bg = default_bg;
$ansi.parse(text, [&](auto fg, auto bg) {
cur_fg = fg;
cur_bg = bg;
},
[&](wchar_t tile) {
switch(tile) {
case '\r': break; // ignore it
case '\n': {
// don't bother processing newlines, just skip
y += $line_spacing;
x = start_x;
}
break;
default: {
// only get a new sprite if the tile changed
if(last_tile != tile) {
sprite = get_text_sprite(tile);
configure_tile(sprite, sp_bounds, $grid_bounds, width_delta, height_delta);
last_tile = tile; // update last tile seen
}
sprite.setPosition({x+width_delta, y+height_delta});
sprite.setColor(cur_fg);
// only draw background char if it's different from default
if(cur_bg != default_bg) {
$bg_sprite.setPosition({x, y});
$bg_sprite.setColor(cur_bg);
$window.draw($bg_sprite);
}
$window.draw(sprite);
// next cell
x += $base_glyph.advance;
}
}
});
}
inline sf::FloatRect draw_chunk(sf::RenderWindow& window,
sf::FloatRect text_bounds, sf::Text& text, sf::Color default_bg,
sf::Color bgcolor, int bg_box_offset, float x, float y, std::wstring &out)
{
text.setString(out);
text.setPosition({x, y});
// get a base character for the cell size
sf::FloatRect bounds({x, y}, {text_bounds.size.x * out.size(), text_bounds.size.y});
if(default_bg != bgcolor) {
sf::RectangleShape backing(bounds.size);
backing.setFillColor(bgcolor);
backing.setPosition({bounds.position.x, bounds.position.y + bg_box_offset});
window.draw(backing);
}
window.draw(text);
out.clear();
return bounds;
}
void SFMLRender::render_text(const std::wstring &text, sf::Color default_fg, sf::Color default_bg, float start_x, float start_y) {
std::wstring out;
float x = start_x;
float y = start_y;
sf::Color cur_bg = default_bg;
// start with the default_fg until it's changed
$ui_text.setFillColor(default_fg);
$ansi.parse(text,
[&](auto fg, auto bg) {
if(out.size() > 0 ) {
auto bounds = draw_chunk($window,
$text_bounds, $ui_text,
default_bg, cur_bg, $config.bg_box_offset, x, y, out);
x += bounds.size.x;
}
cur_bg = bg;
$ui_text.setFillColor(fg);
},
[&](wchar_t tile) {
switch(tile) {
case '\r': break; // ignore it
case '\n': {
sf::FloatRect bounds;
if(out.size() > 0) {
bounds = draw_chunk($window, $text_bounds,
$ui_text, default_bg, cur_bg, $config.bg_box_offset, x, y, out);
} else {
bounds = $ui_text.getLocalBounds();
}
y += bounds.size.y;
x = start_x; // reset to the original position
}
break;
default:
out += tile;
break;
}
}
);
if(out.size() > 0) {
draw_chunk($window, $text_bounds, $ui_text, default_bg, cur_bg, $config.bg_box_offset, x, y, out);
}
}
void SFMLRender::draw_sprite(sf::Sprite &sprite, sf::Shader *shader) {
$window.draw(sprite, shader);
}
/*
* Does not render the panel, you have to do that so you can control
* when things render.
*/
void SFMLRender::draw(Panel &panel, float x_offset, float y_offset) {
const std::wstring &panelout = panel.to_string();
auto bounds = panel.grid ? $grid_bounds : $text_bounds;
sf::RectangleShape backing(
sf::Vector2f(bounds.size.x * panel.width + panel.border_px,
bounds.size.y * panel.height + panel.border_px));
backing.setFillColor(panel.default_bg);
if(panel.has_border) {
backing.setOutlineColor(panel.border_color);
backing.setOutlineThickness(panel.border_px);
}
backing.setPosition({panel.x + x_offset, panel.y + y_offset});
$window.draw(backing);
if(panel.grid) {
render_grid(panelout, panel.default_fg, panel.default_bg, panel.x + x_offset, panel.y + y_offset);
} else {
render_text(panelout, panel.default_fg, panel.default_bg, panel.x + x_offset, panel.y + y_offset);
}
}
bool SFMLRender::mouse_position(Panel &panel, Point &out) {
// yes, you have to do this in sfml
sf::Vector2f pos = $window.mapPixelToCoords(sf::Mouse::getPosition($window));
auto bounds = panel.grid ? $grid_bounds : $text_bounds;
if(pos.x >= panel.x && pos.y >= panel.y
&& pos.x <= (panel.x + panel.width * bounds.size.x)
&& pos.y <= (panel.y + panel.height * bounds.size.y))
{
out = {
size_t((pos.x - panel.x) / bounds.size.x),
size_t((pos.y - panel.y) / bounds.size.y)
};
return true;
}
return false;
}
void SFMLRender::init_terminal() {
#if defined(_WIN64) || defined(_WIN32)
_setmode(_fileno(stdout), _O_U16TEXT);
#endif
ftxui::Terminal::SetColorSupport(ftxui::Terminal::Color::TrueColor);
}

@ -1,80 +0,0 @@
#pragma once
#include <ftxui/screen/screen.hpp>
#include <ftxui/dom/canvas.hpp>
#include <SFML/Window.hpp>
#include <SFML/System.hpp>
#include <SFML/Graphics.hpp>
#include <SFML/Graphics/Rect.hpp>
#include "point.hpp"
#include <codecvt>
#include "ansi_parser.hpp"
#include "panel.hpp"
#include "constants.hpp"
#include <optional>
using ftxui::Canvas, ftxui::Screen;
/*
* BUG: This could be so much better.
*/
struct RenderConfig {
unsigned int video_x = SCREEN_WIDTH;
unsigned int video_y = SCREEN_HEIGHT;
int ui_font_size=UI_FONT_SIZE;
int base_map_font_size=BASE_MAP_FONT_SIZE;
wchar_t bg_tile = BG_TILE;
wchar_t ui_base_char = UI_BASE_CHAR;
int bg_box_offset=BG_BOX_OFFSET;
};
struct SFMLRender {
int $cells_w = 0;
int $cells_h = 0;
RenderConfig $config;
sf::RenderWindow& $window;
int $map_font_size;
float $line_spacing;
sf::Color $default_fg;
sf::Color $default_bg;
sf::Texture $font_texture;
sf::Sprite $bg_sprite;
sf::Font $font;
sf::Text $ui_text;
ANSIParser $ansi;
std::unordered_map<wchar_t, sf::Sprite> $sprites;
sf::Glyph $base_glyph;
sf::FloatRect $grid_bounds;
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> $converter;
sf::FloatRect $text_bounds;
SFMLRender(sf::RenderWindow& window);
// disable copy
SFMLRender(SFMLRender &other) = delete;
sf::Sprite &get_text_sprite(wchar_t tile);
void resize_grid(int new_size, Panel &panel_out);
void render_grid(const std::wstring &text, sf::Color default_fg, sf::Color default_bg, float x, float y);
void render_text(const std::wstring &text, sf::Color default_fg, sf::Color default_bg, float x, float y);
void draw(Panel &panel, float x_offset=0.0f, float y_offset=0.0f);
void draw_sprite(sf::Sprite &sprite, sf::Shader *shader);
void center_panel(Panel &panel);
std::optional<sf::Event> poll_event() {
return $window.pollEvent();
}
void close() { return $window.close(); }
bool is_open() { return $window.isOpen(); }
int font_size() { return $map_font_size; }
void clear() { $window.clear(); }
void display() { $window.display(); }
bool mouse_position(Panel &panel, Point &out);
void clear_cache();
static void init_terminal();
};

@ -8,27 +8,6 @@
using namespace components;
using namespace fmt;
template<typename CompT>
inline void extract(DinkyECS::World &world, std::map<DinkyECS::Entity, CompT> &into) {
auto from_world = world.entity_map_for<CompT>();
for(auto [entity, value] : from_world) {
into[entity] = std::any_cast<CompT>(value);
}
}
void save::to_file(fs::path path, DinkyECS::World &world, Map &map) {
(void)path;
(void)world;
(void)map;
}
void save::from_file(fs::path path, DinkyECS::World &world_out, Map &map_out) {
(void)path;
(void)world_out;
(void)map_out;
}
void save::load_configs(DinkyECS::World &world) {
Config game("./assets/config.json");
Config enemies("./assets/enemies.json");

@ -10,29 +10,5 @@
namespace save {
namespace fs = std::filesystem;
struct MapData {
size_t width;
size_t height;
std::vector<Room> rooms;
Matrix walls;
};
struct Facts {
components::Player player;
};
struct SaveData {
Facts facts;
MapData map;
std::map<DinkyECS::Entity, components::Position> position;
std::map<DinkyECS::Entity, components::Motion> motion;
std::map<DinkyECS::Entity, components::Combat> combat;
std::map<DinkyECS::Entity, components::Tile> tile;
// std::map<DinkyECS::Entity, components::Inventory> inventory;
};
void to_file(fs::path path, DinkyECS::World &world, Map &map);
void from_file(fs::path path, DinkyECS::World &world_out, Map &map);
void load_configs(DinkyECS::World &world);
}

@ -4,7 +4,10 @@ cp *.cpp,*.hpp,*.rl builddir
. .venv/Scripts/activate
rm -recurse -force coverage
cp scripts\gcovr_patched_coverage.py .venv\Lib\site-packages\gcovr\coverage.py
gcovr -o coverage/ --html --html-details --gcov-ignore-errors all -e builddir/subprojects -e subprojects .
gcovr -o coverage/ --html --html-details --html-theme github.dark-blue --gcov-ignore-errors all --gcov-ignore-parse-errors negative_hits.warn_once_per_file -e builddir/subprojects -e builddir -e subprojects -j 10 .
rm *.gcov.json.gz
start .\coverage\coverage_details.html

@ -16,7 +16,6 @@ using std::string;
using namespace fmt;
using namespace components;
using lighting::LightSource;
using ftxui::Color;
void System::lighting(GameLevel &level) {
auto &light = *level.lights;
@ -331,11 +330,9 @@ void System::plan_motion(DinkyECS::World& world, Point move_to) {
/*
* This one is called inside the MapViewUI very often so
* just avoide GameMap unlike the others.
*
* BUG: Just get rid of this and use the new UI to do the map
* just avoid GameMap unlike the others.
*/
void System::draw_entities(DinkyECS::World &world, Map &map, const Matrix &lights, ftxui::Canvas &canvas, const Point &cam_orig, size_t view_x, size_t view_y) {
void System::draw_entities(DinkyECS::World &world, Map &map, const Matrix &lights, const Point &cam_orig, size_t view_x, size_t view_y) {
auto &tiles = map.tiles();
world.query<Position, Tile>([&](auto, auto &pos, auto &tile) {
@ -346,11 +343,10 @@ void System::draw_entities(DinkyECS::World &world, Map &map, const Matrix &light
float light_value = lights[pos.location.y][pos.location.x] * PERCENT;
const Tile& cell = tiles.at(pos.location.x, pos.location.y);
// the 2 and 4 are from ftxui::Canvas since it does a kind of "subpixel" drawing
canvas.DrawText(loc.x*2, loc.y*4, tile.display, [tile, light_value, cell](auto &pixel) {
pixel.foreground_color = Color::HSV(tile.foreground[0], tile.foreground[1], tile.foreground[2] * light_value);
pixel.background_color = Color::HSV(cell.background[0], cell.background[1], cell.background[2] * light_value);
});
(void)loc; // not used yet, this after ripping out map so needs rewrite
(void)light_value;
(void)cell;
(void)tile;
}
});
}

@ -1,7 +1,6 @@
#pragma once
#include "components.hpp"
#include "levelmanager.hpp"
#include <ftxui/dom/canvas.hpp>
namespace System {
@ -18,7 +17,7 @@ namespace System {
void init_positions(DinkyECS::World &world, SpatialMap &collider);
void device(DinkyECS::World &world, DinkyECS::Entity actor, DinkyECS::Entity item);
void plan_motion(DinkyECS::World& world, Point move_to);
void draw_entities(DinkyECS::World &world, Map &map, const Matrix &lights, ftxui::Canvas &canvas, const Point &cam_orig, size_t view_x, size_t view_y);
void draw_entities(DinkyECS::World &world, Map &map, const Matrix &lights, const Point &cam_orig, size_t view_x, size_t view_y);
void enemy_ai(GameLevel &level);
void combat(GameLevel &level);

@ -3,6 +3,7 @@
#include "dinkyecs.hpp"
#include "config.hpp"
#include <iostream>
#include "easings.hpp"
using namespace components;
using namespace DinkyECS;
@ -27,3 +28,66 @@ TEST_CASE("confirm component loading works", "[components]") {
}
}
}
TEST_CASE("make sure json_mods works", "[components]") {
Config config("assets/bosses.json");
// this confirms that loading something with an optional
// field works with the json conversions in json_mods.hpp
for(auto& comp_data : config["RAT_KING"]["components"]) {
if(comp_data["_type"] == "BossFight") {
auto comp = components::convert<components::BossFight>(comp_data);
// the boss fight for the rat king doesn't have a stage so false=optional
REQUIRE(comp.stage == std::nullopt);
}
}
// this then confirms everything else about the json conversion
ComponentMap comp_map;
components::configure(comp_map);
DinkyECS::World world;
auto rat_king = world.entity();
components::configure_entity(comp_map, world, rat_king, config["RAT_KING"]["components"]);
auto boss = world.get<BossFight>(rat_king);
REQUIRE(boss.stage == std::nullopt);
// now load the other one for the other way optional is used
auto devils_fingers = world.entity();
components::configure_entity(comp_map, world, devils_fingers, config["DEVILS_FINGERS"]["components"]);
auto boss2 = world.get<BossFight>(devils_fingers);
REQUIRE(boss2.stage != std::nullopt);
}
TEST_CASE("animation component special cases", "[components]") {
Animation anim;
anim.easing = ease::NONE;
float res = anim.twitching();
REQUIRE(res == 0.0);
anim.easing = ease::SINE;
anim.subframe = 1.0f;
res = anim.twitching();
REQUIRE(!std::isnan(res));
anim.easing = ease::OUT_CIRC;
res = anim.twitching();
REQUIRE(!std::isnan(res));
anim.easing = ease::OUT_BOUNCE;
res = anim.twitching();
REQUIRE(!std::isnan(res));
anim.easing = ease::IN_OUT_BACK;
res = anim.twitching();
REQUIRE(!std::isnan(res));
anim.easing = ease::FUCKFACE;
bool throws = false;
try { anim.twitching(); } catch(...) { throws = true; }
REQUIRE(throws);
}

@ -5,7 +5,9 @@
TEST_CASE("confirm basic config loader ops", "[config]") {
Config config("assets/devices.json");
auto data_list = config.json();
auto the_keys = config.keys();
REQUIRE(the_keys.size() > 0);
for(auto& [key, data] : data_list.items()) {
auto wide1 = config.wstring(key, "name");

@ -0,0 +1,17 @@
#include <catch2/catch_test_macros.hpp>
#include "easings.hpp"
#include <cmath>
TEST_CASE("make sure the easing functions at least run", "[easings]") {
double out = ease::sine(1.3);
REQUIRE(out <= 1.0);
out = ease::out_circ(3.444);
REQUIRE(std::isnan(out));
out = ease::out_bounce(1.13);
REQUIRE(out <= 10 );
out = ease::in_out_back(3.4);
REQUIRE(out < 250.0);
}

@ -1,15 +0,0 @@
[wrap-file]
directory = FTXUI-5.0.0
source_url = https://github.com/ArthurSonzogni/FTXUI/archive/refs/tags/v5.0.0.tar.gz
source_filename = FTXUI-5.0.0.tar.gz
source_hash = a2991cb222c944aee14397965d9f6b050245da849d8c5da7c72d112de2786b5b
patch_filename = ftxui_5.0.0-1_patch.zip
patch_url = https://wrapdb.mesonbuild.com/v2/ftxui_5.0.0-1/get_patch
patch_hash = 21c654e82739b90b95bd98c1d321264608d37c50d29fbcc3487f790fd5412909
source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/ftxui_5.0.0-1/FTXUI-5.0.0.tar.gz
wrapdb_version = 5.0.0-1
[provide]
ftxui-screen = screen_dep
ftxui-dom = dom_dep
ftxui-component = component_dep
Loading…
Cancel
Save