4 import BearLibTerminal
;
6 interface Graphicshandler
{
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) {
13 } else if (args
.length
== 0) {
16 pline(format(s
, args
));
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
;
33 int startx
, starty
, endx
, endy
;
34 int offsetx
, offsety
; // offsets are distance from top edge, left edge to centre
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
58 endx
= startx
+ width
;
59 endy
= starty
+ height
;
65 cursx
= width
- (map_x
- x
) - 1;
70 cursy
= height
- (map_y
- y
) - 1;
84 class BLThandler
: Graphicshandler
{
87 this(Game g
, string 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
);
106 auto lens
= focuscamera(maxx(), maxy(), game
.u
.x
, game
.u
.y
);
107 int tmpx
= 0, tmpy
= 0;
111 foreach (uint i
; lens
.starty
..lens
.endy
) {
113 foreach (uint j
; lens
.startx
..lens
.endx
) {
115 if ((mon
= game
.mon_at(i
, j
)) is null) {
119 if (!game
.map
[i
][j
].visible
) {
120 fg
= darken(mon
.fgcolour
, 16).toint
;
121 bg
= darken(mon
.bgcolour
, 16).toint
;
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
);
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
) {
148 foreach (uint j
; lens
.startx
..lens
.endx
) {
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);
165 terminal
.printf(tmpx
-1, tmpy
, game
.map
[i
][j
].visible ?
"." : "/"); //game.map[i][j].glyph);
172 void pline(string msg
) {
173 if (msg
.length
== 0) {
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
) {
184 immutable int maxlen
= x
- cast(int)" --More--".length
;
185 while (msgtext
.length
> x
) {
186 buf
~= (msgtext
[tmp
..tmp
+maxlen
] ~ " --More--");
188 msgtext
= msgtext
[tmp
..$];
194 terminal
.print(0, 0, fillstr(maxx()));
197 if (msg
.length
<= maxx()) {
199 terminal
.print(0, 0, msg
);
202 string
[] buffer
= chopupmsg(msg
, /*cast(int)*/maxx());
204 loop: foreach (lineindex
; 0..buffer
.length
) {
205 terminal
.print(0, 0, buffer
[lineindex
]);
209 if (c
== terminal
.keycode
.escape
) {
211 terminal
.print(0, 0, buffer
[$-1]);
214 if (lineindex
< buffer
.length
-1) {
215 while ((c
!= terminal
.keycode
.enter) && (c
!= terminal
.keycode
.space
)) {
216 if (c
== terminal
.keycode
.escape
) {
218 terminal
.print(0, 0, buffer
[$-1]);
229 int maxx() { return terminal
.state(terminal
.keycode
.width
); }
230 int maxy() { return terminal
.state(terminal
.keycode
.height
); }
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
: '/',
301 pause = 0x48 /* Pause/Break */,
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
: '.',
334 mouse_left = 0x80 /* Buttons */,
339 mouse_move = 0x85 /* Movement event */,
340 mouse_scroll = 0x86 /* Mouse scroll event */,
341 mouse_x = 0x87 /* Cusor position in cells */,
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 */,
350 while ((k
= terminal
.read()) !in keycode2char
) {}
351 return keycode2char
[k
];