fix deprecation warnings from latest dmd
[SmugglerRL.git] / src / graphix.d
blob997735a59aaab53d6f4d11d604523945490011c6
1 import constants;
2 import util;
3 import graphix0;
4 import map;
5 import stdlib;
6 import colour;
7 import being;
8 import item;
9 import logging;
11 interface Graphics1 {
12 void refresh(uint uy, uint ux, list!Being mons, list!Item items, Map map);
13 // Originally, this was pragma(inline, true) pure final void pline(T...)(string s, T args)
14 // But the opportunity was too good to pass up
15 // then it was
16 //pragma(inline, true) pure const static inout shared ref nothrow override @property @nogc @safe public final void
17 // in all its glory. But after about a year, I found out that this didn't actually work, which brings you to the state you see today :<<
18 pragma(inline, true) public final void pline(T...)(T args) {
19 if (args.length == 1) {
20 this.pline(to!string(args[0]));
21 } else {
22 this.pline(format(args));
25 void pline(string s);
26 int maxx();
27 int maxy();
28 dchar getch();
29 void close();
32 private struct Lens { int startx, endx, cursx, starty, endy, cursy; }
34 // Find the x-y coordinates to start drawing from in the map so the camera is centred on the player
35 // FUN FACT while coding this, I had no idea what it did or how
36 // it worked. I still have no idea how it works. But it does!
37 private @nogc @safe final Lens focuscamera(int width, int height, int y, int x) {
38 import std.math: round;
39 Lens tmp;
40 int startx, starty, endx, endy;
41 int offsetx, offsety; // offsets are distance from top edge, left edge to centre
42 int cursx, cursy;
43 height -= 1;
45 offsetx = cast(int)round(width / 2.0);
46 offsety = cast(int)round(height / 2.0);
48 startx = x - offsetx;
49 cursx = offsetx;
50 starty = y - offsety;
51 cursy = offsety;
53 endx = startx + width;
54 endy = starty + height;
56 tmp.startx = startx;
57 tmp.starty = starty;
58 tmp.endx = endx;
59 tmp.endy = endy;
60 tmp.cursx = cursx;
61 tmp.cursy = cursy;
63 return tmp;
66 class Default_chargfx(G0type: CharGfx0): Graphics1 {
67 CharGfx0 graphics0;
68 bool msgdrawn;
69 list!string msg_queue;
71 this(string title="") {
72 this.graphics0 = new G0type();
74 graphics0.settitle(title);
76 void close() { graphics0.close(); }
78 void drawmons(Lens lens, list!Being mons, Map map) {
79 const Lightmap umap = mons.front.lightmap;
80 foreach (mon; mons) {
81 if (!umap[mon.loc].visible) {
82 continue;
85 // so this is a bit confusing. We're testing if it's not in range and if so continuing
86 if (!(((lens.starty <= cast(int)mon.loc.y) && (cast(int)mon.loc.y < lens.endy))
87 && (lens.startx <= cast(int)mon.loc.x) && (cast(int)mon.loc.x < lens.endx))) {
88 continue;
91 /* By default, we're just drawing at (y, x). But we
92 * need to draw one place lower than normal to make room
93 * for the message bar.
95 with (mon.attrs) {
96 graphics0.mvaddch(mon.glyph, mon.loc.y - lens.starty + 1, mon.loc.x - lens.startx, mon.fgcolour, mon.bgcolour, bold, italic, underline, reverse);
100 void drawitems(Lens lens, list!Item items, const ref Lightmap umap) {
101 foreach (const ref item; items) {
102 if (!umap[item.loc].visible) {
103 continue;
106 // so this is a bit confusing. We're testing if it's not in range and if so continuing
107 if (!(((lens.starty <= cast(int)item.loc.y) && (cast(int)item.loc.y < lens.endy))
108 && (lens.startx <= cast(int)item.loc.x) && (cast(int)item.loc.x < lens.endx))) {
109 continue;
112 graphics0.mvaddch(item.glyph, item.loc.y - lens.starty + 1, item.loc.x - lens.startx, RGBColour(0xffffff), RGBColour(0xff0000), false, false, false, false);
116 void drawmap(Lens lens, Map map, const ref Lightmap lights) {
117 foreach (y; lens.starty..lens.endy) {
118 foreach (x; lens.startx..lens.endx) {
119 Vector2n maploc = Vector2n(y, x);
121 /* x and y both start at 1, so we want to get them to 0
122 * in order to align them with the edges of the terminal.
123 * But it's okay to "add" 1 to y, because we want to leave
124 * an extra line up to for messages.
126 with (map[maploc].attrs)
127 graphics0.mvaddch(map[maploc].glyph, y - lens.starty + 1, x - lens.startx, lights[maploc].visible ? map[maploc].fgcolour.lighten(20) : map[maploc].fgcolour.darken(20), map[maploc].bgcolour, bold, italic, underline, reverse);
133 void clearmsgbar() {
134 graphics0.printext(fillstr(maxx()), 0, 0);
137 private static immutable more = " --More--";
139 void pline(string msg) {
140 msg_queue.insertBack(msg);
144 // true => keep printing, false => stop sending messages
145 private bool actual_pline(string[] messagewords) {
146 if (messagewords.length == 1) {
147 clearmsgbar();
148 graphics0.printext(messagewords[0], 0, 0);
149 graphics0.refresh();
150 } else {
151 loop: foreach (lineindex; 0 .. messagewords.length) {
152 graphics0.printext(messagewords[lineindex], 0, 0);
153 graphics0.refresh();
154 if (lineindex < messagewords.length-1) {
155 dchar c;
156 do {
157 c = graphics0.getch();
158 if (c == '\033') {
159 return false;
161 } while ((c != '\n') && (c != ' '));
163 clearmsgbar();
168 return true;
171 private void fire_plines() {
172 if (msg_queue.empty) {
173 return;
176 pure string[] chopupmsg(string msgtext, size_t width, bool have_more /* there's another message after*/) {
177 string[] words = msgtext.split;
179 size_t tmp;
180 string[] ret;
181 string str;
182 immutable size_t maxlen = width - more.length;
184 while (words.length) {
185 str ~= words[0];
186 words = words[1 .. $];
188 if (!words.length) {
189 ret ~= str;
190 break;
193 if (str.length + words[0].length > maxlen) {
194 str ~= more;
195 ret ~= str;
196 str = null;
197 } else {
198 str ~= ' ';
202 if (have_more) {
203 ret[$-1] ~= more;
206 return ret;
209 string[][] messages;
211 string[] last_msg = chopupmsg(msg_queue.back, maxx(), false);
212 msg_queue.removeBack();
213 foreach (i; msg_queue) {
214 messages ~= chopupmsg(i, maxx(), true);
216 messages ~= last_msg;
217 msg_queue.clear();
219 oloop: foreach (msg; messages[0 .. $-1]) {
220 if (!actual_pline(msg)) {
221 goto lastmsg;
224 while (true) {
225 dchar c = graphics0.getch();
226 if ((c == '\n') || (c == ' ')) {
227 continue oloop;
228 } else if (c == '\033') {
229 goto lastmsg;
233 if (!actual_pline(messages[$-1])) {
234 goto lastmsg;
236 return;
238 lastmsg:
239 clearmsgbar();
240 graphics0.printext(messages[$-1][$-1], 0, 0);
241 graphics0.refresh();
244 int maxx() { return graphics0.maxx(); }
245 int maxy() { return graphics0.maxy(); }
246 void refresh(uint uy, uint ux, list!Being mons, list!Item items, Map map) {
247 auto lens = focuscamera(maxx(), maxy(), uy, ux);
249 drawmap(lens, map, mons.front.lightmap);
250 drawitems(lens, items, mons.front.lightmap);
251 drawmons(lens, mons, map);
253 graphics0.refresh();
255 fire_plines(); // calls its own refresh
258 dchar getch() { return graphics0.getch; }