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
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]));
22 this.pline(format(args
));
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
;
40 int startx
, starty
, endx
, endy
;
41 int offsetx
, offsety
; // offsets are distance from top edge, left edge to centre
45 offsetx
= cast(int)round(width
/ 2.0);
46 offsety
= cast(int)round(height
/ 2.0);
53 endx
= startx
+ width
;
54 endy
= starty
+ height
;
66 class Default_chargfx(G0type
: CharGfx0
): Graphics1
{
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
;
81 if (!umap
[mon
.loc
].visible
) {
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
))) {
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.
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
) {
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
))) {
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
);
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) {
148 graphics0
.printext(messagewords
[0], 0, 0);
151 loop: foreach (lineindex
; 0 .. messagewords
.length
) {
152 graphics0
.printext(messagewords
[lineindex
], 0, 0);
154 if (lineindex
< messagewords
.length
-1) {
157 c
= graphics0
.getch();
161 } while ((c
!= '\n') && (c
!= ' '));
171 private void fire_plines() {
172 if (msg_queue
.empty
) {
176 pure string
[] chopupmsg(string msgtext
, size_t width
, bool have_more
/* there's another message after*/) {
177 string
[] words
= msgtext
.split
;
182 immutable size_t maxlen
= width
- more
.length
;
184 while (words
.length
) {
186 words
= words
[1 .. $];
193 if (str.length
+ words
[0].length
> maxlen
) {
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
;
219 oloop
: foreach (msg
; messages
[0 .. $-1]) {
220 if (!actual_pline(msg
)) {
225 dchar c
= graphics0
.getch();
226 if ((c
== '\n') ||
(c
== ' ')) {
228 } else if (c
== '\033') {
233 if (!actual_pline(messages
[$-1])) {
240 graphics0
.printext(messages
[$-1][$-1], 0, 0);
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
);
255 fire_plines(); // calls its own refresh
258 dchar getch() { return graphics0
.getch
; }