render_maudit.h 34 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282
  1. #ifndef __RENDER_H
  2. #define __RENDER_H
  3. #include <list>
  4. #include <stdexcept>
  5. #include "libmaudit/maudit.h"
  6. #include "fov.h"
  7. #include "bresenham.h"
  8. namespace serialize {
  9. template <>
  10. struct reader<maudit::color> {
  11. void read(Source& s, maudit::color& v) {
  12. reader<uint32_t>().read(s, (uint32_t&)v);
  13. }
  14. };
  15. template <>
  16. struct writer<maudit::color> {
  17. void write(Sink& s, const maudit::color& v) {
  18. writer<uint32_t>().write(s, (uint32_t)v);
  19. }
  20. };
  21. template <>
  22. struct reader<maudit::glyph> {
  23. void read(Source& s, maudit::glyph& v) {
  24. serialize::read(s, v.text);
  25. serialize::read(s, v.fore);
  26. serialize::read(s, v.back);
  27. }
  28. };
  29. template <>
  30. struct writer<maudit::glyph> {
  31. void write(Sink& s, const maudit::glyph& v) {
  32. serialize::write(s, v.text);
  33. serialize::write(s, v.fore);
  34. serialize::write(s, v.back);
  35. }
  36. };
  37. template <>
  38. struct reader<maudit::keycode> {
  39. void read(Source& s, maudit::keycode& v) {
  40. reader<uint32_t>().read(s, (uint32_t&)v);
  41. }
  42. };
  43. template <>
  44. struct writer<maudit::keycode> {
  45. void write(Sink& s, const maudit::keycode& v) {
  46. writer<uint32_t>().write(s, (uint32_t)v);
  47. }
  48. };
  49. template <>
  50. struct reader<maudit::keypress> {
  51. void read(Source& s, maudit::keypress& v) {
  52. serialize::read(s, v.letter);
  53. serialize::read(s, v.key);
  54. }
  55. };
  56. template <>
  57. struct writer<maudit::keypress> {
  58. void write(Sink& s, const maudit::keypress& v) {
  59. serialize::write(s, v.letter);
  60. serialize::write(s, v.key);
  61. }
  62. };
  63. }
  64. namespace grender {
  65. typedef std::pair<unsigned int, unsigned int> pt;
  66. typedef maudit::color color_t;
  67. struct Grid {
  68. static const size_t skincount = 8;
  69. static const color_t black_color = color_t::bright_black;
  70. static const color_t blue_color = color_t::dim_blue;
  71. static const color_t yellow_color = color_t::bright_yellow;
  72. static const color_t gray_color = color_t::dim_white;
  73. static const color_t white_color = color_t::bright_white;
  74. static const color_t no_color = color_t::none;
  75. typedef maudit::glyph skin;
  76. typedef maudit::keypress keypress;
  77. struct gridpoint {
  78. std::vector<skin> skins;
  79. uint8_t is_lit;
  80. uint8_t is_lightsource;
  81. uint8_t in_fov;
  82. uint8_t is_viewblock;
  83. uint8_t is_walkblock;
  84. bool valid;
  85. gridpoint() : is_lit(0), is_lightsource(0), in_fov(0), is_viewblock(0), is_walkblock(0), valid(false)
  86. {
  87. skins.resize(skincount);
  88. }
  89. };
  90. struct message {
  91. std::string text;
  92. bool important;
  93. bool temporary;
  94. unsigned int timestamp;
  95. message(const std::string& t = "", bool i = false, unsigned int ts = 0, bool temp = false) :
  96. text(t), important(i), temporary(temp), timestamp(ts) {}
  97. };
  98. struct hud_line {
  99. enum numtype_t { SIGNED, UNSIGNED };
  100. numtype_t numtype;
  101. int npips;
  102. std::string label;
  103. skin lskin;
  104. skin pskin;
  105. skin nskin;
  106. hud_line(numtype_t nt, int np, const std::string& l,
  107. const skin& ls, const skin& ps, const skin& ns) :
  108. numtype(nt), npips(np), label(l), lskin(ls), pskin(ps), nskin(ns) {}
  109. };
  110. unsigned int w;
  111. unsigned int h;
  112. std::vector<gridpoint> grid;
  113. std::list<message> messages;
  114. // transient, not saved in dump.
  115. uint32_t current_draw_n;
  116. std::vector< std::pair<uint32_t, skin> > overlay;
  117. struct textlabel_t {
  118. std::string label;
  119. unsigned int x;
  120. unsigned int y;
  121. color_t fg;
  122. color_t bg;
  123. textlabel_t(const std::string& l = "", unsigned int _x = 0, unsigned int _y = 0,
  124. color_t _fg = white_color, color_t _bg = black_color) :
  125. label(l), x(_x), y(_y), fg(_fg), bg(_bg) {}
  126. };
  127. std::map< uint32_t, std::vector<textlabel_t> > textlabels;
  128. // transient, not saved in dump.
  129. std::vector<hud_line> hud_pips;
  130. std::vector<ui_symbol_t> ui_symbol_themes;
  131. ui_symbol_t ui_symbol;
  132. size_t ui_symbol_index;
  133. private:
  134. color_t color_fade(color_t c, double v, uint8_t mask, bool true_fade) {
  135. if (v > 10 && (mask & 0xF0)) {
  136. v -= 10;
  137. }
  138. if (!true_fade) {
  139. if (v > 90) {
  140. return color_t::dim_black;
  141. } else {
  142. return c;
  143. }
  144. }
  145. if (v <= 30) {
  146. return c;
  147. } else if (v <= 70) {
  148. switch (c) {
  149. case color_t::bright_black: return color_t::bright_black;
  150. case color_t::bright_red: return color_t::dim_red;
  151. case color_t::bright_green: return color_t::dim_green;
  152. case color_t::bright_yellow: return color_t::dim_yellow;
  153. case color_t::bright_blue: return color_t::dim_blue;
  154. case color_t::bright_magenta: return color_t::dim_magenta;
  155. case color_t::bright_cyan: return color_t::dim_cyan;
  156. case color_t::bright_white: return color_t::dim_white;
  157. default: return c;
  158. }
  159. } else if (v <= 90) {
  160. return color_t::bright_black;
  161. } else {
  162. return color_t::dim_black;
  163. }
  164. }
  165. double _dist(const pt& a, const pt& b) {
  166. unsigned int dx = abs((int)a.first - (int)b.first);
  167. unsigned int dy = abs((int)a.second - (int)b.second);
  168. return sqrt(dx*dx + dy*dy);
  169. }
  170. bool _translate_v2g(int voff_x, int voff_y, const pt& vxy, pt& gxy) {
  171. int rx = (int)vxy.first + voff_x;
  172. int ry = (int)vxy.second + voff_y;
  173. if (rx < 0 || (unsigned int)rx >= w || ry < 0 || (unsigned int)ry >= h)
  174. return false;
  175. gxy.first = (unsigned int)rx;
  176. gxy.second = (unsigned int)ry;
  177. return true;
  178. }
  179. bool _translate_g2v(int voff_x, int voff_y, unsigned int view_w, unsigned int view_h, const pt& gxy, pt& vxy) {
  180. int rx = (int)gxy.first - voff_x;
  181. int ry = (int)gxy.second - voff_y;
  182. if (rx < 0 || (unsigned int)rx >= view_w || ry < 0 || (unsigned int)ry >= view_h)
  183. return false;
  184. vxy.first = (unsigned int)rx;
  185. vxy.second = (unsigned int)ry;
  186. return true;
  187. }
  188. public:
  189. skin& _overlay_set(const pt& xy) {
  190. if (xy.first >= w || xy.second >= h) {
  191. static skin tmp;
  192. return tmp;
  193. }
  194. auto& tmp = overlay[xy.second*w+xy.first];
  195. tmp.first = current_draw_n;
  196. return tmp.second;
  197. }
  198. private:
  199. const std::pair<uint32_t, skin>& _overlay_get(const pt& xy) {
  200. return overlay[xy.second*w+xy.first];
  201. }
  202. template <typename FUNC1, typename FUNC2>
  203. void _draw_circle(unsigned int x, unsigned int y, unsigned int r,
  204. color_t fore, color_t back, FUNC1 f_chk, FUNC2 f_do) {
  205. unsigned int x0 = (x < r ? 0 : x - r);
  206. unsigned int y0 = (y < r ? 0 : y - r);
  207. unsigned int x1 = std::min(x + r + 1, w);
  208. unsigned int y1 = std::min(y + r + 1, h);
  209. std::vector<pt> pts;
  210. for (unsigned int _x = x0; _x < x1; ++_x) {
  211. for (unsigned int _y = y0; _y < y1; ++_y) {
  212. pt _xy(_x, _y);
  213. double d = _dist(pt(x,y), _xy);
  214. if (d <= r && f_chk(_x, _y)) {
  215. pts.push_back(_xy);
  216. }
  217. }
  218. }
  219. for (const auto& xy : pts) {
  220. _overlay_set(xy) = skin(ui_symbol.circle.text, fore, back);
  221. }
  222. for (const auto& xy : pts) {
  223. f_do(xy.first, xy.second);
  224. }
  225. }
  226. void _draw_messages(std::vector<skin>& hud,
  227. unsigned int x, unsigned int y, unsigned int view_w,
  228. unsigned int box_w, unsigned int box_h,
  229. unsigned int t) {
  230. std::vector< std::pair<color_t, std::string> > lines;
  231. unsigned int i = 0;
  232. auto li = messages.begin();
  233. while (i < box_h && li != messages.end()) {
  234. color_t fore = gray_color;
  235. if (li->timestamp == 0 || li->timestamp >= t) {
  236. if (li->important) {
  237. fore = yellow_color;
  238. } else {
  239. fore = white_color;
  240. }
  241. li->timestamp = t;
  242. }
  243. size_t si = 0;
  244. while (si < li->text.size()) {
  245. lines.push_back(std::make_pair(fore, li->text.substr(si, box_w)));
  246. si += box_w;
  247. }
  248. ++i;
  249. ++li;
  250. }
  251. lines.resize(box_h);
  252. for (size_t j = 0; j < lines.size(); ++j) {
  253. auto hudi = hud.begin() + ((y+j) * view_w) + x;
  254. color_t fore = lines[j].first;
  255. const std::string& text = lines[j].second;
  256. for (size_t si = 0; si < box_w; ++si) {
  257. if (si < text.size()) {
  258. hudi->text = text[si];
  259. hudi->fore = fore;
  260. } else {
  261. hudi->fore = black_color;
  262. }
  263. ++hudi;
  264. }
  265. }
  266. }
  267. void _draw_pipline(std::vector<skin>& hud,
  268. unsigned int x, unsigned int y, unsigned int view_w,
  269. const hud_line& line) {
  270. auto hudi = hud.begin() + (y * view_w) + x;
  271. std::string l;
  272. if (line.label.size() < 7)
  273. l += std::string(7 - line.label.size(), ' ');
  274. l += line.label;
  275. l += ": ";
  276. for (unsigned char c : l) {
  277. *hudi = line.lskin;
  278. hudi->set_text(c);
  279. ++hudi;
  280. }
  281. skin tmp(".", color_t::dim_black, color_t::dim_black);
  282. if (line.numtype == hud_line::UNSIGNED) {
  283. for (int i = 0; i < 6; ++i) {
  284. if (i < line.npips) {
  285. *hudi = line.pskin;
  286. } else {
  287. *hudi = tmp;
  288. }
  289. ++hudi;
  290. }
  291. } else {
  292. for (int i = -3; i < 0; ++i) {
  293. if (i >= line.npips) {
  294. *hudi = line.nskin;
  295. } else {
  296. *hudi = tmp;
  297. }
  298. ++hudi;
  299. }
  300. for (int i = 0; i < 3; ++i) {
  301. if (i <= line.npips) {
  302. *hudi = line.pskin;
  303. } else {
  304. *hudi = skin();
  305. }
  306. ++hudi;
  307. }
  308. }
  309. }
  310. void _draw_textlabels(std::vector<skin>& ret_glyphs, int voff_x, int voff_y,
  311. unsigned int view_w, unsigned int view_h, bool fullwidth) {
  312. unsigned int scale = (fullwidth ? 2 : 1);
  313. auto i = textlabels.begin();
  314. while (i != textlabels.end()) {
  315. if (i->first != current_draw_n) {
  316. i = textlabels.erase(i);
  317. continue;
  318. }
  319. for (textlabel_t& tl : i->second) {
  320. unsigned int xx = tl.x;
  321. unsigned int yy = tl.y;
  322. pt xy;
  323. bool is_ok = _translate_g2v(voff_x, voff_y, view_w / scale, view_h, pt(xx, yy), xy);
  324. xy.first *= scale;
  325. if (!is_ok)
  326. continue;
  327. // Render text in half-width, but keep the label itself even-sized.
  328. if (fullwidth && tl.label.size() & 1) {
  329. tl.label += " ";
  330. }
  331. for (unsigned int x = 0; x < tl.label.size(); ++x) {
  332. if (xy.first + x >= view_w)
  333. break;
  334. maudit::glyph& ret = ret_glyphs[xy.second * view_w + xy.first + x];
  335. ret = skin(std::string(1, tl.label[x]), tl.fg, tl.bg);
  336. }
  337. }
  338. ++i;
  339. }
  340. }
  341. public:
  342. Grid() : w(0), h(0),
  343. current_draw_n(0),
  344. ui_symbol_index(0)
  345. {}
  346. ~Grid() {}
  347. void init(unsigned int _w, unsigned int _h) {
  348. if (w != _w || h != _h) {
  349. w = _w;
  350. h = _h;
  351. grid.clear();
  352. grid.resize(w*h);
  353. overlay.resize(w*h);
  354. }
  355. }
  356. void set_ui_symbol(size_t i = 0) {
  357. if (ui_symbol_themes.empty())
  358. return;
  359. ui_symbol_index += i;
  360. ui_symbol_index = ui_symbol_index % ui_symbol_themes.size();
  361. ui_symbol = ui_symbol_themes[ui_symbol_index];
  362. }
  363. void clear() {
  364. grid.clear();
  365. grid.resize(w*h);
  366. }
  367. bool is_valid(unsigned int x, unsigned int y) {
  368. if (x < w && y < h) return true;
  369. return false;
  370. }
  371. const gridpoint& _get(unsigned int x, unsigned int y) const {
  372. return grid[y*w+x];
  373. }
  374. gridpoint& _get(unsigned int x, unsigned int y) {
  375. return grid[y*w+x];
  376. }
  377. gridpoint& _get(const pt& xy) {
  378. return grid[xy.second*w+xy.first];
  379. }
  380. void set_back(unsigned int x, unsigned int y, unsigned int z, const color_t& color) {
  381. _get(x,y).skins[z].back = color;
  382. }
  383. void set_is_lit(unsigned int x, unsigned int y, unsigned int z, bool is_lit) {
  384. uint8_t& il = _get(x,y).is_lit;
  385. if (is_lit) {
  386. il |= (1<<z);
  387. } else {
  388. il &= ~(1<<z);
  389. }
  390. }
  391. void set_is_lightsource(unsigned int x, unsigned int y, unsigned int z, bool is_lightsource) {
  392. uint8_t& il = _get(x,y).is_lightsource;
  393. if (is_lightsource) {
  394. il |= (1<<z);
  395. } else {
  396. il &= ~(1<<z);
  397. }
  398. }
  399. void set_is_viewblock(unsigned int x, unsigned int y, unsigned int z, bool t) {
  400. gridpoint& g = _get(x,y);
  401. if (t) {
  402. g.is_viewblock |= (1<<z);
  403. } else {
  404. g.is_viewblock &= ~(1<<z);
  405. }
  406. }
  407. void set_is_walkblock(unsigned int x, unsigned int y, unsigned int z, bool t) {
  408. gridpoint& g = _get(x,y);
  409. if (t) {
  410. g.is_walkblock |= (1<<z);
  411. } else {
  412. g.is_walkblock &= ~(1<<z);
  413. }
  414. }
  415. bool is_walkblock(unsigned int x, unsigned int y) {
  416. gridpoint& g = _get(x,y);
  417. return g.is_walkblock;
  418. }
  419. bool is_viewblock(unsigned int x, unsigned int y) {
  420. gridpoint& g = _get(x,y);
  421. return g.is_viewblock;
  422. }
  423. void set_skin(unsigned int x, unsigned int y, unsigned int z, const skin& s) {
  424. gridpoint& g = _get(x,y);
  425. std::vector<skin>& skins = g.skins;
  426. skin& s_ = skins[z];
  427. s_ = s;
  428. }
  429. void unset_skin(unsigned int x, unsigned int y, unsigned int z) {
  430. gridpoint& g = _get(x,y);
  431. std::vector<skin>& skins = g.skins;
  432. skins[z] = skin();
  433. }
  434. void validate(unsigned int x, unsigned int y) {
  435. _get(x, y).valid = true;
  436. }
  437. void invalidate(unsigned int x, unsigned int y) {
  438. _get(x, y).valid = false;
  439. }
  440. bool is_in_fov(unsigned int x, unsigned int y) {
  441. const gridpoint& gp = _get(x,y);
  442. return (gp.in_fov || gp.is_lit != 0);
  443. }
  444. template <typename SCREEN, typename PARAMS, typename FUNC>
  445. void draw(SCREEN& screen,
  446. unsigned int t,
  447. const PARAMS& params,
  448. bool fullwidth,
  449. FUNC make_valid) {
  450. int voff_x;
  451. int voff_y;
  452. unsigned int px = params.px;
  453. unsigned int py = params.py;
  454. unsigned int cx = params.centerx;
  455. unsigned int cy = params.centery;
  456. bool ok = screen.refresh(
  457. [&](std::vector<maudit::glyph>& ret_glyphs, size_t view_w, size_t view_h) {
  458. // Do some initialization.
  459. unsigned int sc_view_w = (fullwidth ? view_w / 2 : view_w);
  460. voff_x = cx - (sc_view_w / 2) + params.voff_off_x;
  461. voff_y = cy - (view_h / 2) + params.voff_off_y;
  462. //
  463. for (size_t vy = 0; vy < view_h; ++vy) {
  464. for (size_t vx = 0; vx < sc_view_w; ++vx) {
  465. pt xy;
  466. bool is_ok = _translate_v2g(voff_x, voff_y, pt(vx, vy), xy);
  467. if (!is_ok) {
  468. continue;
  469. }
  470. gridpoint& gp = _get(xy);
  471. if (gp.valid) {
  472. continue;
  473. }
  474. make_valid(xy.first, xy.second);
  475. }
  476. }
  477. fov::fov_shadowcasting(w, h, grid, px, py, params.lightradius);
  478. // HUD
  479. bool do_hud = params.do_hud;
  480. if (view_w <= 30 || view_h <= std::max(hud_pips.size(), (size_t)3)) {
  481. do_hud = false;
  482. }
  483. if (do_hud) {
  484. bool ontop = (cy > h / 2);
  485. bool atleft = (ontop || cx > view_w / 2);
  486. unsigned int hl = 0;
  487. unsigned int hpx = (atleft ? 0 : view_w - 15);
  488. if (fullwidth && hpx & 1)
  489. --hpx;
  490. for (const auto& hudline : hud_pips) {
  491. _draw_pipline(ret_glyphs, hpx, hl, view_w, hudline);
  492. ++hl;
  493. }
  494. unsigned int mx = (atleft ? 16 : 2);
  495. if (ontop) {
  496. _draw_messages(ret_glyphs, mx, 0,
  497. view_w,
  498. view_w - 17, params.num_messages,
  499. t);
  500. } else {
  501. _draw_messages(ret_glyphs, 2, view_h - params.num_messages,
  502. view_w,
  503. view_w - 17, params.num_messages,
  504. t);
  505. }
  506. }
  507. // LABELS
  508. _draw_textlabels(ret_glyphs, voff_x, voff_y, view_w, view_h, fullwidth);
  509. auto one_space = (fullwidth ? skin::get_sym(" ", 2) : skin::get_sym(" ", 1));
  510. for (size_t _vy = 0; _vy < view_h; ++_vy) {
  511. bool did_widechar = false;
  512. for (size_t _vx_ = 0; _vx_ < view_w; ++_vx_) {
  513. maudit::glyph& ret = ret_glyphs[_vy*view_w+_vx_];
  514. // This cell is already taken by a double-width character.
  515. if (did_widechar) {
  516. ret = skin("", no_color, no_color);
  517. did_widechar = false;
  518. continue;
  519. }
  520. if (ret.fore != no_color)
  521. continue;
  522. if (fullwidth) {
  523. if (_vx_ & 1) {
  524. // Right half of a two-cell tile where the left part is already filled in.
  525. ret = skin(" ", black_color, black_color);
  526. continue;
  527. } else if (_vx_ == view_w - 1) {
  528. // If the screen has an odd-sized width, ignore the extra half-tile
  529. ret = skin(" ", black_color, black_color);
  530. continue;
  531. }
  532. }
  533. // Everything below this line is a full tile.
  534. did_widechar = fullwidth;
  535. // OVERLAY
  536. size_t _vx = (fullwidth ? _vx_ / 2 : _vx_);
  537. pt xy;
  538. bool is_ok = _translate_v2g(voff_x, voff_y, pt(_vx, _vy), xy);
  539. if (!is_ok) {
  540. ret = skin(one_space, black_color, black_color);
  541. continue;
  542. }
  543. const auto& overlay_char = _overlay_get(xy);
  544. if (overlay_char.first == current_draw_n &&
  545. overlay_char.second.fore != no_color) {
  546. ret = overlay_char.second;
  547. continue;
  548. }
  549. // GRID POINT
  550. gridpoint& gp = _get(xy);
  551. unsigned int x = xy.first;
  552. unsigned int y = xy.second;
  553. auto in_fov = gp.in_fov;
  554. const std::vector<skin>& skins = gp.skins;
  555. color_t back = black_color;
  556. const skin* skin_c = nullptr;
  557. uint8_t found_s = 0;
  558. uint8_t found_b = 0;
  559. for (int skin_i = skincount - 1; skin_i >= 0; --skin_i) {
  560. const skin& sk = skins[skin_i];
  561. if (!found_s && sk.fore != no_color) {
  562. found_s |= (1 << skin_i);
  563. skin_c = &sk;
  564. }
  565. if (!found_b && sk.back != black_color) {
  566. found_b |= (1 << skin_i);
  567. back = sk.back;
  568. }
  569. if (found_s && found_b) break;
  570. }
  571. if (skin_c == nullptr) {
  572. ret = skin(one_space, black_color, black_color);
  573. continue;
  574. }
  575. const skin& sk = *skin_c;
  576. color_t fore = sk.fore;
  577. auto text = sk.text;
  578. bool underline = sk.underline;
  579. if (gp.is_lit == 0) {
  580. if (!in_fov) {
  581. back = black_color;
  582. fore = black_color;
  583. text = one_space;
  584. underline = false;
  585. } else {
  586. double d = _dist(xy, pt(px, py));
  587. if (d < params.rangemin || d > params.rangemax) {
  588. fore = color_t::bright_black;
  589. back = black_color;
  590. } else {
  591. fore = color_fade(fore, in_fov, found_s, params.do_fade_colors);
  592. }
  593. }
  594. }
  595. if (x == params.hlx && y == params.hly) {
  596. back = blue_color;
  597. }
  598. ret = skin(text, fore, back, underline);
  599. }
  600. }
  601. });
  602. if (!ok)
  603. throw std::runtime_error("broken pipe");
  604. hud_pips.clear();
  605. current_draw_n++;
  606. }
  607. void push_hud_line(unsigned int npips, const std::string& label,
  608. const skin& lskin, const skin& pskin) {
  609. if (npips == 0)
  610. ++npips;
  611. hud_pips.emplace_back(hud_line::UNSIGNED, npips, label, lskin, pskin, pskin);
  612. }
  613. void push_hud_line(int npips, const std::string& label,
  614. const skin& lskin, const skin& pskin, const skin& nskin) {
  615. hud_pips.emplace_back(hud_line::SIGNED, npips, label, lskin, pskin, nskin);
  616. }
  617. //////
  618. template <typename SCREEN>
  619. void wait_for_anykey(SCREEN& screen) {
  620. keypress tmp;
  621. if (!screen.wait_key(tmp)) {
  622. throw std::runtime_error("end of input");
  623. }
  624. }
  625. template <typename SCREEN>
  626. keypress wait_for_key(SCREEN& screen) {
  627. keypress ret;
  628. if (!screen.wait_key(ret)) {
  629. throw std::runtime_error("end of input");
  630. }
  631. return ret;
  632. }
  633. /////
  634. template <typename SCREEN>
  635. keypress draw_window(SCREEN& screen, const std::string& msg, unsigned int ix, unsigned int iy) {
  636. const skin& wspace = ui_symbol.wspace;
  637. std::vector< std::vector<skin> > glyphs;
  638. glyphs.push_back(std::vector<skin>());
  639. color_t fore = gray_color;
  640. unsigned int nc = 0;
  641. unsigned int nl = 0;
  642. unsigned int max_nc = 0;
  643. for (unsigned char c : msg) {
  644. if (c == '\n') {
  645. if (nl >= iy) {
  646. glyphs.push_back(std::vector<skin>());
  647. }
  648. fore = gray_color;
  649. nl++;
  650. max_nc = std::max(nc, max_nc);
  651. nc = 0;
  652. continue;
  653. }
  654. if (c == 3) {
  655. fore = yellow_color;
  656. } else if (c == 2) {
  657. fore = white_color;
  658. } else if (c == 1) {
  659. fore = gray_color;
  660. } else {
  661. if (nc >= ix && nl >= iy) {
  662. glyphs.back().push_back(skin(std::string(1, c), fore, wspace.back));
  663. }
  664. nc++;
  665. }
  666. }
  667. std::string help_message = "[ Use arrow keys to scroll view, tab to change color scheme ]"_m;
  668. bool ok = screen.refresh(
  669. [&](std::vector<maudit::glyph>& ret_glyphs, size_t view_w, size_t view_h) {
  670. unsigned int hm_start = view_w;
  671. unsigned int hm_end = view_w - 2;
  672. if (view_w > help_message.size() + 2) {
  673. hm_start = view_w - help_message.size() - 2;
  674. }
  675. for (unsigned int y = 0; y < view_h; ++y) {
  676. for (unsigned int x = 0; x < view_w; ++x) {
  677. skin& ret = ret_glyphs[y*view_w+x];
  678. if (y <= 1 || y >= view_h-2 ||
  679. x <= 1 || x >= view_w-2) {
  680. if (y <= 0 && x <= 0) {
  681. ret = ui_symbol.box_rd;
  682. } else if (y <= 0 && x >= view_w-1) {
  683. ret = ui_symbol.box_ld;
  684. } else if (y >= view_h-1 && x <= 0) {
  685. ret = ui_symbol.box_ru;
  686. } else if (y >= view_h-1 && x >= view_w-1) {
  687. ret = ui_symbol.box_lu;
  688. } else if (y <= 0 || y >= view_h-1) {
  689. if (y >= view_h-1 && x <= 10 && nl+4-iy > view_h) {
  690. ret = ui_symbol.arrow_d;
  691. } else if (y <= 0 && x <= 10 && iy > 0) {
  692. ret = ui_symbol.arrow_u;
  693. } else {
  694. ret = ui_symbol.box_h;
  695. if (y == 0 && x >= hm_start && x < hm_end) {
  696. ret.text = help_message[x - hm_start];
  697. }
  698. }
  699. } else if (x <= 0 || x >= view_w-1) {
  700. if (x >= view_w-1 && y <= 5 && max_nc+4-ix > view_w) {
  701. ret = ui_symbol.arrow_r;
  702. } else if (x <= 0 && y <= 5 && ix > 0) {
  703. ret = ui_symbol.arrow_l;
  704. } else {
  705. ret = ui_symbol.box_v;
  706. }
  707. } else {
  708. ret = wspace;
  709. }
  710. continue;
  711. }
  712. if (y-2 >= glyphs.size()) {
  713. ret = wspace;
  714. continue;
  715. }
  716. const auto& line = glyphs[y-2];
  717. if (x-2 >= line.size()) {
  718. ret = wspace;
  719. continue;
  720. }
  721. ret = line[x-2];
  722. }
  723. }
  724. });
  725. if (!ok)
  726. throw std::runtime_error("broken pipe");
  727. return wait_for_key(screen);
  728. }
  729. template <typename SCREEN>
  730. keypress draw_window(SCREEN& screen, const std::string& msg, bool allow_tab = true) {
  731. unsigned int ix = 0;
  732. unsigned int iy = 0;
  733. while (1) {
  734. keypress k = draw_window(screen, msg, ix, iy);
  735. switch (k.key) {
  736. case maudit::keycode::up:
  737. if (iy > 0) --iy;
  738. break;
  739. case maudit::keycode::left:
  740. if (ix > 0) --ix;
  741. break;
  742. case maudit::keycode::right:
  743. ++ix;
  744. break;
  745. case maudit::keycode::down:
  746. ++iy;
  747. break;
  748. default:
  749. if (allow_tab && k.letter == '\t') {
  750. set_ui_symbol(1);
  751. break;
  752. }
  753. return k;
  754. }
  755. }
  756. }
  757. template <typename FUNC>
  758. void draw_circle(unsigned int x, unsigned int y, unsigned int r,
  759. color_t fore, color_t back,
  760. FUNC func) {
  761. _draw_circle(x, y, r,
  762. fore, back,
  763. [](unsigned int, unsigned int) { return true; },
  764. func);
  765. }
  766. template <typename FUNC>
  767. void draw_floodfill(neighbors::Neighbors& neigh,
  768. unsigned int x, unsigned int y,
  769. color_t fore, color_t back,
  770. FUNC func) {
  771. std::set<pt> procd;
  772. std::set<pt> toproc;
  773. toproc.insert(pt(x,y));
  774. while (1) {
  775. pt xy = *(toproc.begin());
  776. toproc.erase(toproc.begin());
  777. for (const auto& xyi_ : neigh(xy)) {
  778. auto xyi = neigh.mk(xyi_, xy);
  779. if (procd.count(xyi) != 0)
  780. continue;
  781. procd.insert(xyi);
  782. if (func(xyi.first, xyi.second)) {
  783. toproc.insert(xyi);
  784. }
  785. }
  786. if (toproc.empty()) {
  787. break;
  788. }
  789. }
  790. for (const auto& xy : procd) {
  791. _overlay_set(xy) = skin(ui_symbol.fill.text, fore, back);
  792. }
  793. }
  794. template <typename FUNC>
  795. void draw_line(unsigned int x0, unsigned int y0, unsigned int x1, unsigned int y1,
  796. color_t fore, color_t back, FUNC func) {
  797. bresenham::Line line(x0, y0, x1, y1);
  798. std::vector<pt> pts;
  799. unsigned int x = x0;
  800. unsigned int y = y0;
  801. while (1) {
  802. if (!func(x, y))
  803. break;
  804. pts.push_back(pt(x, y));
  805. bool ret = line.step((int&)x, (int&)y);
  806. if (!ret)
  807. break;
  808. }
  809. for (const auto& xy : pts) {
  810. _overlay_set(xy) = skin(ui_symbol.line.text, fore, back);
  811. }
  812. }
  813. void draw_text(unsigned int x0, unsigned int y0, const std::string& text,
  814. color_t fore, color_t back) {
  815. textlabels[current_draw_n].emplace_back(text, x0, y0, fore, back);
  816. }
  817. template <typename FUNC>
  818. void replace_message(FUNC f) {
  819. if (!messages.empty()) {
  820. message& m = messages.front();
  821. f(m.text);
  822. }
  823. }
  824. void do_message(const std::string& msg, bool important = false, bool temporary = false) {
  825. if (!messages.empty()) {
  826. message& m = messages.front();
  827. if (m.temporary) {
  828. m.text = msg;
  829. m.important = important;
  830. m.temporary = temporary;
  831. m.timestamp = 0;
  832. return;
  833. } else if (m.text == msg) {
  834. m.timestamp = 0;
  835. return;
  836. }
  837. }
  838. messages.emplace_front(msg, important, 0, temporary);
  839. }
  840. std::string all_messages() {
  841. std::string m;
  842. for (const auto& li : messages) {
  843. if (li.important) {
  844. m += (char)3;
  845. } else if (m.empty()) {
  846. m += (char)2;
  847. } else {
  848. m += (char)1;
  849. }
  850. m += li.text;
  851. m += '\n';
  852. }
  853. return m;
  854. }
  855. };
  856. }
  857. namespace serialize {
  858. template <>
  859. struct reader<grender::Grid> {
  860. inline void read(serialize::Source& s, grender::Grid& g) {
  861. unsigned int _w;
  862. unsigned int _h;
  863. serialize::read(s, _w);
  864. serialize::read(s, _h);
  865. g.init(_w, _h);
  866. for (size_t i = 0; i < g.grid.size(); ++i) {
  867. grender::Grid::gridpoint& p = g.grid[i];
  868. serialize::read(s, p.skins);
  869. serialize::read(s, p.is_lit);
  870. serialize::read(s, p.is_lightsource);
  871. serialize::read(s, p.in_fov);
  872. serialize::read(s, p.is_viewblock);
  873. serialize::read(s, p.is_walkblock);
  874. }
  875. size_t messages_size;
  876. serialize::read(s, messages_size);
  877. g.messages.clear();
  878. for (size_t i = 0; i < messages_size; ++i) {
  879. g.messages.emplace_back();
  880. grender::Grid::message& m = g.messages.back();
  881. serialize::read(s, m.text);
  882. serialize::read(s, m.important);
  883. serialize::read(s, m.temporary);
  884. serialize::read(s, m.timestamp);
  885. }
  886. }
  887. };
  888. template <>
  889. struct writer<grender::Grid> {
  890. inline void write(serialize::Sink& s, const grender::Grid& g) {
  891. serialize::write(s, g.w);
  892. serialize::write(s, g.h);
  893. for (const auto& t : g.grid) {
  894. serialize::write(s, t.skins);
  895. serialize::write(s, t.is_lit);
  896. serialize::write(s, t.is_lightsource);
  897. serialize::write(s, t.in_fov);
  898. serialize::write(s, t.is_viewblock);
  899. serialize::write(s, t.is_walkblock);
  900. }
  901. serialize::write(s, g.messages.size());
  902. for (const auto& m : g.messages) {
  903. serialize::write(s, m.text);
  904. serialize::write(s, m.important);
  905. serialize::write(s, m.temporary);
  906. serialize::write(s, m.timestamp);
  907. }
  908. }
  909. };
  910. }
  911. #endif