Add skeleton structure for items
[SmugglerRL.git] / src / graphix.d
blobfdea87797eabae61df87756421c1d5e58dfceb22
1 import constants;
2 import util;
3 import game;
4 import graphix0;
6 interface Graphics1 {
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 // then it was
11 //pragma(inline, true) pure const static inout shared ref nothrow override @property @nogc @safe public final void
12 // 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 :<<
13 pragma(inline, true) inout ref @property @trusted public final void pline(T...)(T args) {
14 import std.conv: to;
15 import std.string: format;
16 if (args.length == 1) {
17 this.pline(to!string(args[0]));
18 } else {
19 this.pline(format(args));
22 void pline(string s);
23 int maxx();
24 int maxy();
25 char getch();
26 void close();
29 private struct startendspec { int startx, starty, endx, endy, cursx, cursy; }
31 // Find the x-y coordinates to start drawing from in the map so the camera is centred on the player
32 // FUN FACT while coding this, I had no idea what it did or how
33 // it worked. I still have no idea how it works. But it does!
34 private @nogc @safe final startendspec focuscamera(int width, int height, int x, int y) {
35 import std.math: round;
36 startendspec tmp;
37 int startx, starty, endx, endy;
38 int offsetx, offsety; // offsets are distance from top edge, left edge to centre
39 int cursx, cursy;
40 height -= 1;
42 offsetx = cast(int)round(width / 2.0);
43 offsety = cast(int)round(height / 2.0);
45 // Don't keep the map centred when we're near an edge,
46 // actually move the @ closer to the edge
47 if (x <= offsetx) {
48 startx = 1;
49 cursx = x - 1;
50 } else {
51 startx = x - offsetx;
52 cursx = offsetx;
54 if (y <= offsety) {
55 starty = 1;
56 cursy = y - 1;
57 } else {
58 starty = y - offsety;
59 cursy = offsety;
62 endx = startx + width;
63 endy = starty + height;
65 // Ditto
66 if (endx >= map_x) {
67 endx = map_x+1;
68 startx = endx-width;
69 cursx = width - (map_x - x) - 1;
71 if (endy >= map_y) {
72 endy = map_y+1;
73 starty = endy-height;
74 cursy = height - (map_y - y) - 1;
77 tmp.startx = startx;
78 tmp.starty = starty;
79 tmp.endx = endx;
80 tmp.endy = endy;
81 tmp.cursx = cursx;
82 tmp.cursy = cursy;
84 return tmp;
87 class Default_chargfx(G0type: CharGfx0): Graphics1 {
88 CharGfx0 graphics0;
89 Game game;
91 this(Game g, string title="") {
92 this.game = g;
93 this.graphics0 = new G0type();
95 graphics0.settitle(title);
97 void close() { graphics0.close(); }
99 void drawmons() {
100 auto lens = focuscamera(maxx(), maxy(), game.u.x, game.u.y);
101 int tmpx = 0, tmpy = 0;
102 Being mon;
103 uint fg, bg;
105 foreach (uint i; lens.starty..lens.endy) {
106 tmpy++;
107 foreach (uint j; lens.startx..lens.endx) {
108 tmpx++;
109 if ((mon = game.mon_at(i, j)) is null) {
110 continue;
112 if (!game.map[i][j].visible)
113 continue;
115 /* x and y both start at 1, so we want to get them to 0
116 * in order to align them with the edges of the terminal.
117 * But it's okay to "add" 1 to y, because we want to leave
118 * an extra line up to for messages.
120 with (mon.attrs)
121 graphics0.mvaddch(mon.glyph, tmpy, tmpx-1, mon.fgcolour, mon.bgcolour, bold, italic, underline, reverse);
123 tmpx = 0;
126 void drawmap() {
127 int tmpx = 0, tmpy = 0;
129 auto lens = focuscamera(maxx(), maxy(), game.u.x, game.u.y);
131 foreach (uint i; lens.starty..lens.endy) {
132 tmpy++;
133 foreach (uint j; lens.startx..lens.endx) {
134 tmpx++;
136 /* x and y both start at 1, so we want to get them to 0
137 * in order to align them with the edges of the terminal.
138 * But it's okay to "add" 1 to y, because we want to leave
139 * an extra line up to for messages.
141 // import std.conv: to;
142 // curses.mvprint(tmpy, tmpx-1, to!string(to!string(game.map[i][j].dijkstra)[0]));
143 if (game.map[i][j].blocks_light) {
144 with (game.map[i][j].attrs)
145 graphics0.mvaddch(game.map[i][j].glyph, tmpy, tmpx-1, game.map[i][j].visible ? game.map[i][j].fgcolour.lighten(20) : game.map[i][j].fgcolour.darken(20), game.map[i][j].bgcolour, bold, italic, underline, reverse);
146 } else {
147 with (game.map[i][j].attrs)
148 graphics0.mvaddch(game.map[i][j].glyph, tmpy, tmpx-1, game.map[i][j].visible ? game.map[i][j].fgcolour.lighten(20) : game.map[i][j].fgcolour.darken(20), game.map[i][j].bgcolour, bold, italic, underline, reverse); //game.map[i][j].glyph);
151 tmpx = 0;
155 void pline(string msg) {
156 if (msg.length == 0) {
157 return;
160 // TODO chop stuff up based on words, not character counts
161 pure string[] chopupmsg(string msgtext, int x) {
162 int tmp = 0;
163 string[] buf;
164 immutable uint maxlen = x - cast(int)" --More--".length;
165 while (msgtext.length > x) {
166 buf ~= (msgtext[tmp..tmp+maxlen] ~ " --More--");
167 tmp += maxlen;
168 msgtext = msgtext[tmp..$];
170 buf ~= msgtext;
171 return buf;
173 void clearmsgbar() {
174 graphics0.printext(fillstr(maxx()), 0, 0);
177 if (msg.length <= maxx()) {
178 clearmsgbar();
179 graphics0.printext(msg, 0, 0);
180 graphics0.refresh();
181 } else {
182 string[] buffer = chopupmsg(msg, /*cast(int)*/maxx());
184 loop: foreach (lineindex; 0..buffer.length) {
185 graphics0.printext(buffer[lineindex], 0, 0);
186 graphics0.refresh();
187 int c;
188 c = graphics0.getch();
189 if (c == '\033') {
190 clearmsgbar();
191 graphics0.printext(buffer[$-1], 0, 0);
192 break;
194 if (lineindex < buffer.length-1) {
195 while ((c != '\n') && (c != ' ')) {
196 if (c == '\033') {
197 clearmsgbar();
198 graphics0.printext(buffer[$-1], 0, 0);
199 break loop;
201 c = graphics0.getch;
204 clearmsgbar();
209 int maxx() { return graphics0.maxx(); }
210 int maxy() { return graphics0.maxy(); }
211 void refresh() {
212 drawmap();
213 drawmons();
214 graphics0.refresh();
217 char getch() { return graphics0.getch; }