Did a full code coverage review and improved many of the tests and a bunch of code. I'll do one more final walk through all the code before getting back to work on the new combat system.
This commit is contained in:
		
							parent
							
								
									113a4a3b3e
								
							
						
					
					
						commit
						d3158291f7
					
				
					 29 changed files with 119 additions and 1218 deletions
				
			
		
							
								
								
									
										2
									
								
								Makefile
									
										
									
									
									
								
							
							
						
						
									
										2
									
								
								Makefile
									
										
									
									
									
								
							|  | @ -6,7 +6,7 @@ reset: | |||
| %.cpp : %.rl | ||||
| 	ragel -o $@ $< | ||||
| 
 | ||||
| build: ansi_parser.cpp lel_parser.cpp | ||||
| build: lel_parser.cpp | ||||
| 	meson compile -j 10 -C builddir | ||||
| 
 | ||||
| release_build: | ||||
|  |  | |||
							
								
								
									
										376
									
								
								ansi_parser.cpp
									
										
									
									
									
								
							
							
						
						
									
										376
									
								
								ansi_parser.cpp
									
										
									
									
									
								
							|  | @ -1,376 +0,0 @@ | |||
| 
 | ||||
| #line 1 "ansi_parser.rl" | ||||
| #include <fmt/core.h> | ||||
| #include <string_view> | ||||
| #include "dbc.hpp" | ||||
| #include <SFML/Graphics.hpp> | ||||
| #include "ansi_parser.hpp" | ||||
| #include <iostream> | ||||
| 
 | ||||
| using namespace fmt; | ||||
| 
 | ||||
| 
 | ||||
| #line 122 "ansi_parser.rl" | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| #line 13 "ansi_parser.cpp" | ||||
| static const char _ansi_parser_actions[] = { | ||||
| 	0, 1, 0, 1, 3, 1, 4, 1, | ||||
| 	5, 1, 6, 1, 7, 1, 8, 1, | ||||
| 	9, 1, 10, 1, 11, 1, 15, 1, | ||||
| 	16, 2, 1, 12, 2, 1, 13, 2, | ||||
| 	6, 7, 2, 16, 5, 3, 1, 14, | ||||
| 	2 | ||||
| }; | ||||
| 
 | ||||
| static const char _ansi_parser_key_offsets[] = { | ||||
| 	0, 0, 1, 2, 11, 12, 14, 17, | ||||
| 	18, 22, 23, 27, 28, 29, 30, 31, | ||||
| 	33, 36, 38, 41, 43, 46, 47, 50, | ||||
| 	51, 52, 53, 54, 55 | ||||
| }; | ||||
| 
 | ||||
| static const int _ansi_parser_trans_keys[] = { | ||||
| 	27, 91, 48, 49, 50, 51, 52, 55, | ||||
| 	57, 53, 54, 109, 48, 109, 34, 48, | ||||
| 	55, 109, 50, 52, 55, 109, 109, 49, | ||||
| 	56, 57, 109, 109, 59, 50, 59, 48, | ||||
| 	57, 59, 48, 57, 48, 57, 59, 48, | ||||
| 	57, 48, 57, 109, 48, 57, 109, 56, | ||||
| 	57, 109, 59, 50, 109, 109, 27, 27, | ||||
| 	0 | ||||
| }; | ||||
| 
 | ||||
| static const char _ansi_parser_single_lengths[] = { | ||||
| 	0, 1, 1, 7, 1, 2, 3, 1, | ||||
| 	4, 1, 4, 1, 1, 1, 1, 0, | ||||
| 	1, 0, 1, 0, 1, 1, 3, 1, | ||||
| 	1, 1, 1, 1, 1 | ||||
| }; | ||||
| 
 | ||||
| static const char _ansi_parser_range_lengths[] = { | ||||
| 	0, 0, 0, 1, 0, 0, 0, 0, | ||||
| 	0, 0, 0, 0, 0, 0, 0, 1, | ||||
| 	1, 1, 1, 1, 1, 0, 0, 0, | ||||
| 	0, 0, 0, 0, 0 | ||||
| }; | ||||
| 
 | ||||
| static const char _ansi_parser_index_offsets[] = { | ||||
| 	0, 0, 2, 4, 13, 15, 18, 22, | ||||
| 	24, 29, 31, 36, 38, 40, 42, 44, | ||||
| 	46, 49, 51, 54, 56, 59, 61, 65, | ||||
| 	67, 69, 71, 73, 75 | ||||
| }; | ||||
| 
 | ||||
| static const char _ansi_parser_trans_targs[] = { | ||||
| 	2, 1, 3, 0, 4, 5, 8, 10, | ||||
| 	22, 26, 6, 7, 0, 28, 0, 6, | ||||
| 	28, 0, 7, 7, 7, 0, 28, 0, | ||||
| 	7, 7, 9, 28, 0, 28, 0, 11, | ||||
| 	12, 21, 28, 0, 28, 0, 13, 0, | ||||
| 	14, 0, 15, 0, 16, 0, 17, 16, | ||||
| 	0, 18, 0, 19, 18, 0, 20, 0, | ||||
| 	28, 20, 0, 28, 0, 23, 25, 28, | ||||
| 	0, 24, 0, 14, 0, 28, 0, 28, | ||||
| 	0, 2, 1, 2, 1, 0 | ||||
| }; | ||||
| 
 | ||||
| static const char _ansi_parser_trans_actions[] = { | ||||
| 	0, 7, 0, 0, 21, 21, 21, 21, | ||||
| 	21, 21, 21, 21, 0, 31, 0, 0, | ||||
| 	0, 0, 0, 0, 0, 0, 0, 0, | ||||
| 	0, 0, 0, 17, 0, 15, 0, 0, | ||||
| 	0, 0, 0, 0, 19, 0, 0, 0, | ||||
| 	3, 0, 0, 0, 1, 0, 25, 0, | ||||
| 	0, 1, 0, 28, 0, 0, 1, 0, | ||||
| 	37, 0, 0, 9, 0, 0, 0, 0, | ||||
| 	0, 0, 0, 5, 0, 11, 0, 13, | ||||
| 	0, 0, 7, 23, 34, 0 | ||||
| }; | ||||
| 
 | ||||
| static const char _ansi_parser_eof_actions[] = { | ||||
| 	0, 0, 0, 0, 0, 0, 0, 0, | ||||
| 	0, 0, 0, 0, 0, 0, 0, 0, | ||||
| 	0, 0, 0, 0, 0, 0, 0, 0, | ||||
| 	0, 0, 0, 0, 23 | ||||
| }; | ||||
| 
 | ||||
| static const int ansi_parser_start = 27; | ||||
| static const int ansi_parser_first_final = 27; | ||||
| static const int ansi_parser_error = 0; | ||||
| 
 | ||||
| static const int ansi_parser_en_main = 27; | ||||
| 
 | ||||
| 
 | ||||
| #line 125 "ansi_parser.rl" | ||||
| 
 | ||||
| #include <ftxui/screen/terminal.hpp> | ||||
| 
 | ||||
| ANSIParser::ANSIParser(sf::Color default_fg, sf::Color default_bg) : | ||||
|   $default_fg(default_fg), | ||||
|   $default_bg(default_bg) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| bool ANSIParser::parse(std::wstring_view codes, ColorCB color_cb, WriteCB write_cb) { | ||||
|   const wchar_t *start = nullptr; | ||||
|   int cs = 0; | ||||
|   unsigned int value = 0; | ||||
|   const wchar_t *p = codes.data(); | ||||
|   const wchar_t *pe = p + codes.size(); | ||||
|   const wchar_t *eof = pe; | ||||
|   sf::Color bgcolor($default_bg); | ||||
|   sf::Color color($default_fg); | ||||
|   sf::Color* target = &color; | ||||
| 
 | ||||
| 
 | ||||
| #line 120 "ansi_parser.cpp" | ||||
| 	{ | ||||
| 	cs = ansi_parser_start; | ||||
| 	} | ||||
| 
 | ||||
| #line 146 "ansi_parser.rl" | ||||
| 
 | ||||
| #line 123 "ansi_parser.cpp" | ||||
| 	{ | ||||
| 	int _klen; | ||||
| 	unsigned int _trans; | ||||
| 	const char *_acts; | ||||
| 	unsigned int _nacts; | ||||
| 	const int *_keys; | ||||
| 
 | ||||
| 	if ( p == pe ) | ||||
| 		goto _test_eof; | ||||
| 	if ( cs == 0 ) | ||||
| 		goto _out; | ||||
| _resume: | ||||
| 	_keys = _ansi_parser_trans_keys + _ansi_parser_key_offsets[cs]; | ||||
| 	_trans = _ansi_parser_index_offsets[cs]; | ||||
| 
 | ||||
| 	_klen = _ansi_parser_single_lengths[cs]; | ||||
| 	if ( _klen > 0 ) { | ||||
| 		const int *_lower = _keys; | ||||
| 		const int *_mid; | ||||
| 		const int *_upper = _keys + _klen - 1; | ||||
| 		while (1) { | ||||
| 			if ( _upper < _lower ) | ||||
| 				break; | ||||
| 
 | ||||
| 			_mid = _lower + ((_upper-_lower) >> 1); | ||||
| 			if ( (*p) < *_mid ) | ||||
| 				_upper = _mid - 1; | ||||
| 			else if ( (*p) > *_mid ) | ||||
| 				_lower = _mid + 1; | ||||
| 			else { | ||||
| 				_trans += (unsigned int)(_mid - _keys); | ||||
| 				goto _match; | ||||
| 			} | ||||
| 		} | ||||
| 		_keys += _klen; | ||||
| 		_trans += _klen; | ||||
| 	} | ||||
| 
 | ||||
| 	_klen = _ansi_parser_range_lengths[cs]; | ||||
| 	if ( _klen > 0 ) { | ||||
| 		const int *_lower = _keys; | ||||
| 		const int *_mid; | ||||
| 		const int *_upper = _keys + (_klen<<1) - 2; | ||||
| 		while (1) { | ||||
| 			if ( _upper < _lower ) | ||||
| 				break; | ||||
| 
 | ||||
| 			_mid = _lower + (((_upper-_lower) >> 1) & ~1); | ||||
| 			if ( (*p) < _mid[0] ) | ||||
| 				_upper = _mid - 2; | ||||
| 			else if ( (*p) > _mid[1] ) | ||||
| 				_lower = _mid + 2; | ||||
| 			else { | ||||
| 				_trans += (unsigned int)((_mid - _keys)>>1); | ||||
| 				goto _match; | ||||
| 			} | ||||
| 		} | ||||
| 		_trans += _klen; | ||||
| 	} | ||||
| 
 | ||||
| _match: | ||||
| 	cs = _ansi_parser_trans_targs[_trans]; | ||||
| 
 | ||||
| 	if ( _ansi_parser_trans_actions[_trans] == 0 ) | ||||
| 		goto _again; | ||||
| 
 | ||||
| 	_acts = _ansi_parser_actions + _ansi_parser_trans_actions[_trans]; | ||||
| 	_nacts = (unsigned int) *_acts++; | ||||
| 	while ( _nacts-- > 0 ) | ||||
| 	{ | ||||
| 		switch ( *_acts++ ) | ||||
| 		{ | ||||
| 	case 0: | ||||
| #line 14 "ansi_parser.rl" | ||||
| 	{ | ||||
|     start = p; | ||||
|   } | ||||
| 	break; | ||||
| 	case 1: | ||||
| #line 18 "ansi_parser.rl" | ||||
| 	{ | ||||
|     value = 0; | ||||
|     size_t len = p - start; | ||||
|     dbc::check(start[0] != '-', "negative numbers not supported"); | ||||
| 
 | ||||
|     switch(len) { | ||||
|       case 10:    value += (start[len-10] - '0') * 1000000000; [[fallthrough]]; | ||||
|       case  9:    value += (start[len- 9] - '0') * 100000000; [[fallthrough]]; | ||||
|       case  8:    value += (start[len- 8] - '0') * 10000000; [[fallthrough]]; | ||||
|       case  7:    value += (start[len- 7] - '0') * 1000000; [[fallthrough]]; | ||||
|       case  6:    value += (start[len- 6] - '0') * 100000; [[fallthrough]]; | ||||
|       case  5:    value += (start[len- 5] - '0') * 10000; [[fallthrough]]; | ||||
|       case  4:    value += (start[len- 4] - '0') * 1000; [[fallthrough]]; | ||||
|       case  3:    value += (start[len- 3] - '0') * 100; [[fallthrough]]; | ||||
|       case  2:    value += (start[len- 2] - '0') * 10; [[fallthrough]]; | ||||
|       case  1:    value += (start[len- 1] - '0'); | ||||
|         break; | ||||
|       default: | ||||
|         dbc::sentinel("can't process > 10 digits"); | ||||
|     } | ||||
|   } | ||||
| 	break; | ||||
| 	case 2: | ||||
| #line 40 "ansi_parser.rl" | ||||
| 	{ | ||||
|     color_cb(color, bgcolor); | ||||
|   } | ||||
| 	break; | ||||
| 	case 3: | ||||
| #line 43 "ansi_parser.rl" | ||||
| 	{ | ||||
|     target = &color; | ||||
|   } | ||||
| 	break; | ||||
| 	case 4: | ||||
| #line 46 "ansi_parser.rl" | ||||
| 	{ | ||||
|     target = &bgcolor; | ||||
|   } | ||||
| 	break; | ||||
| 	case 5: | ||||
| #line 50 "ansi_parser.rl" | ||||
| 	{ | ||||
|     write_cb((*p)); | ||||
|   } | ||||
| 	break; | ||||
| 	case 6: | ||||
| #line 54 "ansi_parser.rl" | ||||
| 	{ | ||||
|     color = $default_fg; | ||||
|     color_cb(color, bgcolor); | ||||
|   } | ||||
| 	break; | ||||
| 	case 7: | ||||
| #line 58 "ansi_parser.rl" | ||||
| 	{ | ||||
|     bgcolor = $default_bg; | ||||
|     color_cb(color, bgcolor); | ||||
|   } | ||||
| 	break; | ||||
| 	case 8: | ||||
| #line 62 "ansi_parser.rl" | ||||
| 	{ | ||||
|     color = $default_bg; | ||||
|     bgcolor = $default_fg; | ||||
|     color_cb(color, bgcolor); | ||||
|   } | ||||
| 	break; | ||||
| 	case 9: | ||||
| #line 67 "ansi_parser.rl" | ||||
| 	{ | ||||
|     color = $default_fg; | ||||
|     bgcolor = $default_bg; | ||||
|     color_cb(color, bgcolor); | ||||
|   } | ||||
| 	break; | ||||
| 	case 10: | ||||
| #line 72 "ansi_parser.rl" | ||||
| 	{ | ||||
|     color = sf::Color(100,100,100); | ||||
|     color_cb(color, bgcolor); | ||||
|   } | ||||
| 	break; | ||||
| 	case 11: | ||||
| #line 76 "ansi_parser.rl" | ||||
| 	{ | ||||
|     color = sf::Color::Red; | ||||
|     color_cb(color, bgcolor); | ||||
|   } | ||||
| 	break; | ||||
| 	case 12: | ||||
| #line 81 "ansi_parser.rl" | ||||
| 	{ target->r = value; } | ||||
| 	break; | ||||
| 	case 13: | ||||
| #line 82 "ansi_parser.rl" | ||||
| 	{ target->g = value; } | ||||
| 	break; | ||||
| 	case 14: | ||||
| #line 83 "ansi_parser.rl" | ||||
| 	{ target->b = value; } | ||||
| 	break; | ||||
| 	case 15: | ||||
| #line 84 "ansi_parser.rl" | ||||
| 	{ value = 0; } | ||||
| 	break; | ||||
| 	case 16: | ||||
| #line 85 "ansi_parser.rl" | ||||
| 	{} | ||||
| 	break; | ||||
| #line 296 "ansi_parser.cpp" | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| _again: | ||||
| 	if ( cs == 0 ) | ||||
| 		goto _out; | ||||
| 	if ( ++p != pe ) | ||||
| 		goto _resume; | ||||
| 	_test_eof: {} | ||||
| 	if ( p == eof ) | ||||
| 	{ | ||||
| 	const char *__acts = _ansi_parser_actions + _ansi_parser_eof_actions[cs]; | ||||
| 	unsigned int __nacts = (unsigned int) *__acts++; | ||||
| 	while ( __nacts-- > 0 ) { | ||||
| 		switch ( *__acts++ ) { | ||||
| 	case 16: | ||||
| #line 85 "ansi_parser.rl" | ||||
| 	{} | ||||
| 	break; | ||||
| #line 314 "ansi_parser.cpp" | ||||
| 		} | ||||
| 	} | ||||
| 	} | ||||
| 
 | ||||
| 	_out: {} | ||||
| 	} | ||||
| 
 | ||||
| #line 147 "ansi_parser.rl" | ||||
| 
 | ||||
|   bool good = pe - p == 0; | ||||
| 
 | ||||
|   if(!good) { | ||||
|     p -= 10; | ||||
|     // dear cthuhlu, save me from the pain that is wstring
 | ||||
|     for(int i = 0; i < 100; i++) { | ||||
|       try { | ||||
|         print("{}", p[i] == 0x1B ? '^' : char(p[i])); | ||||
|       } catch(...) { | ||||
|         print("?=", int(p[i])); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   (void)ansi_parser_first_final; | ||||
|   (void)ansi_parser_error; | ||||
|   (void)ansi_parser_en_main; | ||||
| 
 | ||||
| 
 | ||||
|   return good; | ||||
| } | ||||
|  | @ -1,23 +0,0 @@ | |||
| #pragma once | ||||
| #include <string_view> | ||||
| #include <SFML/Graphics.hpp> | ||||
| #include <codecvt> | ||||
| #include <functional> | ||||
| 
 | ||||
| typedef std::function<void(sf::Color bgcolor, sf::Color color)> ColorCB; | ||||
| 
 | ||||
| typedef std::function<void(wchar_t ch)> WriteCB; | ||||
| 
 | ||||
| class ANSIParser { | ||||
|   sf::Color $default_fg; | ||||
|   sf::Color $default_bg; | ||||
|   std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> $converter; | ||||
| 
 | ||||
| public: | ||||
|   ANSIParser(sf::Color default_fg, sf::Color default_bg); | ||||
| 
 | ||||
|   // disable copying
 | ||||
|   ANSIParser(ANSIParser& ap) = delete; | ||||
| 
 | ||||
|   bool parse(std::wstring_view codes, ColorCB color_cb, WriteCB write_cb); | ||||
| }; | ||||
							
								
								
									
										167
									
								
								ansi_parser.rl
									
										
									
									
									
								
							
							
						
						
									
										167
									
								
								ansi_parser.rl
									
										
									
									
									
								
							|  | @ -1,167 +0,0 @@ | |||
| #include <fmt/core.h> | ||||
| #include <string_view> | ||||
| #include "dbc.hpp" | ||||
| #include <SFML/Graphics.hpp> | ||||
| #include "ansi_parser.hpp" | ||||
| #include <iostream> | ||||
| 
 | ||||
| using namespace fmt; | ||||
| 
 | ||||
| %%{ | ||||
|   machine ansi_parser; | ||||
|   alphtype int; | ||||
| 
 | ||||
|   action tstart { | ||||
|     start = fpc; | ||||
|   } | ||||
| 
 | ||||
|   action number { | ||||
|     value = 0; | ||||
|     size_t len = fpc - start; | ||||
|     dbc::check(start[0] != '-', "negative numbers not supported"); | ||||
| 
 | ||||
|     switch(len) { | ||||
|       case 10:    value += (start[len-10] - '0') * 1000000000; [[fallthrough]]; | ||||
|       case  9:    value += (start[len- 9] - '0') * 100000000; [[fallthrough]]; | ||||
|       case  8:    value += (start[len- 8] - '0') * 10000000; [[fallthrough]]; | ||||
|       case  7:    value += (start[len- 7] - '0') * 1000000; [[fallthrough]]; | ||||
|       case  6:    value += (start[len- 6] - '0') * 100000; [[fallthrough]]; | ||||
|       case  5:    value += (start[len- 5] - '0') * 10000; [[fallthrough]]; | ||||
|       case  4:    value += (start[len- 4] - '0') * 1000; [[fallthrough]]; | ||||
|       case  3:    value += (start[len- 3] - '0') * 100; [[fallthrough]]; | ||||
|       case  2:    value += (start[len- 2] - '0') * 10; [[fallthrough]]; | ||||
|       case  1:    value += (start[len- 1] - '0'); | ||||
|         break; | ||||
|       default: | ||||
|         dbc::sentinel("can't process > 10 digits"); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   action color_out { | ||||
|     color_cb(color, bgcolor); | ||||
|   } | ||||
|   action is_fg { | ||||
|     target = &color; | ||||
|   } | ||||
|   action is_bg { | ||||
|     target = &bgcolor; | ||||
|   } | ||||
| 
 | ||||
|   action out { | ||||
|     write_cb(fc); | ||||
|   } | ||||
| 
 | ||||
|   action reset_fg { | ||||
|     color = $default_fg; | ||||
|     color_cb(color, bgcolor); | ||||
|   } | ||||
|   action reset_bg { | ||||
|     bgcolor = $default_bg; | ||||
|     color_cb(color, bgcolor); | ||||
|   } | ||||
|   action invert { | ||||
|     color = $default_bg; | ||||
|     bgcolor = $default_fg; | ||||
|     color_cb(color, bgcolor); | ||||
|   } | ||||
|   action reset_invert { | ||||
|     color = $default_fg; | ||||
|     bgcolor = $default_bg; | ||||
|     color_cb(color, bgcolor); | ||||
|   } | ||||
|   action half_bright { | ||||
|     color = sf::Color(100,100,100); | ||||
|     color_cb(color, bgcolor); | ||||
|   } | ||||
|   action red_text { | ||||
|     color = sf::Color::Red; | ||||
|     color_cb(color, bgcolor); | ||||
|   } | ||||
| 
 | ||||
|   action red { target->r = value; } | ||||
|   action blue { target->g = value; } | ||||
|   action green { target->b = value; } | ||||
|   action start { value = 0; } | ||||
|   action end {} | ||||
|   action log { println("command {}", (char)fc); } | ||||
| 
 | ||||
|   ESC = 0x1B; | ||||
|   start = ESC "["; | ||||
|   fg = "38;" %is_fg; | ||||
|   bg = "48;" %is_bg; | ||||
|   reset = ("39" %reset_fg | "49" %reset_bg); | ||||
|   num = digit+ >tstart %number; | ||||
|   color256 = "5;"; | ||||
|   color24b = "2;"; | ||||
| 
 | ||||
|   ansi = ( | ||||
|       start %start | ||||
|       ( | ||||
|         reset | | ||||
|         "0" %reset_fg %reset_bg | | ||||
|         "1"  | | ||||
|         "2" %half_bright | | ||||
|         "3"  | | ||||
|         "4"  | | ||||
|         "5"  | | ||||
|         "6"  | | ||||
|         "7" %invert | | ||||
|         "31" %red_text | | ||||
|         "22" | | ||||
|         "24" | | ||||
|         "27" %reset_invert | | ||||
|         "9" ["0"-"7"] | | ||||
|         "10" ["0"-"7"] | | ||||
|         (fg|bg) (color24b num %red ";" num %blue ";" num %green ) %color_out | ||||
|       ) "m" %end | ||||
|   ); | ||||
| 
 | ||||
|   other = (any+ @out -- ESC)*; | ||||
| 
 | ||||
|   main := (other :> ansi)**; | ||||
| }%% | ||||
| 
 | ||||
| %% write data; | ||||
| 
 | ||||
| #include <ftxui/screen/terminal.hpp> | ||||
| 
 | ||||
| ANSIParser::ANSIParser(sf::Color default_fg, sf::Color default_bg) : | ||||
|   $default_fg(default_fg), | ||||
|   $default_bg(default_bg) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| bool ANSIParser::parse(std::wstring_view codes, ColorCB color_cb, WriteCB write_cb) { | ||||
|   const wchar_t *start = nullptr; | ||||
|   int cs = 0; | ||||
|   unsigned int value = 0; | ||||
|   const wchar_t *p = codes.data(); | ||||
|   const wchar_t *pe = p + codes.size(); | ||||
|   const wchar_t *eof = pe; | ||||
|   sf::Color bgcolor($default_bg); | ||||
|   sf::Color color($default_fg); | ||||
|   sf::Color* target = &color; | ||||
| 
 | ||||
|   %% write init; | ||||
|   %% write exec; | ||||
| 
 | ||||
|   bool good = pe - p == 0; | ||||
| 
 | ||||
|   if(!good) { | ||||
|     p -= 10; | ||||
|     // dear cthuhlu, save me from the pain that is wstring | ||||
|     for(int i = 0; i < 100; i++) { | ||||
|       try { | ||||
|         print("{}", p[i] == 0x1B ? '^' : char(p[i])); | ||||
|       } catch(...) { | ||||
|         print("?=", int(p[i])); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   (void)ansi_parser_first_final; | ||||
|   (void)ansi_parser_error; | ||||
|   (void)ansi_parser_en_main; | ||||
| 
 | ||||
|   return good; | ||||
| } | ||||
|  | @ -3,7 +3,7 @@ | |||
|     "components": [ | ||||
|       {"_type": "BossFight", | ||||
|         "background": "boss_fight_background", | ||||
|         "stage": false, | ||||
|         "stage": null, | ||||
|         "weapon_sound": "Sword_Hit_2" | ||||
|       }, | ||||
|       {"_type": "Combat", "hp": 20, "max_hp": 20, "damage": 20, "dead": false}, | ||||
|  | @ -24,7 +24,7 @@ | |||
|     "components": [ | ||||
|       {"_type": "BossFight", | ||||
|         "background": "devils_fingers_background", | ||||
|         "stage": false, | ||||
|         "stage": "devils_fingers_stage", | ||||
|         "weapon_sound": "Sword_Hit_2" | ||||
|       }, | ||||
|       {"_type": "Combat", "hp": 20, "max_hp": 20, "damage": 20, "dead": false}, | ||||
|  |  | |||
|  | @ -178,6 +178,8 @@ void Autowalker::rotate_player(Point current, Point target) { | |||
|     facing = fsm.$main_ui.$compass_dir; | ||||
|   } | ||||
| 
 | ||||
|   while(fsm.in_state(gui::State::ROTATING)) send_event(gui::Event::TICK); | ||||
| 
 | ||||
|   dbc::check(fsm.$main_ui.$compass_dir == target_facing, | ||||
|       "player isn't facing the correct direction"); | ||||
| } | ||||
|  | @ -229,6 +231,14 @@ void Autowalker::handle_player_walk(ai::State& start, ai::State& goal) { | |||
|     send_event(gui::Event::ATTACK); | ||||
|   } else if(action.name == "kill_enemy") { | ||||
|     status("KILLING ENEMY"); | ||||
| 
 | ||||
|     // TODO: find the enemy and then rotate toward them
 | ||||
|     Point current = get_current_position(); | ||||
|     if(fsm.in_state(gui::State::IN_COMBAT)) { | ||||
|       rotate_player(current, {current.x - 1, current.y - 1}); | ||||
|       dbc::log("TODO: you should find the enemy and face them instead of THIS GARBAGE!"); | ||||
|     } | ||||
| 
 | ||||
|     process_combat(); | ||||
|   } else if(action.name == "use_healing") { | ||||
|     status("USING HEALING"); | ||||
|  | @ -283,7 +293,6 @@ void Autowalker::process_move(Pathing& paths) { | |||
|   } | ||||
| 
 | ||||
|   rotate_player(current, target); | ||||
|   while(fsm.in_state(gui::State::ROTATING)) send_event(gui::Event::TICK); | ||||
| 
 | ||||
|   send_event(gui::Event::MOVE_FORWARD); | ||||
|   while(fsm.in_state(gui::State::MOVING)) send_event(gui::Event::TICK); | ||||
|  |  | |||
|  | @ -43,11 +43,11 @@ namespace components { | |||
|       case ease::SINE: | ||||
|         return ease::sine(float(frames) / subframe * ease_rate); | ||||
|       case ease::OUT_CIRC: | ||||
|         return ease::sine(ease::out_circ(float(frames) / subframe * ease_rate)); | ||||
|         return ease::out_circ(ease::sine(float(frames) / subframe * ease_rate)); | ||||
|       case ease::OUT_BOUNCE: | ||||
|         return ease::sine(ease::out_bounce(float(frames) / subframe * ease_rate)); | ||||
|         return ease::out_bounce(ease::sine(float(frames) / subframe * ease_rate)); | ||||
|       case ease::IN_OUT_BACK: | ||||
|         return ease::sine(ease::in_out_back(float(frames) / subframe * ease_rate)); | ||||
|         return ease::in_out_back(ease::sine(float(frames) / subframe * ease_rate)); | ||||
|       default: | ||||
|         dbc::sentinel( | ||||
|             fmt::format("Invalid easing {} given to animation", | ||||
|  |  | |||
|  | @ -120,7 +120,7 @@ namespace components { | |||
|     bool stationary = false; | ||||
|     int current = 0; | ||||
|     bool playing = false; | ||||
|     float subframe = 0; | ||||
|     float subframe = 1.0f; | ||||
|     int texture_width = TEXTURE_WIDTH; | ||||
| 
 | ||||
|     void play(); | ||||
|  |  | |||
|  | @ -15,13 +15,6 @@ json &Config::operator[](const std::string &key) { | |||
|   return $config[key]; | ||||
| } | ||||
| 
 | ||||
| std::wstring Config::wstring(const std::string key) { | ||||
|   dbc::check($config.contains(key), fmt::format("ERROR wstring in config, key {} doesn't exist.", key)); | ||||
|   std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> $converter; | ||||
|   const std::string& str_val = $config[key]; | ||||
|   return $converter.from_bytes(str_val); | ||||
| } | ||||
| 
 | ||||
| std::wstring Config::wstring(const std::string main_key, const std::string sub_key) { | ||||
|   dbc::check($config.contains(main_key), fmt::format("ERROR wstring main/key in config, main_key {} doesn't exist.", main_key)); | ||||
|   dbc::check($config[main_key].contains(sub_key), fmt::format("ERROR wstring in config, main_key/key {}/{} doesn't exist.", main_key, sub_key)); | ||||
|  |  | |||
|  | @ -14,7 +14,6 @@ struct Config { | |||
| 
 | ||||
|   nlohmann::json &operator[](const std::string &key); | ||||
|   nlohmann::json &json() { return $config; }; | ||||
|   std::wstring wstring(const std::string main_key); | ||||
|   std::wstring wstring(const std::string main_key, const std::string sub_key); | ||||
|   std::vector<std::string> keys(); | ||||
| }; | ||||
|  |  | |||
|  | @ -4,7 +4,7 @@ | |||
| namespace ease { | ||||
| 
 | ||||
|   enum Style { | ||||
|     NONE, SINE, OUT_CIRC, OUT_BOUNCE, IN_OUT_BACK | ||||
|     NONE, SINE, OUT_CIRC, OUT_BOUNCE, IN_OUT_BACK, FUCKFACE | ||||
|   }; | ||||
| 
 | ||||
|   inline double sine(double x) { | ||||
|  | @ -12,7 +12,7 @@ namespace ease { | |||
|   } | ||||
| 
 | ||||
|   inline double out_circ(double x) { | ||||
|     return std::sqrt(1.0f - std::pow(x - 1.0f, 2.0f)); | ||||
|     return std::sqrt(1.0f - ((x - 1.0f) * (x - 1.0f))); | ||||
|   } | ||||
| 
 | ||||
|   inline double out_bounce(double x) { | ||||
|  |  | |||
							
								
								
									
										11
									
								
								gui_fsm.cpp
									
										
									
									
									
								
							
							
						
						
									
										11
									
								
								gui_fsm.cpp
									
										
									
									
									
								
							|  | @ -15,7 +15,6 @@ namespace gui { | |||
|   FSM::FSM() : | ||||
|     $window(sf::VideoMode({SCREEN_WIDTH, SCREEN_HEIGHT}), "Zed's Raycaster Thing"), | ||||
|     $main_ui($window), | ||||
|     $renderer($window), | ||||
|     $level($levels.current()), | ||||
|     $map_ui($level), | ||||
|     $combat_ui($level), | ||||
|  | @ -53,11 +52,6 @@ namespace gui { | |||
|     $boss_fight_ui = $levels.create_bossfight($level.world); | ||||
|     $boss_fight_ui->init(); | ||||
| 
 | ||||
|     $renderer.init_terminal(); | ||||
|     $map_ui.create_render(); | ||||
|     $map_ui.resize_canvas(); | ||||
|     $renderer.resize_grid(BASE_MAP_FONT_SIZE, $map_ui); | ||||
| 
 | ||||
|     run_systems(); | ||||
|     state(State::IDLE); | ||||
|   } | ||||
|  | @ -106,6 +100,7 @@ namespace gui { | |||
|         break; | ||||
|       default: | ||||
|         dbc::log(fmt::format("In ATTACKING state, unhandled event {}", (int)ev)); | ||||
|         state(State::IDLE); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|  | @ -152,8 +147,6 @@ namespace gui { | |||
|         state(State::ROTATING); | ||||
|         break; | ||||
|       case MAP_OPEN: | ||||
|         $renderer.resize_grid(BASE_MAP_FONT_SIZE, $map_ui); | ||||
|         $map_ui.resize_canvas(); | ||||
|         state(State::MAPPING); | ||||
|         break; | ||||
|       case ATTACK: | ||||
|  | @ -327,8 +320,6 @@ namespace gui { | |||
|   void FSM::render() { | ||||
|     if(in_state(State::MAPPING)) { | ||||
|       $window.clear(); | ||||
|       $map_ui.render(); | ||||
|       $renderer.draw($map_ui); | ||||
|     } else if(in_state(State::NEXT_LEVEL)) { | ||||
|       $window.clear(); | ||||
|       $boss_fight_ui->render($window); | ||||
|  |  | |||
|  | @ -3,7 +3,6 @@ | |||
| #include "stats.hpp" | ||||
| #include "levelmanager.hpp" | ||||
| #include "fsm.hpp" | ||||
| #include "render.hpp" | ||||
| #include "map_view.hpp" | ||||
| #include "main_ui.hpp" | ||||
| #include "combat_ui.hpp" | ||||
|  | @ -49,7 +48,6 @@ namespace gui { | |||
|       bool autowalking = false; | ||||
|       LevelManager $levels; | ||||
|       MainUI $main_ui; | ||||
|       SFMLRender $renderer; | ||||
|       GameLevel $level; | ||||
|       shared_ptr<BossFightUI> $boss_fight_ui = nullptr; | ||||
|       MapViewUI $map_ui; | ||||
|  |  | |||
							
								
								
									
										51
									
								
								map_view.cpp
									
										
									
									
									
								
							
							
						
						
									
										51
									
								
								map_view.cpp
									
										
									
									
									
								
							|  | @ -1,14 +1,11 @@ | |||
| #include "map_view.hpp" | ||||
| #include <functional> | ||||
| #include <string> | ||||
| #include "systems.hpp" | ||||
| 
 | ||||
| namespace gui { | ||||
|   using namespace components; | ||||
|   using ftxui::Color; | ||||
| 
 | ||||
|   MapViewUI::MapViewUI(GameLevel &level) : | ||||
|     Panel(0, 0, 0, 0, true), | ||||
|     $level(level) | ||||
|   { | ||||
|   } | ||||
|  | @ -18,54 +15,6 @@ namespace gui { | |||
|   } | ||||
| 
 | ||||
|   void MapViewUI::draw_map() { | ||||
|     const auto& debug = $level.world->get_the<Debug>(); | ||||
|     const auto& player = $level.world->get_the<Player>(); | ||||
|     const auto& player_position = $level.world->get<Position>(player.entity); | ||||
|     Point start = $level.map->center_camera(player_position.location, width, height); | ||||
|     auto &tiles = $level.map->tiles(); | ||||
|     auto &paths = $level.map->paths(); | ||||
|     auto &lighting = $level.lights->lighting(); | ||||
| 
 | ||||
|     // WARN: this is exploiting that -1 in size_t becomes largest
 | ||||
|     size_t end_x = std::min(size_t(width), $level.map->width() - start.x); | ||||
|     size_t end_y = std::min(size_t(height), $level.map->height() - start.y); | ||||
| 
 | ||||
|     for(size_t y = 0; y < end_y; ++y) { | ||||
|       for(size_t x = 0; x < end_x; ++x) | ||||
|       { | ||||
|         const Tile& tile = tiles.at(start.x+x, start.y+y); | ||||
|         // light value is an integer that's a percent
 | ||||
|         float light_value = debug.LIGHT ? 80 * PERCENT : lighting[start.y+y][start.x+x] * PERCENT; | ||||
|         int dnum = debug.PATHS ? paths[start.y+y][start.x+x] : WALL_PATH_LIMIT; | ||||
| 
 | ||||
|         if(debug.PATHS && dnum != WALL_PATH_LIMIT) { | ||||
|           string num = dnum > 15 ? "*" : fmt::format("{:x}", dnum); | ||||
| 
 | ||||
|           $canvas.DrawText(x * 2, y * 4, num, [dnum, tile](auto &pixel) { | ||||
|               pixel.foreground_color = Color::HSV(dnum * 20, 150, 200); | ||||
|               pixel.background_color = Color::HSV(30, 20, tile.foreground[2] * 50 * PERCENT); | ||||
|               }); | ||||
|         } else { | ||||
|           $canvas.DrawText(x * 2, y * 4, tile.display, [tile, light_value](auto &pixel) { | ||||
|               pixel.foreground_color = Color::HSV(tile.foreground[0], tile.foreground[1], tile.foreground[2] * light_value); | ||||
|               pixel.background_color = Color::HSV(tile.background[0], tile.background[1], tile.background[2] * light_value); | ||||
|               }); | ||||
|         } | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     System::draw_entities(*$level.world, *$level.map, lighting, $canvas, start, width, height); | ||||
|   } | ||||
| 
 | ||||
|   void MapViewUI::create_render() { | ||||
|     set_renderer(Renderer([&] { | ||||
|           draw_map(); | ||||
|           return canvas($canvas); | ||||
|           })); | ||||
|   } | ||||
| 
 | ||||
|   void MapViewUI::resize_canvas() { | ||||
|     // set canvas to best size
 | ||||
|     $canvas = Canvas(width * 2, height * 4); | ||||
|   } | ||||
| } | ||||
|  |  | |||
|  | @ -1,19 +1,12 @@ | |||
| #pragma once | ||||
| #include "panel.hpp" | ||||
| #include <ftxui/dom/canvas.hpp> | ||||
| #include "levelmanager.hpp" | ||||
| 
 | ||||
| using ftxui::Canvas; | ||||
| 
 | ||||
| namespace gui { | ||||
|   class MapViewUI : public Panel { | ||||
|   class MapViewUI { | ||||
|     public: | ||||
|       Canvas $canvas; | ||||
|       GameLevel $level; | ||||
| 
 | ||||
|       MapViewUI(GameLevel &level); | ||||
|       void create_render(); | ||||
|       void resize_canvas(); | ||||
|       void draw_map(); | ||||
|       void update_level(GameLevel &level); | ||||
|   }; | ||||
|  |  | |||
							
								
								
									
										15
									
								
								meson.build
									
										
									
									
									
								
							
							
						
						
									
										15
									
								
								meson.build
									
										
									
									
									
								
							|  | @ -68,22 +68,17 @@ sfml_system = dependency('sfml_system') | |||
| sfml_window = dependency('sfml_window', | ||||
|   default_options: ['default_library=shared']) | ||||
| 
 | ||||
| ftxui_screen = dependency('ftxui-screen') | ||||
| ftxui_dom = dependency('ftxui-dom') | ||||
| ftxui_component = dependency('ftxui-component') | ||||
| 
 | ||||
| dependencies += [ | ||||
|   fmt, json, freetype2, | ||||
|   flac, ogg, vorbis, vorbisfile, vorbisenc, | ||||
|   sfml_audio, sfml_graphics, | ||||
|   sfml_network, sfml_system, | ||||
|   sfml_window, ftxui_screen, ftxui_dom, ftxui_component | ||||
|   sfml_window | ||||
| ] | ||||
| 
 | ||||
| sources = [ | ||||
|   'ai.cpp', | ||||
|   'ai_debug.cpp', | ||||
|   'ansi_parser.cpp', | ||||
|   'autowalker.cpp', | ||||
|   'boss_fight_ui.cpp', | ||||
|   'camera.cpp', | ||||
|  | @ -106,11 +101,9 @@ sources = [ | |||
|   'matrix.cpp', | ||||
|   'matrix.cpp', | ||||
|   'overlay_ui.cpp', | ||||
|   'panel.cpp', | ||||
|   'pathing.cpp', | ||||
|   'rand.cpp', | ||||
|   'raycaster.cpp', | ||||
|   'render.cpp', | ||||
|   'rituals.cpp', | ||||
|   'save.cpp', | ||||
|   'shiterator.hpp', | ||||
|  | @ -125,15 +118,14 @@ sources = [ | |||
| ] | ||||
| 
 | ||||
| executable('runtests', sources + [ | ||||
|   'tests/ansi_parser.cpp', | ||||
|   'tests/ai.cpp', | ||||
|   'tests/base.cpp', | ||||
|   'tests/rituals.cpp', | ||||
|   'tests/components.cpp', | ||||
|   'tests/config.cpp', | ||||
|   'tests/dbc.cpp', | ||||
|   'tests/dinkyecs.cpp', | ||||
|   'tests/easings.cpp', | ||||
|   'tests/fsm.cpp', | ||||
|   'tests/ai.cpp', | ||||
|   'tests/guecs.cpp', | ||||
|   'tests/inventory.cpp', | ||||
|   'tests/lel.cpp', | ||||
|  | @ -142,6 +134,7 @@ executable('runtests', sources + [ | |||
|   'tests/map.cpp', | ||||
|   'tests/matrix.cpp', | ||||
|   'tests/pathing.cpp', | ||||
|   'tests/rituals.cpp', | ||||
|   'tests/sound.cpp', | ||||
|   'tests/spatialmap.cpp', | ||||
|   'tests/textures.cpp', | ||||
|  |  | |||
							
								
								
									
										64
									
								
								panel.cpp
									
										
									
									
									
								
							
							
						
						
									
										64
									
								
								panel.cpp
									
										
									
									
									
								
							|  | @ -1,64 +0,0 @@ | |||
| #include "panel.hpp" | ||||
| #include "dbc.hpp" | ||||
| 
 | ||||
| void Panel::resize(int w, int h) { | ||||
|   $dirty = true; | ||||
|   width = w; | ||||
|   height = h; | ||||
|   $screen = Screen(width, height); | ||||
| } | ||||
| 
 | ||||
| void Panel::set_renderer(Component renderer) { | ||||
|   $dirty = true; | ||||
|   $component = renderer; | ||||
| } | ||||
| 
 | ||||
| void Panel::add(Component child) { | ||||
|   dbc::pre("must set_renderer first", $component != nullptr); | ||||
|   $dirty = true; | ||||
|   $component->Add(child); | ||||
| } | ||||
| 
 | ||||
| void Panel::render() { | ||||
|   $dirty = true; | ||||
|   if(must_clear) $screen.Clear(); | ||||
|   Render($screen, $component->Render()); | ||||
| } | ||||
| 
 | ||||
| const std::wstring& Panel::to_string() { | ||||
|   if($dirty) { | ||||
|     std::string as_text = $screen.ToString(); | ||||
|     $screenout = $converter.from_bytes(as_text); | ||||
|     $dirty = false; | ||||
|   } | ||||
| 
 | ||||
|   return $screenout; | ||||
| } | ||||
| 
 | ||||
| void Panel::mouse_click(ftxui::Mouse::Button btn, Point pos) { | ||||
|   ftxui::Mouse mev{ | ||||
|     .button=btn, | ||||
|     .motion=ftxui::Mouse::Motion::Pressed, | ||||
|     .x=int(pos.x), .y=int(pos.y) | ||||
|   }; | ||||
| 
 | ||||
|   $component->OnEvent(ftxui::Event::Mouse("", mev)); | ||||
| } | ||||
| 
 | ||||
| void Panel::mouse_release(ftxui::Mouse::Button btn, Point pos) { | ||||
|   ftxui::Mouse mev{ | ||||
|     .button=btn, | ||||
|     .motion=ftxui::Mouse::Motion::Released, | ||||
|     .x=int(pos.x), .y=int(pos.y) | ||||
|   }; | ||||
| 
 | ||||
|   $component->OnEvent(ftxui::Event::Mouse("", mev)); | ||||
| } | ||||
| 
 | ||||
| const Screen& Panel::screen() { | ||||
|   return $screen; | ||||
| } | ||||
| 
 | ||||
| void Panel::key_press(ftxui::Event event) { | ||||
|   $component->OnEvent(event); | ||||
| } | ||||
							
								
								
									
										60
									
								
								panel.hpp
									
										
									
									
									
								
							
							
						
						
									
										60
									
								
								panel.hpp
									
										
									
									
									
								
							|  | @ -1,60 +0,0 @@ | |||
| #pragma once | ||||
| #include <ftxui/dom/node.hpp>      // for Render
 | ||||
| #include <ftxui/component/component.hpp> | ||||
| #include <ftxui/component/mouse.hpp> | ||||
| #include <ftxui/dom/canvas.hpp> | ||||
| #include <ftxui/screen/screen.hpp> | ||||
| #include <ftxui/dom/canvas.hpp> | ||||
| #include <ftxui/screen/screen.hpp> | ||||
| #include <ftxui/dom/canvas.hpp> | ||||
| #include <SFML/Graphics/Color.hpp> | ||||
| #include <locale> | ||||
| #include <codecvt> | ||||
| #include "color.hpp" | ||||
| #include "point.hpp" | ||||
| 
 | ||||
| const int UI_PANEL_BORDER_PX=5; | ||||
| 
 | ||||
| using ftxui::Renderer, ftxui::Component, ftxui::Element, ftxui::Screen; | ||||
| 
 | ||||
| class Panel { | ||||
| public: | ||||
|   int x; | ||||
|   int y; | ||||
|   int width; | ||||
|   int height; | ||||
|   bool has_border = false; | ||||
|   bool must_clear = true; | ||||
|   bool grid = false; | ||||
|   sf::Color default_bg = ColorValue::BLACK; | ||||
|   sf::Color default_fg = ColorValue::LIGHT_LIGHT; | ||||
|   sf::Color border_color = ColorValue::MID; | ||||
|   int border_px = UI_PANEL_BORDER_PX; | ||||
| 
 | ||||
|   bool $dirty = true; | ||||
|   Component $component = nullptr; | ||||
|   Screen $screen; | ||||
|   std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> $converter; | ||||
|   std::wstring $screenout; | ||||
| 
 | ||||
|   Panel(int x, int y, int width, int height, bool is_grid=false) : | ||||
|     x(x), | ||||
|     y(y), | ||||
|     width(width), | ||||
|     height(height), | ||||
|     grid(is_grid), | ||||
|     $screen(Screen(width, height)) | ||||
|   { | ||||
|     must_clear = !is_grid; | ||||
|   }; | ||||
| 
 | ||||
|   void resize(int width, int height); | ||||
|   void set_renderer(Component renderer); | ||||
|   void add(Component child); | ||||
|   void render(); | ||||
|   void mouse_click(ftxui::Mouse::Button btn, Point pos); | ||||
|   void mouse_release(ftxui::Mouse::Button btn, Point pos); | ||||
|   void key_press(ftxui::Event event); | ||||
|   const std::wstring &to_string(); | ||||
|   const Screen &screen(); | ||||
| }; | ||||
							
								
								
									
										275
									
								
								render.cpp
									
										
									
									
									
								
							
							
						
						
									
										275
									
								
								render.cpp
									
										
									
									
									
								
							|  | @ -1,275 +0,0 @@ | |||
| #include "render.hpp" | ||||
| #include "ansi_parser.hpp" | ||||
| #include <cmath> | ||||
| #include <fmt/core.h> | ||||
| #include <array> | ||||
| #include "map.hpp" | ||||
| #include <iostream> | ||||
| #include "color.hpp" | ||||
| 
 | ||||
| #if defined(_WIN64) || defined(_WIN32) | ||||
|  #include <stdio.h> | ||||
|  #include <fcntl.h> | ||||
|  #include <io.h> | ||||
| #endif | ||||
| 
 | ||||
| using namespace fmt; | ||||
| 
 | ||||
| SFMLRender::SFMLRender(sf::RenderWindow &window) : | ||||
|   $window(window), | ||||
|   $map_font_size(0), | ||||
|   $line_spacing(0), | ||||
|   $default_fg(ColorValue::LIGHT_MID), | ||||
|   $default_bg(ColorValue::BLACK), | ||||
|   $bg_sprite($font_texture), | ||||
|   $font(FONT_FILE_NAME), | ||||
|   $ui_text($font), | ||||
|   $ansi($default_fg, $default_bg) | ||||
| { | ||||
|   // force true color, but maybe I want to support different color sets
 | ||||
|   $font.setSmooth(false); | ||||
|   $ui_text.setPosition({0,0}); | ||||
|   $ui_text.setCharacterSize($config.ui_font_size); | ||||
|   $ui_text.setFillColor(ColorValue::LIGHT_MID); | ||||
|   sf::Glyph glyph = $font.getGlyph($config.ui_base_char, $config.ui_font_size, false); | ||||
|   $text_bounds = glyph.bounds; | ||||
|   $cells_w = std::ceil($config.video_x / $text_bounds.size.x); | ||||
|   $cells_h = std::ceil($config.video_y / $text_bounds.size.y); | ||||
| } | ||||
| 
 | ||||
| sf::Sprite &SFMLRender::get_text_sprite(wchar_t tile) { | ||||
|   if(!$sprites.contains(tile)) { | ||||
|     sf::Glyph glyph = $font.getGlyph(tile, $map_font_size, false); | ||||
|     // WARNING! we actually have to do this here because SFML caches
 | ||||
|     // the glyphs on the font texture, so this gets loaded each time
 | ||||
|     // we get a new glyph from the font.
 | ||||
|     $font_texture = $font.getTexture($map_font_size); | ||||
|     $sprites.try_emplace(tile, $font_texture, glyph.textureRect); | ||||
|   } | ||||
| 
 | ||||
|   return $sprites.at(tile); | ||||
| } | ||||
| 
 | ||||
| void SFMLRender::clear_cache() { | ||||
|   $sprites.clear(); | ||||
|   bool good = $font.openFromFile(FONT_FILE_NAME); | ||||
|   dbc::check(good, "Failed to load the font."); | ||||
|   $font.setSmooth(false); | ||||
|   $ui_text.setFont($font); | ||||
| } | ||||
| 
 | ||||
| void SFMLRender::center_panel(Panel &panel) { | ||||
|   int cell_center_x = ($cells_w - panel.width) / 2; | ||||
|   int cell_center_y = ($cells_h - panel.height) / 2; | ||||
| 
 | ||||
|   panel.x = cell_center_x * $text_bounds.size.x; | ||||
|   panel.y = cell_center_y * $text_bounds.size.y; | ||||
| } | ||||
| 
 | ||||
| void SFMLRender::resize_grid(int new_size, Panel &panel_out) { | ||||
|   auto glyph = $font.getGlyph($config.bg_tile, new_size, false); | ||||
|   int view_x = std::ceil(($config.video_x - panel_out.x) / glyph.bounds.size.x); | ||||
|   int view_y = std::ceil(($config.video_y - panel_out.y) / glyph.bounds.size.y); | ||||
| 
 | ||||
|   // looks good, set 'em all
 | ||||
|   $base_glyph = glyph; | ||||
|   $map_font_size = new_size; | ||||
|   $sprites.clear(); // need to reset the sprites for the new size
 | ||||
|   $line_spacing = $font.getLineSpacing($map_font_size); | ||||
|   $bg_sprite = get_text_sprite($config.bg_tile); | ||||
|   $grid_bounds = $bg_sprite.getLocalBounds(); | ||||
|   panel_out.resize(view_x, view_y); | ||||
| } | ||||
| 
 | ||||
| inline void configure_tile(const sf::Sprite &sprite, sf::FloatRect &sp_bounds, sf::FloatRect grid_bounds, float &width_delta, float &height_delta) { | ||||
|   // BUG: I think I could create a struct that kept this info for all sprites loaded
 | ||||
|   // should look into caching all this instead of calcing it each time
 | ||||
|   sp_bounds = sprite.getLocalBounds(); | ||||
| 
 | ||||
|   // calculate where to center the sprite, but only if it's smaller
 | ||||
|   width_delta = grid_bounds.size.x > sp_bounds.size.x ? (grid_bounds.size.x - sp_bounds.size.x) / 2 : 0; | ||||
|   height_delta = grid_bounds.size.y > sp_bounds.size.x ? (grid_bounds.size.y - sp_bounds.size.y) / 2 : 0; | ||||
| } | ||||
| 
 | ||||
| void SFMLRender::render_grid(const std::wstring &text, sf::Color default_fg, sf::Color default_bg, float x, float y) { | ||||
|   wchar_t last_tile = $config.bg_tile; | ||||
|   sf::FloatRect sp_bounds; | ||||
|   float width_delta = 0; | ||||
|   float height_delta = 0; | ||||
|   sf::Sprite &sprite = get_text_sprite(last_tile); | ||||
|   const float start_x = x; | ||||
|   sf::Color cur_fg = default_fg; | ||||
|   sf::Color cur_bg = default_bg; | ||||
| 
 | ||||
|   $ansi.parse(text, [&](auto fg, auto bg) { | ||||
|       cur_fg = fg; | ||||
|       cur_bg = bg; | ||||
|     }, | ||||
| 
 | ||||
|     [&](wchar_t tile) { | ||||
|       switch(tile) { | ||||
|         case '\r': break; // ignore it
 | ||||
|         case '\n': { | ||||
|             // don't bother processing newlines, just skip
 | ||||
|             y += $line_spacing; | ||||
|             x = start_x; | ||||
|           } | ||||
|           break; | ||||
|         default: { | ||||
|           // only get a new sprite if the tile changed
 | ||||
|           if(last_tile != tile) { | ||||
|             sprite = get_text_sprite(tile); | ||||
|             configure_tile(sprite, sp_bounds, $grid_bounds, width_delta, height_delta); | ||||
|             last_tile = tile; // update last tile seen
 | ||||
|           } | ||||
| 
 | ||||
|           sprite.setPosition({x+width_delta, y+height_delta}); | ||||
|           sprite.setColor(cur_fg); | ||||
| 
 | ||||
|           // only draw background char if it's different from default
 | ||||
|           if(cur_bg != default_bg) { | ||||
|             $bg_sprite.setPosition({x, y}); | ||||
|             $bg_sprite.setColor(cur_bg); | ||||
|             $window.draw($bg_sprite); | ||||
|           } | ||||
| 
 | ||||
|           $window.draw(sprite); | ||||
|           // next cell
 | ||||
|           x += $base_glyph.advance; | ||||
|         } | ||||
|       } | ||||
|   }); | ||||
| } | ||||
| 
 | ||||
| inline sf::FloatRect draw_chunk(sf::RenderWindow& window, | ||||
|     sf::FloatRect text_bounds, sf::Text& text, sf::Color default_bg, | ||||
|     sf::Color bgcolor, int bg_box_offset, float x, float y, std::wstring &out) | ||||
| { | ||||
|   text.setString(out); | ||||
|   text.setPosition({x, y}); | ||||
|   // get a base character for the cell size
 | ||||
|   sf::FloatRect bounds({x, y}, {text_bounds.size.x * out.size(), text_bounds.size.y}); | ||||
| 
 | ||||
|   if(default_bg != bgcolor) { | ||||
|     sf::RectangleShape backing(bounds.size); | ||||
|     backing.setFillColor(bgcolor); | ||||
|     backing.setPosition({bounds.position.x, bounds.position.y + bg_box_offset}); | ||||
|     window.draw(backing); | ||||
|   } | ||||
| 
 | ||||
|   window.draw(text); | ||||
|   out.clear(); | ||||
|   return bounds; | ||||
| } | ||||
| 
 | ||||
| void SFMLRender::render_text(const std::wstring &text, sf::Color default_fg, sf::Color default_bg, float start_x, float start_y) { | ||||
|   std::wstring out; | ||||
|   float x = start_x; | ||||
|   float y = start_y; | ||||
|   sf::Color cur_bg = default_bg; | ||||
| 
 | ||||
|   // start with the default_fg until it's changed
 | ||||
|   $ui_text.setFillColor(default_fg); | ||||
| 
 | ||||
|   $ansi.parse(text, | ||||
|     [&](auto fg, auto bg) { | ||||
|         if(out.size() > 0 ) { | ||||
|           auto bounds = draw_chunk($window, | ||||
|               $text_bounds, $ui_text, | ||||
|               default_bg, cur_bg, $config.bg_box_offset, x, y, out); | ||||
|           x += bounds.size.x; | ||||
|         } | ||||
|         cur_bg = bg; | ||||
|         $ui_text.setFillColor(fg); | ||||
|     }, | ||||
|     [&](wchar_t tile) { | ||||
|       switch(tile) { | ||||
|         case '\r': break; // ignore it
 | ||||
|         case '\n': { | ||||
|           sf::FloatRect bounds; | ||||
| 
 | ||||
|           if(out.size() > 0) { | ||||
|             bounds = draw_chunk($window, $text_bounds, | ||||
|                 $ui_text, default_bg, cur_bg, $config.bg_box_offset, x, y, out); | ||||
|           } else { | ||||
|             bounds = $ui_text.getLocalBounds(); | ||||
|           } | ||||
| 
 | ||||
|           y += bounds.size.y; | ||||
|           x = start_x; // reset to the original position
 | ||||
|         } | ||||
|         break; | ||||
|         default: | ||||
|           out += tile; | ||||
|           break; | ||||
|       } | ||||
|     } | ||||
|   ); | ||||
| 
 | ||||
|   if(out.size() > 0) { | ||||
|     draw_chunk($window, $text_bounds, $ui_text, default_bg, cur_bg, $config.bg_box_offset, x, y, out); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void SFMLRender::draw_sprite(sf::Sprite &sprite, sf::Shader *shader) { | ||||
|   $window.draw(sprite, shader); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Does not render the panel, you have to do that so you can control | ||||
|  * when things render. | ||||
|  */ | ||||
| void SFMLRender::draw(Panel &panel, float x_offset, float y_offset) { | ||||
|   const std::wstring &panelout = panel.to_string(); | ||||
| 
 | ||||
|   auto bounds = panel.grid ? $grid_bounds : $text_bounds; | ||||
| 
 | ||||
|   sf::RectangleShape backing( | ||||
|       sf::Vector2f(bounds.size.x * panel.width + panel.border_px, | ||||
|         bounds.size.y * panel.height + panel.border_px)); | ||||
| 
 | ||||
|   backing.setFillColor(panel.default_bg); | ||||
| 
 | ||||
|   if(panel.has_border) { | ||||
|     backing.setOutlineColor(panel.border_color); | ||||
|     backing.setOutlineThickness(panel.border_px); | ||||
|   } | ||||
| 
 | ||||
|   backing.setPosition({panel.x + x_offset, panel.y + y_offset}); | ||||
|   $window.draw(backing); | ||||
| 
 | ||||
|   if(panel.grid) { | ||||
|     render_grid(panelout, panel.default_fg, panel.default_bg, panel.x + x_offset, panel.y + y_offset); | ||||
|   } else { | ||||
|     render_text(panelout, panel.default_fg, panel.default_bg, panel.x + x_offset, panel.y + y_offset); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| bool SFMLRender::mouse_position(Panel &panel, Point &out) { | ||||
|   // yes, you have to do this in sfml
 | ||||
|   sf::Vector2f pos = $window.mapPixelToCoords(sf::Mouse::getPosition($window)); | ||||
| 
 | ||||
|   auto bounds = panel.grid ? $grid_bounds : $text_bounds; | ||||
| 
 | ||||
|   if(pos.x >= panel.x && pos.y >= panel.y | ||||
|       && pos.x <= (panel.x + panel.width * bounds.size.x) | ||||
|       && pos.y <= (panel.y + panel.height * bounds.size.y)) | ||||
|   { | ||||
|       out = { | ||||
|         size_t((pos.x - panel.x) / bounds.size.x), | ||||
|         size_t((pos.y - panel.y) / bounds.size.y) | ||||
|       }; | ||||
| 
 | ||||
|       return true; | ||||
|   } | ||||
| 
 | ||||
|   return false; | ||||
| } | ||||
| 
 | ||||
| void SFMLRender::init_terminal() { | ||||
| #if defined(_WIN64) || defined(_WIN32) | ||||
|   _setmode(_fileno(stdout), _O_U16TEXT); | ||||
| #endif | ||||
| 
 | ||||
|   ftxui::Terminal::SetColorSupport(ftxui::Terminal::Color::TrueColor); | ||||
| } | ||||
							
								
								
									
										80
									
								
								render.hpp
									
										
									
									
									
								
							
							
						
						
									
										80
									
								
								render.hpp
									
										
									
									
									
								
							|  | @ -1,80 +0,0 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include <ftxui/screen/screen.hpp> | ||||
| #include <ftxui/dom/canvas.hpp> | ||||
| #include <SFML/Window.hpp> | ||||
| #include <SFML/System.hpp> | ||||
| #include <SFML/Graphics.hpp> | ||||
| #include <SFML/Graphics/Rect.hpp> | ||||
| #include "point.hpp" | ||||
| #include <codecvt> | ||||
| #include "ansi_parser.hpp" | ||||
| #include "panel.hpp" | ||||
| #include "constants.hpp" | ||||
| #include <optional> | ||||
| 
 | ||||
| using ftxui::Canvas, ftxui::Screen; | ||||
| 
 | ||||
| /*
 | ||||
|  * BUG: This could be so much better. | ||||
|  */ | ||||
| struct RenderConfig { | ||||
|   unsigned int video_x = SCREEN_WIDTH; | ||||
|   unsigned int video_y = SCREEN_HEIGHT; | ||||
|   int ui_font_size=UI_FONT_SIZE; | ||||
|   int base_map_font_size=BASE_MAP_FONT_SIZE; | ||||
|   wchar_t bg_tile = BG_TILE; | ||||
|   wchar_t ui_base_char = UI_BASE_CHAR; | ||||
|   int bg_box_offset=BG_BOX_OFFSET; | ||||
| }; | ||||
| 
 | ||||
| struct SFMLRender { | ||||
|   int $cells_w = 0; | ||||
|   int $cells_h = 0; | ||||
|   RenderConfig $config; | ||||
|   sf::RenderWindow& $window; | ||||
|   int $map_font_size; | ||||
|   float $line_spacing; | ||||
|   sf::Color $default_fg; | ||||
|   sf::Color $default_bg; | ||||
|   sf::Texture $font_texture; | ||||
|   sf::Sprite $bg_sprite; | ||||
|   sf::Font $font; | ||||
|   sf::Text $ui_text; | ||||
|   ANSIParser $ansi; | ||||
| 
 | ||||
|   std::unordered_map<wchar_t, sf::Sprite> $sprites; | ||||
|   sf::Glyph $base_glyph; | ||||
|   sf::FloatRect $grid_bounds; | ||||
|   std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> $converter; | ||||
|   sf::FloatRect $text_bounds; | ||||
| 
 | ||||
|   SFMLRender(sf::RenderWindow& window); | ||||
| 
 | ||||
|   // disable copy
 | ||||
|   SFMLRender(SFMLRender &other) = delete; | ||||
| 
 | ||||
|   sf::Sprite &get_text_sprite(wchar_t tile); | ||||
|   void resize_grid(int new_size, Panel &panel_out); | ||||
|   void render_grid(const std::wstring &text, sf::Color default_fg, sf::Color default_bg, float x, float y); | ||||
|   void render_text(const std::wstring &text, sf::Color default_fg, sf::Color default_bg, float x, float y); | ||||
| 
 | ||||
|   void draw(Panel &panel, float x_offset=0.0f, float y_offset=0.0f); | ||||
|   void draw_sprite(sf::Sprite &sprite, sf::Shader *shader); | ||||
|   void center_panel(Panel &panel); | ||||
| 
 | ||||
|   std::optional<sf::Event> poll_event() { | ||||
|     return $window.pollEvent(); | ||||
|   } | ||||
| 
 | ||||
|   void close() { return $window.close(); } | ||||
| 
 | ||||
|   bool is_open() { return $window.isOpen(); } | ||||
| 
 | ||||
|   int font_size() { return $map_font_size; } | ||||
|   void clear() { $window.clear(); } | ||||
|   void display() { $window.display(); } | ||||
|   bool mouse_position(Panel &panel, Point &out); | ||||
|   void clear_cache(); | ||||
|   static void init_terminal(); | ||||
| }; | ||||
							
								
								
									
										21
									
								
								save.cpp
									
										
									
									
									
								
							
							
						
						
									
										21
									
								
								save.cpp
									
										
									
									
									
								
							|  | @ -8,27 +8,6 @@ | |||
| using namespace components; | ||||
| using namespace fmt; | ||||
| 
 | ||||
| template<typename CompT> | ||||
| inline void extract(DinkyECS::World &world, std::map<DinkyECS::Entity, CompT> &into) { | ||||
|   auto from_world = world.entity_map_for<CompT>(); | ||||
|   for(auto [entity, value] : from_world) { | ||||
|     into[entity] = std::any_cast<CompT>(value); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void save::to_file(fs::path path, DinkyECS::World &world, Map &map) { | ||||
|   (void)path; | ||||
|   (void)world; | ||||
|   (void)map; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void save::from_file(fs::path path, DinkyECS::World &world_out, Map &map_out) { | ||||
|   (void)path; | ||||
|   (void)world_out; | ||||
|   (void)map_out; | ||||
| } | ||||
| 
 | ||||
| void save::load_configs(DinkyECS::World &world) { | ||||
|   Config game("./assets/config.json"); | ||||
|   Config enemies("./assets/enemies.json"); | ||||
|  |  | |||
							
								
								
									
										24
									
								
								save.hpp
									
										
									
									
									
								
							
							
						
						
									
										24
									
								
								save.hpp
									
										
									
									
									
								
							|  | @ -10,29 +10,5 @@ | |||
| namespace save { | ||||
|   namespace fs = std::filesystem; | ||||
| 
 | ||||
|   struct MapData { | ||||
|     size_t width; | ||||
|     size_t height; | ||||
|     std::vector<Room> rooms; | ||||
|     Matrix walls; | ||||
|   }; | ||||
| 
 | ||||
|   struct Facts { | ||||
|     components::Player player; | ||||
|   }; | ||||
| 
 | ||||
|   struct SaveData { | ||||
|     Facts facts; | ||||
|     MapData map; | ||||
| 
 | ||||
|     std::map<DinkyECS::Entity, components::Position> position; | ||||
|     std::map<DinkyECS::Entity, components::Motion> motion; | ||||
|     std::map<DinkyECS::Entity, components::Combat> combat; | ||||
|     std::map<DinkyECS::Entity, components::Tile> tile; | ||||
|     // std::map<DinkyECS::Entity, components::Inventory> inventory;
 | ||||
|   }; | ||||
| 
 | ||||
|   void to_file(fs::path path, DinkyECS::World &world, Map &map); | ||||
|   void from_file(fs::path path, DinkyECS::World &world_out, Map &map); | ||||
|   void load_configs(DinkyECS::World &world); | ||||
| } | ||||
|  |  | |||
|  | @ -4,7 +4,10 @@ cp *.cpp,*.hpp,*.rl builddir | |||
| . .venv/Scripts/activate | ||||
| 
 | ||||
| rm -recurse -force coverage | ||||
| cp scripts\gcovr_patched_coverage.py .venv\Lib\site-packages\gcovr\coverage.py | ||||
| 
 | ||||
| gcovr -o coverage/ --html --html-details --gcov-ignore-errors all -e builddir/subprojects -e subprojects . | ||||
| gcovr -o coverage/ --html --html-details  --html-theme github.dark-blue --gcov-ignore-errors all --gcov-ignore-parse-errors negative_hits.warn_once_per_file -e builddir/subprojects -e builddir -e subprojects -j 10 . | ||||
| 
 | ||||
| rm *.gcov.json.gz | ||||
| 
 | ||||
| start .\coverage\coverage_details.html | ||||
|  |  | |||
							
								
								
									
										16
									
								
								systems.cpp
									
										
									
									
									
								
							
							
						
						
									
										16
									
								
								systems.cpp
									
										
									
									
									
								
							|  | @ -16,7 +16,6 @@ using std::string; | |||
| using namespace fmt; | ||||
| using namespace components; | ||||
| using lighting::LightSource; | ||||
| using ftxui::Color; | ||||
| 
 | ||||
| void System::lighting(GameLevel &level) { | ||||
|   auto &light = *level.lights; | ||||
|  | @ -331,11 +330,9 @@ void System::plan_motion(DinkyECS::World& world, Point move_to) { | |||
| 
 | ||||
| /*
 | ||||
|  * This one is called inside the MapViewUI very often so | ||||
|  * just avoide GameMap unlike the others. | ||||
|  * | ||||
|  * BUG: Just get rid of this and use the new UI to do the map | ||||
|  * just avoid GameMap unlike the others. | ||||
|  */ | ||||
| void System::draw_entities(DinkyECS::World &world, Map &map, const Matrix &lights, ftxui::Canvas &canvas, const Point &cam_orig, size_t view_x, size_t view_y) { | ||||
| void System::draw_entities(DinkyECS::World &world, Map &map, const Matrix &lights, const Point &cam_orig, size_t view_x, size_t view_y) { | ||||
|   auto &tiles = map.tiles(); | ||||
| 
 | ||||
|   world.query<Position, Tile>([&](auto, auto &pos, auto &tile) { | ||||
|  | @ -346,11 +343,10 @@ void System::draw_entities(DinkyECS::World &world, Map &map, const Matrix &light | |||
|       float light_value = lights[pos.location.y][pos.location.x] * PERCENT; | ||||
|       const Tile& cell = tiles.at(pos.location.x, pos.location.y); | ||||
| 
 | ||||
|       // the 2 and 4 are from ftxui::Canvas since it does a kind of "subpixel" drawing
 | ||||
|       canvas.DrawText(loc.x*2, loc.y*4, tile.display, [tile, light_value, cell](auto &pixel) { | ||||
|           pixel.foreground_color = Color::HSV(tile.foreground[0], tile.foreground[1], tile.foreground[2] * light_value); | ||||
|           pixel.background_color = Color::HSV(cell.background[0], cell.background[1], cell.background[2] * light_value); | ||||
|       }); | ||||
|       (void)loc; // not used yet, this after ripping out map so needs rewrite
 | ||||
|       (void)light_value; | ||||
|       (void)cell; | ||||
|       (void)tile; | ||||
|     } | ||||
|   }); | ||||
| } | ||||
|  |  | |||
|  | @ -1,7 +1,6 @@ | |||
| #pragma once | ||||
| #include "components.hpp" | ||||
| #include "levelmanager.hpp" | ||||
| #include <ftxui/dom/canvas.hpp> | ||||
| 
 | ||||
| 
 | ||||
| namespace System { | ||||
|  | @ -18,7 +17,7 @@ namespace System { | |||
|   void init_positions(DinkyECS::World &world, SpatialMap &collider); | ||||
|   void device(DinkyECS::World &world, DinkyECS::Entity actor, DinkyECS::Entity item); | ||||
|   void plan_motion(DinkyECS::World& world, Point move_to); | ||||
|   void draw_entities(DinkyECS::World &world, Map &map, const Matrix &lights, ftxui::Canvas &canvas, const Point &cam_orig, size_t view_x, size_t view_y); | ||||
|   void draw_entities(DinkyECS::World &world, Map &map, const Matrix &lights, const Point &cam_orig, size_t view_x, size_t view_y); | ||||
| 
 | ||||
|   void enemy_ai(GameLevel &level); | ||||
|   void combat(GameLevel &level); | ||||
|  |  | |||
|  | @ -3,6 +3,7 @@ | |||
| #include "dinkyecs.hpp" | ||||
| #include "config.hpp" | ||||
| #include <iostream> | ||||
| #include "easings.hpp" | ||||
| 
 | ||||
| using namespace components; | ||||
| using namespace DinkyECS; | ||||
|  | @ -27,3 +28,66 @@ TEST_CASE("confirm component loading works", "[components]") { | |||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| TEST_CASE("make sure json_mods works", "[components]") { | ||||
|   Config config("assets/bosses.json"); | ||||
|   // this confirms that loading something with an optional
 | ||||
|   // field works with the json conversions in json_mods.hpp
 | ||||
|   for(auto& comp_data : config["RAT_KING"]["components"]) { | ||||
|     if(comp_data["_type"] == "BossFight") { | ||||
|       auto comp = components::convert<components::BossFight>(comp_data); | ||||
|       // the boss fight for the rat king doesn't have a stage so false=optional
 | ||||
|       REQUIRE(comp.stage == std::nullopt); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   // this then confirms everything else about the json conversion
 | ||||
| 
 | ||||
|   ComponentMap comp_map; | ||||
|   components::configure(comp_map); | ||||
| 
 | ||||
|   DinkyECS::World world; | ||||
|   auto rat_king = world.entity(); | ||||
| 
 | ||||
|   components::configure_entity(comp_map, world, rat_king, config["RAT_KING"]["components"]); | ||||
| 
 | ||||
|   auto boss = world.get<BossFight>(rat_king); | ||||
|   REQUIRE(boss.stage == std::nullopt); | ||||
| 
 | ||||
|   // now load the other one for the other way optional is used
 | ||||
|   auto devils_fingers = world.entity(); | ||||
|   components::configure_entity(comp_map, world, devils_fingers, config["DEVILS_FINGERS"]["components"]); | ||||
|   auto boss2 = world.get<BossFight>(devils_fingers); | ||||
|   REQUIRE(boss2.stage != std::nullopt); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| TEST_CASE("animation component special cases", "[components]") { | ||||
|   Animation anim; | ||||
| 
 | ||||
|   anim.easing = ease::NONE; | ||||
|   float res = anim.twitching(); | ||||
|   REQUIRE(res == 0.0); | ||||
| 
 | ||||
|   anim.easing = ease::SINE; | ||||
|   anim.subframe = 1.0f; | ||||
|   res = anim.twitching(); | ||||
|   REQUIRE(!std::isnan(res)); | ||||
| 
 | ||||
|   anim.easing = ease::OUT_CIRC; | ||||
|   res = anim.twitching(); | ||||
|   REQUIRE(!std::isnan(res)); | ||||
| 
 | ||||
|   anim.easing = ease::OUT_BOUNCE; | ||||
|   res = anim.twitching(); | ||||
|   REQUIRE(!std::isnan(res)); | ||||
| 
 | ||||
|   anim.easing = ease::IN_OUT_BACK; | ||||
|   res = anim.twitching(); | ||||
|   REQUIRE(!std::isnan(res)); | ||||
| 
 | ||||
|   anim.easing = ease::FUCKFACE; | ||||
|   bool throws = false; | ||||
|   try { anim.twitching(); } catch(...) { throws = true; } | ||||
|   REQUIRE(throws); | ||||
| } | ||||
|  |  | |||
|  | @ -5,7 +5,9 @@ | |||
| TEST_CASE("confirm basic config loader ops", "[config]") { | ||||
|   Config config("assets/devices.json"); | ||||
|   auto data_list = config.json(); | ||||
|   auto the_keys = config.keys(); | ||||
| 
 | ||||
|   REQUIRE(the_keys.size() > 0); | ||||
| 
 | ||||
|   for(auto& [key, data] : data_list.items()) { | ||||
|     auto wide1 = config.wstring(key, "name"); | ||||
|  |  | |||
							
								
								
									
										17
									
								
								tests/easings.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								tests/easings.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,17 @@ | |||
| #include <catch2/catch_test_macros.hpp> | ||||
| #include "easings.hpp" | ||||
| #include <cmath> | ||||
| 
 | ||||
| TEST_CASE("make sure the easing functions at least run", "[easings]") { | ||||
|   double out = ease::sine(1.3); | ||||
|   REQUIRE(out <= 1.0); | ||||
| 
 | ||||
|   out = ease::out_circ(3.444); | ||||
|   REQUIRE(std::isnan(out)); | ||||
| 
 | ||||
|   out = ease::out_bounce(1.13); | ||||
|   REQUIRE(out <= 10 ); | ||||
| 
 | ||||
|   out = ease::in_out_back(3.4); | ||||
|   REQUIRE(out < 250.0); | ||||
| } | ||||
|  | @ -1,15 +0,0 @@ | |||
| [wrap-file] | ||||
| directory = FTXUI-5.0.0 | ||||
| source_url = https://github.com/ArthurSonzogni/FTXUI/archive/refs/tags/v5.0.0.tar.gz | ||||
| source_filename = FTXUI-5.0.0.tar.gz | ||||
| source_hash = a2991cb222c944aee14397965d9f6b050245da849d8c5da7c72d112de2786b5b | ||||
| patch_filename = ftxui_5.0.0-1_patch.zip | ||||
| patch_url = https://wrapdb.mesonbuild.com/v2/ftxui_5.0.0-1/get_patch | ||||
| patch_hash = 21c654e82739b90b95bd98c1d321264608d37c50d29fbcc3487f790fd5412909 | ||||
| source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/ftxui_5.0.0-1/FTXUI-5.0.0.tar.gz | ||||
| wrapdb_version = 5.0.0-1 | ||||
| 
 | ||||
| [provide] | ||||
| ftxui-screen = screen_dep | ||||
| ftxui-dom = dom_dep | ||||
| ftxui-component = component_dep | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Zed A. Shaw
						Zed A. Shaw