6 interface Graphicshandler
{
8 pragma(inline
, true) pure final void pline(T
...)(string s
, T args
) {
11 } else if (args
.length
== 0) {
14 pline(format(s
, args
));
22 class Ncurseshandler
: Graphicshandler
{
24 ushort[ubyte[2]] colourlists
;
28 import std
.stdio
: writefln
;
29 // objects are references. I don't pretend to like it,
30 // but in this case it means we don't have to use a pointer.
33 // need this for unicode
34 import core
.stdc
.locale
: setlocale
, LC_CTYPE
;
35 setlocale(LC_CTYPE
, "");
40 curses
.cbreak(); // Receive data 1 character at a time
41 // By default, the terminal will buffer inputted
42 // characters until the user presses enter,
43 // and then present them to the program. This makes
44 // the terminal present us with one character at a time
45 curses
.noecho(); // Don't echo output
46 if ((maxx() < min_x
) ||
(maxy() < min_y
)) {
48 writefln("Error: the terminal needs to be at least %dx%d, but it's really %dx%d", min_x
, min_y
, curses
.COLS
, curses
.LINES
);
55 int tmpx
= 0, tmpy
= 0;
57 // FUN FACT while coding this, I had no idea what it did or how
58 // it worked. I still have no idea how it works. But it does!
59 int[string
] getstartendxy() {
60 import std
.math
: round
;
62 int startx
, starty
, endx
, endy
;
63 int offsetx
, offsety
, x
= game
.x
, y
= game
.y
; // offsets are distance from top edge, left edge to centre
66 int height
= maxy() - 1;
68 offsetx
= cast(int)round(width
/ 2.0);
69 offsety
= cast(int)round(height
/ 2.0);
71 // Don't keep the map centred when we're near an edge,
72 // actually move the @ closer to the edge
88 endx
= startx
+ width
;
89 endy
= starty
+ height
;
95 cursx
= width
- (map_x
- x
) - 1;
100 cursy
= height
- (map_y
- y
) - 1;
103 tmp
["startx"] = startx
;
104 tmp
["starty"] = starty
;
107 tmp
["cursx"] = cursx
;
108 tmp
["cursy"] = cursy
;
113 int[string
] t
= getstartendxy();
115 foreach (uint i
; t
["starty"]..t
["endy"]) {
117 foreach (uint j
; t
["startx"]..t
["endx"]) {
119 ubyte fg
= get256colour(game
.map
[i
][j
].fgcolour
), bg
= get256colour(game
.map
[i
][j
].bgcolour
);
121 curses
.attron(curses
.COLOR_PAIR(getclr(fg
, bg
))&curses
.A_COLOR
);
122 /* x and y both start at 1, so we want to get them to 0
123 * in order to align them with the edges of the terminal.
124 * But it's okay to "add" 1 to y, because we want to leave
125 * an extra line up to for messages.
127 curses
.mvprint(tmpy
, tmpx
-1, game
.map
[i
][j
].glyph
);
128 curses
.attroff(curses
.COLOR_PAIR(getclr(fg
, bg
))&curses
.A_COLOR
);
134 curses
.move(t
["cursy"]+1, t
["cursx"]);
139 void pline(string msg
) {
143 // TODO chop stuff up based on words, not character counts
144 pure string
[] chopupmsg(string msgtext
, int x
) {
147 immutable int maxlen
= x
- cast(int)" --More--".length
;
148 while (msgtext
.length
> x
) {
149 buf
~= (msgtext
[tmp
..tmp
+maxlen
] ~ " --More--");
151 msgtext
= msgtext
[tmp
..$];
156 if (msg
.length
<= maxx()) {
157 curses
.mvprint(0, 0, msg
);
160 string
[] buffer
= chopupmsg(msg
, /*cast(int)*/maxx());
162 loop: foreach (lineindex
; 0..buffer
.length
) {
163 curses
.mvprint(0, 0, buffer
[lineindex
]);
168 // Clear the message bar
169 curses
.mvprint(0, 0, fillstr(maxx()));
170 curses
.mvprint(0, 0, buffer
[$-1]);
174 if (lineindex
< buffer
.length
-1) {
175 while ((c
!= '\n') && (c
!= ' ')) {
177 curses
.mvprint(0, 0, fillstr(maxx()));
178 curses
.mvprint(0, 0, buffer
[$-1]);
184 curses
.mvprint(0, 0, fillstr(maxx()));
190 pragma(inline
, true) {
198 private ushort getclr(ubyte fg
, ubyte bg
) {
199 ubyte[2] tmp
= [fg
, bg
];
200 if (tmp
in colourlists
) {
201 return colourlists
[tmp
];
204 curses
.init_pair(biggestclr
, fg
, bg
);
205 colourlists
[tmp
] = biggestclr
;