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
|
||||
ragel -o $@ $<
|
||||
|
||||
build:
|
||||
build: ansi_parser.cpp
|
||||
meson compile -j 10 -C builddir
|
||||
|
||||
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],
|
||||
"background": [230, 20, 120],
|
||||
"collision": true,
|
||||
"display": "█"
|
||||
"display": "\ua5b8"
|
||||
},
|
||||
"WALL_VINES": {
|
||||
"texture": "assets/wall_with_vines-256.png",
|
||||
"foreground": [40, 15, 125],
|
||||
"background": [200, 29, 75],
|
||||
"collision": false,
|
||||
"display":"#"
|
||||
"display":"\u0799"
|
||||
},
|
||||
"WALL_PILLAR": {
|
||||
"texture": "assets/wall_with_pillars-256.png",
|
||||
"foreground": [40, 15, 125],
|
||||
"background": [200, 29, 75],
|
||||
"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") {
|
||||
world.set<Loot>(entity, {config["amount"]});
|
||||
} 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") {
|
||||
world.set<EnemyConfig>(entity, {config["hearing_distance"]});
|
||||
} else if(comp_type == "Combat") {
|
||||
|
|
|
@ -32,6 +32,13 @@ namespace components {
|
|||
|
||||
struct Tile {
|
||||
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);
|
||||
};
|
||||
|
||||
|
|
|
@ -6,10 +6,10 @@ constexpr const int TEXTURE_WIDTH=256;
|
|||
constexpr const int TEXTURE_HEIGHT=256;
|
||||
constexpr const int RAY_VIEW_WIDTH=960;
|
||||
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 SCREEN_HEIGHT=720;
|
||||
constexpr const int SCREEN_WIDTH=1280;
|
||||
constexpr const bool VSYNC=false;
|
||||
constexpr const int FRAME_LIMIT=60;
|
||||
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_HEIGHT = 30;
|
||||
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;
|
||||
|
||||
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() :
|
||||
$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},
|
||||
$map_display{$font},
|
||||
$rayview($textures, RAY_VIEW_WIDTH, RAY_VIEW_HEIGHT)
|
||||
{
|
||||
$window.setVerticalSyncEnabled(VSYNC);
|
||||
$window.setFramerateLimit(FRAME_LIMIT);
|
||||
$text.setPosition({10,10});
|
||||
$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_sprites();
|
||||
}
|
||||
|
@ -33,6 +94,7 @@ namespace gui {
|
|||
switch($state) {
|
||||
FSM_STATE(State, START, ev);
|
||||
FSM_STATE(State, MOVING, ev);
|
||||
FSM_STATE(State, MAPPING, ev);
|
||||
FSM_STATE(State, ROTATING, ev);
|
||||
FSM_STATE(State, IDLE, ev);
|
||||
FSM_STATE(State, END, ev);
|
||||
|
@ -41,12 +103,36 @@ namespace gui {
|
|||
|
||||
void FSM::START(Event ) {
|
||||
generate_map();
|
||||
$level.world->set_the<Debug>({});
|
||||
$rayview.init_shaders();
|
||||
$rayview.set_position(RAY_VIEW_X, RAY_VIEW_Y);
|
||||
$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);
|
||||
}
|
||||
|
||||
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 ) {
|
||||
if($camera.play_move($rayview)) {
|
||||
System::plan_motion(*$level.world, {size_t($camera.targetX), size_t($camera.targetY)});
|
||||
|
@ -89,6 +175,12 @@ namespace gui {
|
|||
$camera.plan_rotate($rayview, -1);
|
||||
state(State::ROTATING);
|
||||
break;
|
||||
case FU::MAP_OPEN:
|
||||
state(State::MAPPING);
|
||||
break;
|
||||
case FU::CLOSE:
|
||||
dbc::log("Nothing to close.");
|
||||
break;
|
||||
default:
|
||||
dbc::sentinel("unhandled event in IDLE");
|
||||
}
|
||||
|
@ -140,6 +232,12 @@ namespace gui {
|
|||
case KEY::R:
|
||||
$stats.reset();
|
||||
break;
|
||||
case KEY::M:
|
||||
event(Event::MAP_OPEN);
|
||||
break;
|
||||
case KEY::Escape:
|
||||
event(Event::CLOSE);
|
||||
break;
|
||||
default:
|
||||
break; // ignored
|
||||
}
|
||||
|
@ -156,15 +254,9 @@ namespace gui {
|
|||
|
||||
void FSM::draw_gui() {
|
||||
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.setFillColor({50, 50, 50});
|
||||
|
||||
map_rect.setPosition({0, SCREEN_HEIGHT-300});
|
||||
map_rect.setFillColor({20, 20, 20});
|
||||
$window.draw(rect);
|
||||
$window.draw(map_rect);
|
||||
|
||||
$text.setString(
|
||||
fmt::format("FPS\n"
|
||||
|
@ -184,15 +276,19 @@ namespace gui {
|
|||
$rayview.$dirY, $rayview.$posX, $rayview.$posY));
|
||||
|
||||
$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() {
|
||||
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 elapsed = std::chrono::duration<double>(end - start);
|
||||
$stats.sample(1/elapsed.count());
|
||||
|
@ -211,7 +307,7 @@ namespace gui {
|
|||
}
|
||||
|
||||
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_position = $level.world->get<Position>(player.entity);
|
||||
$player = player_position.location;
|
||||
|
@ -222,6 +318,7 @@ namespace gui {
|
|||
System::enemy_pathing($level);
|
||||
System::collision($level);
|
||||
System::motion($level);
|
||||
System::lighting($level);
|
||||
System::death($level);
|
||||
}
|
||||
|
||||
|
|
27
gui.hpp
27
gui.hpp
|
@ -5,11 +5,30 @@
|
|||
#include "levelmanager.hpp"
|
||||
#include "camera.hpp"
|
||||
#include "fsm.hpp"
|
||||
#include "render.hpp"
|
||||
#include "panel.hpp"
|
||||
#include <ftxui/dom/canvas.hpp>
|
||||
|
||||
using ftxui::Canvas;
|
||||
|
||||
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 {
|
||||
START,
|
||||
MOVING,
|
||||
MAPPING,
|
||||
ROTATING,
|
||||
IDLE,
|
||||
END
|
||||
|
@ -22,6 +41,8 @@ namespace gui {
|
|||
MOVE_BACK,
|
||||
MOVE_LEFT,
|
||||
MOVE_RIGHT,
|
||||
MAP_OPEN,
|
||||
CLOSE,
|
||||
ROTATE_LEFT,
|
||||
ROTATE_RIGHT,
|
||||
QUIT
|
||||
|
@ -29,15 +50,16 @@ namespace gui {
|
|||
|
||||
class FSM : public DeadSimpleFSM<State, Event> {
|
||||
public:
|
||||
GameLevel $level;
|
||||
float $rotation = -30.0f;
|
||||
Point $player{0,0};
|
||||
LevelManager $levels;
|
||||
sf::RenderWindow $window;
|
||||
SFMLRender $renderer;
|
||||
GameLevel $level;
|
||||
MapViewUI $map_view;
|
||||
CameraLOL $camera;
|
||||
sf::Font $font;
|
||||
sf::Text $text;
|
||||
sf::Text $map_display;
|
||||
Stats $stats;
|
||||
TexturePack $textures;
|
||||
Raycaster $rayview;
|
||||
|
@ -48,6 +70,7 @@ namespace gui {
|
|||
|
||||
void START(Event );
|
||||
void MOVING(Event );
|
||||
void MAPPING(Event);
|
||||
void ROTATING(Event );
|
||||
void IDLE(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
|
||||
if(main.in_state(gui::State::IDLE)) {
|
||||
main.keyboard();
|
||||
} else if(main.in_state(gui::State::MAPPING)) {
|
||||
main.keyboard();
|
||||
} else{
|
||||
main.event(gui::Event::TICK);
|
||||
}
|
||||
|
|
|
@ -30,17 +30,21 @@ sfml_main = dependency('sfml_main')
|
|||
sfml_network = dependency('sfml_network')
|
||||
sfml_system = dependency('sfml_system')
|
||||
sfml_window = dependency('sfml_window')
|
||||
ftxui_screen = dependency('ftxui-screen')
|
||||
ftxui_dom = dependency('ftxui-dom')
|
||||
ftxui_component = dependency('ftxui-component')
|
||||
|
||||
dependencies = [
|
||||
fmt, json, opengl32, freetype2,
|
||||
flac, ogg, vorbis, vorbisfile, vorbisenc,
|
||||
winmm, gdi32, sfml_audio, sfml_graphics,
|
||||
sfml_main, sfml_network, sfml_system,
|
||||
sfml_window
|
||||
sfml_window, ftxui_screen, ftxui_dom, ftxui_component
|
||||
]
|
||||
|
||||
sources = [
|
||||
'animator.cpp',
|
||||
'ansi_parser.cpp',
|
||||
'camera.cpp',
|
||||
'combat.cpp',
|
||||
'components.cpp',
|
||||
|
@ -54,9 +58,11 @@ sources = [
|
|||
'map.cpp',
|
||||
'matrix.cpp',
|
||||
'matrix.cpp',
|
||||
'panel.cpp',
|
||||
'pathing.cpp',
|
||||
'rand.cpp',
|
||||
'raycaster.cpp',
|
||||
'render.cpp',
|
||||
'save.cpp',
|
||||
'shiterator.hpp',
|
||||
'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 components;
|
||||
using lighting::LightSource;
|
||||
using ftxui::Color;
|
||||
|
||||
void System::lighting(GameLevel &level) {
|
||||
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.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
|
||||
#include "components.hpp"
|
||||
#include "levelmanager.hpp"
|
||||
#include <ftxui/dom/canvas.hpp>
|
||||
|
||||
|
||||
namespace System {
|
||||
|
@ -16,4 +17,5 @@ namespace System {
|
|||
void pickup(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 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");
|
||||
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);
|
||||
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