We now have a full map that's basically the same mapping system from Roguish. There's a bug right now where it needs you to move once to calc the light and it's not being centered, but it does work.
This commit is contained in:
parent
55b67dcf5d
commit
d798d154ae
22 changed files with 1270 additions and 47 deletions
2
Makefile
2
Makefile
|
@ -6,7 +6,7 @@ reset:
|
||||||
%.cpp : %.rl
|
%.cpp : %.rl
|
||||||
ragel -o $@ $<
|
ragel -o $@ $<
|
||||||
|
|
||||||
build:
|
build: ansi_parser.cpp
|
||||||
meson compile -j 10 -C builddir
|
meson compile -j 10 -C builddir
|
||||||
|
|
||||||
release_build:
|
release_build:
|
||||||
|
|
371
ansi_parser.cpp
Normal file
371
ansi_parser.cpp
Normal file
|
@ -0,0 +1,371 @@
|
||||||
|
|
||||||
|
#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 = NULL;
|
||||||
|
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]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return good;
|
||||||
|
}
|
23
ansi_parser.hpp
Normal file
23
ansi_parser.hpp
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
#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);
|
||||||
|
};
|
163
ansi_parser.rl
Normal file
163
ansi_parser.rl
Normal file
|
@ -0,0 +1,163 @@
|
||||||
|
#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 = NULL;
|
||||||
|
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]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return good;
|
||||||
|
}
|
|
@ -11,20 +11,20 @@
|
||||||
"foreground": [230, 20, 30],
|
"foreground": [230, 20, 30],
|
||||||
"background": [230, 20, 120],
|
"background": [230, 20, 120],
|
||||||
"collision": true,
|
"collision": true,
|
||||||
"display": "█"
|
"display": "\ua5b8"
|
||||||
},
|
},
|
||||||
"WALL_VINES": {
|
"WALL_VINES": {
|
||||||
"texture": "assets/wall_with_vines-256.png",
|
"texture": "assets/wall_with_vines-256.png",
|
||||||
"foreground": [40, 15, 125],
|
"foreground": [40, 15, 125],
|
||||||
"background": [200, 29, 75],
|
"background": [200, 29, 75],
|
||||||
"collision": false,
|
"collision": false,
|
||||||
"display":"#"
|
"display":"\u0799"
|
||||||
},
|
},
|
||||||
"WALL_PILLAR": {
|
"WALL_PILLAR": {
|
||||||
"texture": "assets/wall_with_pillars-256.png",
|
"texture": "assets/wall_with_pillars-256.png",
|
||||||
"foreground": [40, 15, 125],
|
"foreground": [40, 15, 125],
|
||||||
"background": [200, 29, 75],
|
"background": [200, 29, 75],
|
||||||
"collision": false,
|
"collision": false,
|
||||||
"display":"%"
|
"display":"\u2274"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
14
color.hpp
Normal file
14
color.hpp
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace ColorValue {
|
||||||
|
const sf::Color BLACK{1, 4, 2};
|
||||||
|
const sf::Color DARK_DARK{9, 29, 16};
|
||||||
|
const sf::Color DARK_MID{14, 50, 26};
|
||||||
|
const sf::Color DARK_LIGHT{0, 109, 44};
|
||||||
|
const sf::Color MID{63, 171, 92};
|
||||||
|
const sf::Color LIGHT_DARK{161, 217, 155};
|
||||||
|
const sf::Color LIGHT_MID{199, 233, 192};
|
||||||
|
const sf::Color LIGHT_LIGHT{229, 245, 224};
|
||||||
|
const sf::Color WHITE{255, 255, 255};
|
||||||
|
const sf::Color TRANSPARENT = sf::Color::Transparent;
|
||||||
|
}
|
|
@ -13,7 +13,15 @@ namespace components {
|
||||||
} else if(comp_type == "Loot") {
|
} else if(comp_type == "Loot") {
|
||||||
world.set<Loot>(entity, {config["amount"]});
|
world.set<Loot>(entity, {config["amount"]});
|
||||||
} else if(comp_type == "Tile") {
|
} else if(comp_type == "Tile") {
|
||||||
world.set<Tile>(entity, {config["chr"]});
|
world.set<Tile>(entity, {
|
||||||
|
config["chr"],
|
||||||
|
entity_data["foreground"][0],
|
||||||
|
entity_data["foreground"][1],
|
||||||
|
entity_data["foreground"][2],
|
||||||
|
entity_data["background"][0],
|
||||||
|
entity_data["background"][1],
|
||||||
|
entity_data["background"][2]});
|
||||||
|
|
||||||
} else if(comp_type == "EnemyConfig") {
|
} else if(comp_type == "EnemyConfig") {
|
||||||
world.set<EnemyConfig>(entity, {config["hearing_distance"]});
|
world.set<EnemyConfig>(entity, {config["hearing_distance"]});
|
||||||
} else if(comp_type == "Combat") {
|
} else if(comp_type == "Combat") {
|
||||||
|
|
|
@ -32,6 +32,13 @@ namespace components {
|
||||||
|
|
||||||
struct Tile {
|
struct Tile {
|
||||||
std::string chr;
|
std::string chr;
|
||||||
|
uint8_t fg_h = 200;
|
||||||
|
uint8_t fg_s = 20;
|
||||||
|
uint8_t fg_v = 200;
|
||||||
|
uint8_t bg_h = 100;
|
||||||
|
uint8_t bg_s = 20;
|
||||||
|
uint8_t bg_v = 0;
|
||||||
|
|
||||||
DEFINE_SERIALIZABLE(Tile, chr);
|
DEFINE_SERIALIZABLE(Tile, chr);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -6,10 +6,10 @@ constexpr const int TEXTURE_WIDTH=256;
|
||||||
constexpr const int TEXTURE_HEIGHT=256;
|
constexpr const int TEXTURE_HEIGHT=256;
|
||||||
constexpr const int RAY_VIEW_WIDTH=960;
|
constexpr const int RAY_VIEW_WIDTH=960;
|
||||||
constexpr const int RAY_VIEW_HEIGHT=720;
|
constexpr const int RAY_VIEW_HEIGHT=720;
|
||||||
constexpr const int RAY_VIEW_X=(1280 - RAY_VIEW_WIDTH);
|
constexpr const int SCREEN_WIDTH=1280;
|
||||||
|
constexpr const int RAY_VIEW_X=(SCREEN_WIDTH - RAY_VIEW_WIDTH);
|
||||||
constexpr const int RAY_VIEW_Y=0;
|
constexpr const int RAY_VIEW_Y=0;
|
||||||
constexpr const int SCREEN_HEIGHT=720;
|
constexpr const int SCREEN_HEIGHT=720;
|
||||||
constexpr const int SCREEN_WIDTH=1280;
|
|
||||||
constexpr const bool VSYNC=false;
|
constexpr const bool VSYNC=false;
|
||||||
constexpr const int FRAME_LIMIT=60;
|
constexpr const int FRAME_LIMIT=60;
|
||||||
constexpr const int NUM_SPRITES=1;
|
constexpr const int NUM_SPRITES=1;
|
||||||
|
@ -42,3 +42,10 @@ constexpr int MIN_FONT_SIZE = 20;
|
||||||
constexpr int STATUS_UI_WIDTH = 40;
|
constexpr int STATUS_UI_WIDTH = 40;
|
||||||
constexpr int STATUS_UI_HEIGHT = 30;
|
constexpr int STATUS_UI_HEIGHT = 30;
|
||||||
constexpr float PERCENT = 0.01f;
|
constexpr float PERCENT = 0.01f;
|
||||||
|
|
||||||
|
|
||||||
|
// for the panels/renderer
|
||||||
|
constexpr wchar_t BG_TILE = L'█';
|
||||||
|
constexpr wchar_t UI_BASE_CHAR = L'█';
|
||||||
|
constexpr int BG_BOX_OFFSET=5;
|
||||||
|
constexpr const char *FONT_FILE_NAME="./assets/text.otf";
|
||||||
|
|
133
gui.cpp
133
gui.cpp
|
@ -10,21 +10,82 @@
|
||||||
using namespace components;
|
using namespace components;
|
||||||
|
|
||||||
namespace gui {
|
namespace gui {
|
||||||
|
using ftxui::Color;
|
||||||
|
|
||||||
|
MapViewUI::MapViewUI(GameLevel &level) :
|
||||||
|
Panel(RAY_VIEW_X, 0, 0, 0, true),
|
||||||
|
$level(level)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void MapViewUI::update_level(GameLevel &level) {
|
||||||
|
$level = level;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 TileCell& 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, light_value](auto &pixel) {
|
||||||
|
pixel.foreground_color = Color::HSV(dnum * 20, 150, 200);
|
||||||
|
pixel.background_color = Color::HSV(30, 20, tile.bg_v * 50 * PERCENT);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
$canvas.DrawText(x * 2, y * 4, tile.display, [tile, light_value](auto &pixel) {
|
||||||
|
pixel.foreground_color = Color::HSV(tile.fg_h, tile.fg_s, tile.fg_v * light_value);
|
||||||
|
pixel.background_color = Color::HSV(tile.bg_h, tile.bg_s, tile.bg_v * 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);
|
||||||
|
}
|
||||||
|
|
||||||
FSM::FSM() :
|
FSM::FSM() :
|
||||||
$window(sf::VideoMode({SCREEN_WIDTH, SCREEN_HEIGHT}), "Zed's Raycaster Thing"),
|
$window(sf::VideoMode({SCREEN_WIDTH, SCREEN_HEIGHT}), "Zed's Raycaster Thing"),
|
||||||
$font{"./assets/text.otf"},
|
$renderer($window),
|
||||||
|
$level($levels.current()),
|
||||||
|
$map_view($level),
|
||||||
|
$font{FONT_FILE_NAME},
|
||||||
$text{$font},
|
$text{$font},
|
||||||
$map_display{$font},
|
|
||||||
$rayview($textures, RAY_VIEW_WIDTH, RAY_VIEW_HEIGHT)
|
$rayview($textures, RAY_VIEW_WIDTH, RAY_VIEW_HEIGHT)
|
||||||
{
|
{
|
||||||
$window.setVerticalSyncEnabled(VSYNC);
|
$window.setVerticalSyncEnabled(VSYNC);
|
||||||
$window.setFramerateLimit(FRAME_LIMIT);
|
$window.setFramerateLimit(FRAME_LIMIT);
|
||||||
$text.setPosition({10,10});
|
$text.setPosition({10,10});
|
||||||
$text.setFillColor({255,255,255});
|
$text.setFillColor({255,255,255});
|
||||||
$map_display.setPosition({10, SCREEN_HEIGHT-300});
|
|
||||||
$map_display.setFillColor({255,255,255});
|
|
||||||
$map_display.setLetterSpacing(0.5);
|
|
||||||
$map_display.setLineSpacing(0.9);
|
|
||||||
$textures.load_tiles();
|
$textures.load_tiles();
|
||||||
$textures.load_sprites();
|
$textures.load_sprites();
|
||||||
}
|
}
|
||||||
|
@ -33,6 +94,7 @@ namespace gui {
|
||||||
switch($state) {
|
switch($state) {
|
||||||
FSM_STATE(State, START, ev);
|
FSM_STATE(State, START, ev);
|
||||||
FSM_STATE(State, MOVING, ev);
|
FSM_STATE(State, MOVING, ev);
|
||||||
|
FSM_STATE(State, MAPPING, ev);
|
||||||
FSM_STATE(State, ROTATING, ev);
|
FSM_STATE(State, ROTATING, ev);
|
||||||
FSM_STATE(State, IDLE, ev);
|
FSM_STATE(State, IDLE, ev);
|
||||||
FSM_STATE(State, END, ev);
|
FSM_STATE(State, END, ev);
|
||||||
|
@ -41,12 +103,36 @@ namespace gui {
|
||||||
|
|
||||||
void FSM::START(Event ) {
|
void FSM::START(Event ) {
|
||||||
generate_map();
|
generate_map();
|
||||||
|
$level.world->set_the<Debug>({});
|
||||||
$rayview.init_shaders();
|
$rayview.init_shaders();
|
||||||
$rayview.set_position(RAY_VIEW_X, RAY_VIEW_Y);
|
$rayview.set_position(RAY_VIEW_X, RAY_VIEW_Y);
|
||||||
$rayview.position_camera($player.x + 0.5, $player.y + 0.5);
|
$rayview.position_camera($player.x + 0.5, $player.y + 0.5);
|
||||||
|
|
||||||
|
$renderer.init_terminal();
|
||||||
|
$map_view.create_render();
|
||||||
|
|
||||||
|
$renderer.resize_grid(MAX_FONT_SIZE, $map_view);
|
||||||
|
$map_view.resize_canvas();
|
||||||
|
|
||||||
state(State::IDLE);
|
state(State::IDLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FSM::MAPPING(Event ev) {
|
||||||
|
// BUG: can't close window when in mapping
|
||||||
|
switch(ev) {
|
||||||
|
case Event::MAP_OPEN:
|
||||||
|
state(State::IDLE);
|
||||||
|
break;
|
||||||
|
case Event::CLOSE:
|
||||||
|
state(State::IDLE);
|
||||||
|
break;
|
||||||
|
case Event::TICK:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
dbc::log("invalid event sent to MAPPING");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void FSM::MOVING(Event ) {
|
void FSM::MOVING(Event ) {
|
||||||
if($camera.play_move($rayview)) {
|
if($camera.play_move($rayview)) {
|
||||||
System::plan_motion(*$level.world, {size_t($camera.targetX), size_t($camera.targetY)});
|
System::plan_motion(*$level.world, {size_t($camera.targetX), size_t($camera.targetY)});
|
||||||
|
@ -89,6 +175,12 @@ namespace gui {
|
||||||
$camera.plan_rotate($rayview, -1);
|
$camera.plan_rotate($rayview, -1);
|
||||||
state(State::ROTATING);
|
state(State::ROTATING);
|
||||||
break;
|
break;
|
||||||
|
case FU::MAP_OPEN:
|
||||||
|
state(State::MAPPING);
|
||||||
|
break;
|
||||||
|
case FU::CLOSE:
|
||||||
|
dbc::log("Nothing to close.");
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
dbc::sentinel("unhandled event in IDLE");
|
dbc::sentinel("unhandled event in IDLE");
|
||||||
}
|
}
|
||||||
|
@ -140,6 +232,12 @@ namespace gui {
|
||||||
case KEY::R:
|
case KEY::R:
|
||||||
$stats.reset();
|
$stats.reset();
|
||||||
break;
|
break;
|
||||||
|
case KEY::M:
|
||||||
|
event(Event::MAP_OPEN);
|
||||||
|
break;
|
||||||
|
case KEY::Escape:
|
||||||
|
event(Event::CLOSE);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break; // ignored
|
break; // ignored
|
||||||
}
|
}
|
||||||
|
@ -156,15 +254,9 @@ namespace gui {
|
||||||
|
|
||||||
void FSM::draw_gui() {
|
void FSM::draw_gui() {
|
||||||
sf::RectangleShape rect({SCREEN_WIDTH - RAY_VIEW_WIDTH, SCREEN_HEIGHT});
|
sf::RectangleShape rect({SCREEN_WIDTH - RAY_VIEW_WIDTH, SCREEN_HEIGHT});
|
||||||
sf::RectangleShape map_rect({SCREEN_WIDTH - RAY_VIEW_WIDTH - 10, 300});
|
|
||||||
|
|
||||||
rect.setPosition({0,0});
|
rect.setPosition({0,0});
|
||||||
rect.setFillColor({50, 50, 50});
|
rect.setFillColor({50, 50, 50});
|
||||||
|
|
||||||
map_rect.setPosition({0, SCREEN_HEIGHT-300});
|
|
||||||
map_rect.setFillColor({20, 20, 20});
|
|
||||||
$window.draw(rect);
|
$window.draw(rect);
|
||||||
$window.draw(map_rect);
|
|
||||||
|
|
||||||
$text.setString(
|
$text.setString(
|
||||||
fmt::format("FPS\n"
|
fmt::format("FPS\n"
|
||||||
|
@ -184,15 +276,19 @@ namespace gui {
|
||||||
$rayview.$dirY, $rayview.$posX, $rayview.$posY));
|
$rayview.$dirY, $rayview.$posX, $rayview.$posY));
|
||||||
|
|
||||||
$window.draw($text);
|
$window.draw($text);
|
||||||
|
|
||||||
std::wstring map = $level.map->tiles().minimap(int($rayview.$posX), int($rayview.$posY));
|
|
||||||
$map_display.setString(map);
|
|
||||||
$window.draw($map_display);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FSM::render() {
|
void FSM::render() {
|
||||||
auto start = std::chrono::high_resolution_clock::now();
|
auto start = std::chrono::high_resolution_clock::now();
|
||||||
$rayview.draw($window);
|
|
||||||
|
if(in_state(State::MAPPING)) {
|
||||||
|
$window.clear();
|
||||||
|
$map_view.render();
|
||||||
|
$renderer.draw($map_view);
|
||||||
|
} else {
|
||||||
|
$rayview.draw($window);
|
||||||
|
}
|
||||||
|
|
||||||
auto end = std::chrono::high_resolution_clock::now();
|
auto end = std::chrono::high_resolution_clock::now();
|
||||||
auto elapsed = std::chrono::duration<double>(end - start);
|
auto elapsed = std::chrono::duration<double>(end - start);
|
||||||
$stats.sample(1/elapsed.count());
|
$stats.sample(1/elapsed.count());
|
||||||
|
@ -211,7 +307,7 @@ namespace gui {
|
||||||
}
|
}
|
||||||
|
|
||||||
void FSM::generate_map() {
|
void FSM::generate_map() {
|
||||||
$level = $levels.current();
|
// ZED: this should eventually go away now that level manager is in play
|
||||||
auto& player = $level.world->get_the<Player>();
|
auto& player = $level.world->get_the<Player>();
|
||||||
auto& player_position = $level.world->get<Position>(player.entity);
|
auto& player_position = $level.world->get<Position>(player.entity);
|
||||||
$player = player_position.location;
|
$player = player_position.location;
|
||||||
|
@ -222,6 +318,7 @@ namespace gui {
|
||||||
System::enemy_pathing($level);
|
System::enemy_pathing($level);
|
||||||
System::collision($level);
|
System::collision($level);
|
||||||
System::motion($level);
|
System::motion($level);
|
||||||
|
System::lighting($level);
|
||||||
System::death($level);
|
System::death($level);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
27
gui.hpp
27
gui.hpp
|
@ -5,11 +5,30 @@
|
||||||
#include "levelmanager.hpp"
|
#include "levelmanager.hpp"
|
||||||
#include "camera.hpp"
|
#include "camera.hpp"
|
||||||
#include "fsm.hpp"
|
#include "fsm.hpp"
|
||||||
|
#include "render.hpp"
|
||||||
|
#include "panel.hpp"
|
||||||
|
#include <ftxui/dom/canvas.hpp>
|
||||||
|
|
||||||
|
using ftxui::Canvas;
|
||||||
|
|
||||||
namespace gui {
|
namespace gui {
|
||||||
|
class MapViewUI : public Panel {
|
||||||
|
public:
|
||||||
|
Canvas $canvas;
|
||||||
|
GameLevel $level;
|
||||||
|
|
||||||
|
MapViewUI(GameLevel &level);
|
||||||
|
void create_render();
|
||||||
|
void resize_canvas();
|
||||||
|
void draw_map();
|
||||||
|
void update_level(GameLevel &level);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
enum class State {
|
enum class State {
|
||||||
START,
|
START,
|
||||||
MOVING,
|
MOVING,
|
||||||
|
MAPPING,
|
||||||
ROTATING,
|
ROTATING,
|
||||||
IDLE,
|
IDLE,
|
||||||
END
|
END
|
||||||
|
@ -22,6 +41,8 @@ namespace gui {
|
||||||
MOVE_BACK,
|
MOVE_BACK,
|
||||||
MOVE_LEFT,
|
MOVE_LEFT,
|
||||||
MOVE_RIGHT,
|
MOVE_RIGHT,
|
||||||
|
MAP_OPEN,
|
||||||
|
CLOSE,
|
||||||
ROTATE_LEFT,
|
ROTATE_LEFT,
|
||||||
ROTATE_RIGHT,
|
ROTATE_RIGHT,
|
||||||
QUIT
|
QUIT
|
||||||
|
@ -29,15 +50,16 @@ namespace gui {
|
||||||
|
|
||||||
class FSM : public DeadSimpleFSM<State, Event> {
|
class FSM : public DeadSimpleFSM<State, Event> {
|
||||||
public:
|
public:
|
||||||
GameLevel $level;
|
|
||||||
float $rotation = -30.0f;
|
float $rotation = -30.0f;
|
||||||
Point $player{0,0};
|
Point $player{0,0};
|
||||||
LevelManager $levels;
|
LevelManager $levels;
|
||||||
sf::RenderWindow $window;
|
sf::RenderWindow $window;
|
||||||
|
SFMLRender $renderer;
|
||||||
|
GameLevel $level;
|
||||||
|
MapViewUI $map_view;
|
||||||
CameraLOL $camera;
|
CameraLOL $camera;
|
||||||
sf::Font $font;
|
sf::Font $font;
|
||||||
sf::Text $text;
|
sf::Text $text;
|
||||||
sf::Text $map_display;
|
|
||||||
Stats $stats;
|
Stats $stats;
|
||||||
TexturePack $textures;
|
TexturePack $textures;
|
||||||
Raycaster $rayview;
|
Raycaster $rayview;
|
||||||
|
@ -48,6 +70,7 @@ namespace gui {
|
||||||
|
|
||||||
void START(Event );
|
void START(Event );
|
||||||
void MOVING(Event );
|
void MOVING(Event );
|
||||||
|
void MAPPING(Event);
|
||||||
void ROTATING(Event );
|
void ROTATING(Event );
|
||||||
void IDLE(Event ev);
|
void IDLE(Event ev);
|
||||||
void END(Event ev);
|
void END(Event ev);
|
||||||
|
|
2
main.cpp
2
main.cpp
|
@ -10,6 +10,8 @@ int main() {
|
||||||
// ZED: need to sort out how to deal with this in the FSM
|
// ZED: 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.keyboard();
|
main.keyboard();
|
||||||
|
} else if(main.in_state(gui::State::MAPPING)) {
|
||||||
|
main.keyboard();
|
||||||
} else{
|
} else{
|
||||||
main.event(gui::Event::TICK);
|
main.event(gui::Event::TICK);
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,17 +30,21 @@ sfml_main = dependency('sfml_main')
|
||||||
sfml_network = dependency('sfml_network')
|
sfml_network = dependency('sfml_network')
|
||||||
sfml_system = dependency('sfml_system')
|
sfml_system = dependency('sfml_system')
|
||||||
sfml_window = dependency('sfml_window')
|
sfml_window = dependency('sfml_window')
|
||||||
|
ftxui_screen = dependency('ftxui-screen')
|
||||||
|
ftxui_dom = dependency('ftxui-dom')
|
||||||
|
ftxui_component = dependency('ftxui-component')
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
fmt, json, opengl32, freetype2,
|
fmt, json, opengl32, freetype2,
|
||||||
flac, ogg, vorbis, vorbisfile, vorbisenc,
|
flac, ogg, vorbis, vorbisfile, vorbisenc,
|
||||||
winmm, gdi32, sfml_audio, sfml_graphics,
|
winmm, gdi32, sfml_audio, sfml_graphics,
|
||||||
sfml_main, sfml_network, sfml_system,
|
sfml_main, sfml_network, sfml_system,
|
||||||
sfml_window
|
sfml_window, ftxui_screen, ftxui_dom, ftxui_component
|
||||||
]
|
]
|
||||||
|
|
||||||
sources = [
|
sources = [
|
||||||
'animator.cpp',
|
'animator.cpp',
|
||||||
|
'ansi_parser.cpp',
|
||||||
'camera.cpp',
|
'camera.cpp',
|
||||||
'combat.cpp',
|
'combat.cpp',
|
||||||
'components.cpp',
|
'components.cpp',
|
||||||
|
@ -54,9 +58,11 @@ sources = [
|
||||||
'map.cpp',
|
'map.cpp',
|
||||||
'matrix.cpp',
|
'matrix.cpp',
|
||||||
'matrix.cpp',
|
'matrix.cpp',
|
||||||
|
'panel.cpp',
|
||||||
'pathing.cpp',
|
'pathing.cpp',
|
||||||
'rand.cpp',
|
'rand.cpp',
|
||||||
'raycaster.cpp',
|
'raycaster.cpp',
|
||||||
|
'render.cpp',
|
||||||
'save.cpp',
|
'save.cpp',
|
||||||
'shiterator.hpp',
|
'shiterator.hpp',
|
||||||
'spatialmap.cpp',
|
'spatialmap.cpp',
|
||||||
|
|
64
panel.cpp
Normal file
64
panel.cpp
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
#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);
|
||||||
|
}
|
60
panel.hpp
Normal file
60
panel.hpp
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
#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();
|
||||||
|
};
|
275
render.cpp
Normal file
275
render.cpp
Normal file
|
@ -0,0 +1,275 @@
|
||||||
|
#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);
|
||||||
|
}
|
80
render.hpp
Normal file
80
render.hpp
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
#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 = VIDEO_WINDOW_X;
|
||||||
|
unsigned int video_y = VIDEO_WINDOW_Y;
|
||||||
|
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();
|
||||||
|
};
|
25
systems.cpp
25
systems.cpp
|
@ -12,6 +12,7 @@ using std::string;
|
||||||
using namespace fmt;
|
using namespace fmt;
|
||||||
using namespace components;
|
using namespace components;
|
||||||
using lighting::LightSource;
|
using lighting::LightSource;
|
||||||
|
using ftxui::Color;
|
||||||
|
|
||||||
void System::lighting(GameLevel &level) {
|
void System::lighting(GameLevel &level) {
|
||||||
auto &light = *level.lights;
|
auto &light = *level.lights;
|
||||||
|
@ -221,3 +222,27 @@ void System::plan_motion(DinkyECS::World& world, Point move_to) {
|
||||||
motion.dx = move_to.x - player_position.location.x;
|
motion.dx = move_to.x - player_position.location.x;
|
||||||
motion.dy = move_to.y - player_position.location.y;
|
motion.dy = move_to.y - player_position.location.y;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This one is called inside the MapViewUI very often so
|
||||||
|
* just avoide 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) {
|
||||||
|
auto &tiles = map.tiles();
|
||||||
|
|
||||||
|
world.query<Position, Tile>([&](auto &ent[[maybe_unused]], auto &pos, auto &tile) {
|
||||||
|
if(pos.location.x >= cam_orig.x && pos.location.x <= cam_orig.x + view_x
|
||||||
|
&& pos.location.y >= cam_orig.y && pos.location.y <= cam_orig.y + view_y) {
|
||||||
|
Point loc = map.map_to_camera(pos.location, cam_orig);
|
||||||
|
|
||||||
|
float light_value = lights[pos.location.y][pos.location.x] * PERCENT;
|
||||||
|
const TileCell& 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.chr, [tile, light_value, cell](auto &pixel) {
|
||||||
|
pixel.foreground_color = Color::HSV(tile.fg_h, tile.fg_s, tile.fg_v * light_value);
|
||||||
|
pixel.background_color = Color::HSV(cell.bg_h, cell.bg_s, cell.bg_v * light_value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "components.hpp"
|
#include "components.hpp"
|
||||||
#include "levelmanager.hpp"
|
#include "levelmanager.hpp"
|
||||||
|
#include <ftxui/dom/canvas.hpp>
|
||||||
|
|
||||||
|
|
||||||
namespace System {
|
namespace System {
|
||||||
|
@ -16,4 +17,5 @@ namespace System {
|
||||||
void pickup(DinkyECS::World &world, DinkyECS::Entity actor, DinkyECS::Entity item);
|
void pickup(DinkyECS::World &world, DinkyECS::Entity actor, DinkyECS::Entity item);
|
||||||
void device(DinkyECS::World &world, DinkyECS::Entity actor, DinkyECS::Entity item);
|
void device(DinkyECS::World &world, DinkyECS::Entity actor, DinkyECS::Entity item);
|
||||||
void plan_motion(DinkyECS::World& world, Point move_to);
|
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);
|
||||||
}
|
}
|
||||||
|
|
18
tilemap.cpp
18
tilemap.cpp
|
@ -73,21 +73,3 @@ bool TileMap::INVARIANT() {
|
||||||
dbc::check(matrix::width($tile_ids) == $width, "$tile_ids has wrong width");
|
dbc::check(matrix::width($tile_ids) == $width, "$tile_ids has wrong width");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::wstring TileMap::minimap(size_t x, size_t y) {
|
|
||||||
string result;
|
|
||||||
|
|
||||||
for(matrix::box it{$tile_ids, x, y, 5}; it.next();) {
|
|
||||||
const TileCell &cell = $display[it.y][it.x];
|
|
||||||
if(it.x == x && it.y == y) {
|
|
||||||
result += "@";
|
|
||||||
} else {
|
|
||||||
result += cell.display;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(it.x == it.right - 1) result += "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> $converter;
|
|
||||||
return $converter.from_bytes(result);
|
|
||||||
}
|
|
||||||
|
|
|
@ -43,5 +43,4 @@ public:
|
||||||
|
|
||||||
void dump(int show_x=-1, int show_y=-1);
|
void dump(int show_x=-1, int show_y=-1);
|
||||||
bool INVARIANT();
|
bool INVARIANT();
|
||||||
std::wstring minimap(size_t x, size_t y);
|
|
||||||
};
|
};
|
||||||
|
|
15
wraps/ftxui.wrap
Normal file
15
wraps/ftxui.wrap
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
[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…
Add table
Add a link
Reference in a new issue