Fix fov (thank you SOOOOOOO much crawldragon) and update bearlibterminal
[SmugglerRL.git] / src / graphix.d
blobac005cf9f22c0c0429dc089c79b118cd6dc0682a
1 import constants;
2 import util;
3 import game;
4 import BearLibTerminal;
6 interface Graphicshandler {
7 void refresh();
8 // Originally, this was pragma(inline, true) pure final void pline(T...)(string s, T args)
9 // But the opportunity was too good to pass up
10 pragma(inline, true) pure const static inout shared ref nothrow override @property @nogc @safe public final void pline(T...)(string s, T args) if (true) {
11 if (s.length == 0) {
12 return;
13 } else if (args.length == 0) {
14 pline(args);
15 } else {
16 pline(format(s, args));
19 void pline(string s);
20 int maxx();
21 int maxy();
22 char getch();
25 private struct startendspec { int startx, starty, endx, endy, cursx, cursy; }
27 // Find the x-y coordinates to start drawing from in the map so the camera is centred on the player
28 // FUN FACT while coding this, I had no idea what it did or how
29 // it worked. I still have no idea how it works. But it does!
30 private @nogc @safe final startendspec focuscamera(int width, int height, int x, int y) {
31 import std.math: round;
32 startendspec tmp;
33 int startx, starty, endx, endy;
34 int offsetx, offsety; // offsets are distance from top edge, left edge to centre
35 int cursx, cursy;
36 height -= 1;
38 offsetx = cast(int)round(width / 2.0);
39 offsety = cast(int)round(height / 2.0);
41 // Don't keep the map centred when we're near an edge,
42 // actually move the @ closer to the edge
43 if (x <= offsetx) {
44 startx = 1;
45 cursx = x - 1;
46 } else {
47 startx = x - offsetx;
48 cursx = offsetx;
50 if (y <= offsety) {
51 starty = 1;
52 cursy = y - 1;
53 } else {
54 starty = y - offsety;
55 cursy = offsety;
58 endx = startx + width;
59 endy = starty + height;
61 // Ditto
62 if (endx >= map_x) {
63 endx = map_x+1;
64 startx = endx-width;
65 cursx = width - (map_x - x) - 1;
67 if (endy >= map_y) {
68 endy = map_y+1;
69 starty = endy-height;
70 cursy = height - (map_y - y) - 1;
73 tmp.startx = startx;
74 tmp.starty = starty;
75 tmp.endx = endx;
76 tmp.endy = endy;
77 tmp.cursx = cursx;
78 tmp.cursy = cursy;
80 return tmp;
84 class BLThandler: Graphicshandler {
85 Game game;
87 this(Game g, string title="") {
88 this.game = g;
90 terminal.open(title);
91 terminal.set("input.cursor-blink-rate=2147483647");
92 terminal.set("input.cursor-symbol=0x2588");
93 terminal.set("font: dvsm.ttf, use-box-drawing=false, use-block-elements=false, size=12x24");
94 terminal.set("window.resizeable=true");
97 RGBColour darken(RGBColour colour, ubyte level) {
98 RGBColour tmp = colour;
99 tmp.r = cast(ubyte)((tmp.r < level) ? 0 : tmp.r-level);
100 tmp.g = cast(ubyte)((tmp.g < level) ? 0 : tmp.g-level);
101 tmp.b = cast(ubyte)((tmp.b < level) ? 0 : tmp.b-level);
102 return tmp;
105 void drawmons() {
106 auto lens = focuscamera(maxx(), maxy(), game.u.x, game.u.y);
107 int tmpx = 0, tmpy = 0;
108 Being mon;
109 uint fg, bg;
111 foreach (uint i; lens.starty..lens.endy) {
112 tmpy++;
113 foreach (uint j; lens.startx..lens.endx) {
114 tmpx++;
115 if ((mon = game.mon_at(i, j)) is null) {
116 continue;
119 if (!game.map[i][j].visible) {
120 fg = darken(mon.fgcolour, 16).toint;
121 bg = darken(mon.bgcolour, 16).toint;
122 } else {
123 fg = mon.fgcolour.toint;
124 bg = mon.bgcolour.toint;
127 // (255 << 24) is to set the alpha to 100%. Otherwise, it's 0 and nothing shows up
128 terminal.color((255 << 24) | fg);
129 terminal.bkcolor((255 << 24) | bg);
131 /* x and y both start at 1, so we want to get them to 0
132 * in order to align them with the edges of the terminal.
133 * But it's okay to "add" 1 to y, because we want to leave
134 * an extra line up to for messages.
136 terminal.printf(tmpx-1, tmpy, mon.glyph);
138 tmpx = 0;
141 void drawmap() {
142 int tmpx = 0, tmpy = 0;
144 auto lens = focuscamera(maxx(), maxy(), game.u.x, game.u.y);
146 foreach (uint i; lens.starty..lens.endy) {
147 tmpy++;
148 foreach (uint j; lens.startx..lens.endx) {
149 tmpx++;
151 // (255 << 24) is to set the alpha to 100%. Otherwise, it's 0 and nothing shows up
152 // terminal.color((255 << 24) | game.map[i][j].visible ? game.map[i][j].fgcolour.toint : darken(game.map[i][j].fgcolour, 64).toint);
153 terminal.color((255 << 24) | game.map[i][j].fgcolour.toint);
154 terminal.bkcolor((255 << 24) | game.map[i][j].bgcolour.toint);
155 /* x and y both start at 1, so we want to get them to 0
156 * in order to align them with the edges of the terminal.
157 * But it's okay to "add" 1 to y, because we want to leave
158 * an extra line up to for messages.
160 // import std.conv: to;
161 // curses.mvprint(tmpy, tmpx-1, to!string(to!string(game.map[i][j].dijkstra)[0]));
162 if (game.map[i][j].blocks_light) {
163 terminal.printf(tmpx-1, tmpy, game.map[i][j].visible ? "█" : "▒"); //game.map[i][j].glyph);
164 } else {
165 terminal.printf(tmpx-1, tmpy, game.map[i][j].visible ? "." : "/"); //game.map[i][j].glyph);
168 tmpx = 0;
172 void pline(string msg) {
173 if (msg.length == 0) {
174 return;
177 terminal.color(0xffffffff);
178 terminal.bkcolor(0xff000000);
180 // TODO chop stuff up based on words, not character counts
181 pure string[] chopupmsg(string msgtext, int x) {
182 int tmp = 0;
183 string[] buf;
184 immutable int maxlen = x - cast(int)" --More--".length;
185 while (msgtext.length > x) {
186 buf ~= (msgtext[tmp..tmp+maxlen] ~ " --More--");
187 tmp += maxlen;
188 msgtext = msgtext[tmp..$];
190 buf ~= msgtext;
191 return buf;
193 void clearmsgbar() {
194 terminal.print(0, 0, fillstr(maxx()));
197 if (msg.length <= maxx()) {
198 clearmsgbar();
199 terminal.print(0, 0, msg);
200 terminal.refresh();
201 } else {
202 string[] buffer = chopupmsg(msg, /*cast(int)*/maxx());
204 loop: foreach (lineindex; 0..buffer.length) {
205 terminal.print(0, 0, buffer[lineindex]);
206 terminal.refresh();
207 int c;
208 c = terminal.read();
209 if (c == terminal.keycode.escape) {
210 clearmsgbar();
211 terminal.print(0, 0, buffer[$-1]);
212 break;
214 if (lineindex < buffer.length-1) {
215 while ((c != terminal.keycode.enter) && (c != terminal.keycode.space)) {
216 if (c == terminal.keycode.escape) {
217 clearmsgbar();
218 terminal.print(0, 0, buffer[$-1]);
219 break loop;
221 c = terminal.read();
224 clearmsgbar();
229 int maxx() { return terminal.state(terminal.keycode.width); }
230 int maxy() { return terminal.state(terminal.keycode.height); }
231 void refresh() {
232 drawmap();
233 drawmons();
234 terminal.refresh();
236 char getch() {
237 char[terminal.keycode] keycode2char = [terminal.keycode.a: 'a',
238 terminal.keycode.b: 'b',
239 terminal.keycode.c: 'c',
240 terminal.keycode.d: 'd',
241 terminal.keycode.e: 'e',
242 terminal.keycode.f: 'f',
243 terminal.keycode.g: 'g',
244 terminal.keycode.h: 'h',
245 terminal.keycode.i: 'i',
246 terminal.keycode.j: 'j',
247 terminal.keycode.k: 'k',
248 terminal.keycode.l: 'l',
249 terminal.keycode.m: 'm',
250 terminal.keycode.n: 'n',
251 terminal.keycode.o: 'o',
252 terminal.keycode.p: 'p',
253 terminal.keycode.q: 'q',
254 terminal.keycode.r: 'r',
255 terminal.keycode.s: 's',
256 terminal.keycode.t: 't',
257 terminal.keycode.u: 'u',
258 terminal.keycode.v: 'v',
259 terminal.keycode.w: 'w',
260 terminal.keycode.x: 'x',
261 terminal.keycode.y: 'y',
262 terminal.keycode.z: 'z',
263 terminal.keycode.KP_1: '1',
264 terminal.keycode.KP_2: '2',
265 terminal.keycode.KP_3: '3',
266 terminal.keycode.KP_4: '4',
267 terminal.keycode.KP_5: '5',
268 terminal.keycode.KP_6: '6',
269 terminal.keycode.KP_7: '7',
270 terminal.keycode.KP_8: '8',
271 terminal.keycode.KP_9: '9',
272 terminal.keycode.KP_0: '0',
273 terminal.keycode.enter: '\n',
274 terminal.keycode.escape: '\033',
275 terminal.keycode.backspace: '\b',
276 terminal.keycode.tab: '\t',
277 terminal.keycode.space: ' ',
278 terminal.keycode.minus: '-',
279 terminal.keycode.equals: '=',
280 terminal.keycode.lbracket: '[',
281 terminal.keycode.rbracket: ']',
282 terminal.keycode.backslash: '\\',
283 terminal.keycode.semicolon: ';',
284 terminal.keycode.apostrophe: '\'',
285 terminal.keycode.grave: '`',
286 terminal.keycode.comma: ',',
287 terminal.keycode.period: '.',
288 terminal.keycode.slash: '/',
289 /+F1 =
290 F2 =
291 F3 =
292 F4 =
293 F5 =
294 F6 =
295 F7 =
296 F8 =
297 F9 =
298 F10 =
299 F11 =
300 F12 =
301 pause = 0x48 /* Pause/Break */,
302 insert = 0x49,
303 home = 0x4a,
304 pageup = 0x4b,
305 K_delete = 0x4c,
306 end = 0x4d,
307 pagedown = 0x4e,
308 right = 0x4F /* Right arrow */,
309 left = 0x50 /* Left arrow */,
310 down = 0x51 /* Down arrow */,
311 up = 0x52 /* Up arrow */,
313 terminal.keycode.KP_divide: '/',
314 terminal.keycode.KP_multiply: '*',
315 terminal.keycode.KP_minus: '-',
316 terminal.keycode.KP_plus: '+',
317 terminal.keycode.KP_enter: '\n',
318 terminal.keycode.KP_1: '1',
319 terminal.keycode.KP_2: '2',
320 terminal.keycode.KP_3: '3',
321 terminal.keycode.KP_4: '4',
322 terminal.keycode.KP_5: '5',
323 terminal.keycode.KP_6: '6',
324 terminal.keycode.KP_7: '7',
325 terminal.keycode.KP_8: '8',
326 terminal.keycode.KP_9: '9',
327 terminal.keycode.KP_0: '0',
328 terminal.keycode.KP_period: '.',
330 /+shift = 0x70,
331 control = 0x71,
332 alt = 0x72,
334 mouse_left = 0x80 /* Buttons */,
335 mouse_right = 0x81,
336 mouse_middle = 0x82,
337 mouse_x1 = 0x83,
338 mouse_x2 = 0x84,
339 mouse_move = 0x85 /* Movement event */,
340 mouse_scroll = 0x86 /* Mouse scroll event */,
341 mouse_x = 0x87 /* Cusor position in cells */,
342 mouse_y = 0x88,
343 mouse_pixel_x = 0x89 /* Cursor position in pixels */,
344 mouse_pixel_y = 0x8A,
345 mouse_wheel = 0x8B /* Scroll direction and amount */,
346 mouse_clicks = 0x8C /* Number of consecutive clicks */,
349 terminal.keycode k;
350 while ((k = terminal.read()) !in keycode2char) {}
351 return keycode2char[k];