add colour blending, separate fov distance from light distance
[SmugglerRL.git] / src / game.d
blob5e28ea533256d29913591e75c5a9f5ed87e48f7f
1 import BearLibTerminal;
3 import stdlib;
5 import colour;
6 import constants;
7 import myfov;
8 import glyph;
9 import graphix;
10 import graphix0;
11 import item;
12 import light;
13 import logging;
14 import mapgen;
15 import map;
16 import rng;
17 import tile;
18 import util;
22 abstract class Being {
23 int i;
24 private bool gender;
25 ubyte xlvl;
26 int xp, hp = 30, maxhp, mp, maxmp;
27 Vector2n loc;
28 RGBColour fgcolour, bgcolour;
29 Glyph glyph;
30 bool light_source;
31 int fov_dist, light_dist;
32 Item[] inventory;
33 Lightmap lightmap;
34 RGBColour lightclr;
36 private struct _attr { bool reverse, italic, bold, underline; }
37 _attr attrs;
39 pragma(inline, true) {
40 pure void inchp(int amount=1) {
41 if ((amount + hp) > maxhp) {
42 hp = maxhp;
43 } else {
44 hp += amount;
47 pure bool ismale() {
48 return gender;
50 pure bool isfemale() {
51 return !gender;
55 Action getaction();
57 this() {
58 lightmap = new Lightmap(&loc, fov_dist*2 + 1);
62 class Mon: Being {
63 override Action getaction() {
64 return rnd(Action.MoveNorth, Action.MoveSouthRight);
66 this() {
67 fgcolour = RGBColour(0x00ff00);
68 bgcolour = RGBColour(0xff00ff);
69 attrs.reverse = true;
70 this.glyph = Glyph.r;
72 this.light_source = true;
73 this.fov_dist = 4;
74 this.light_dist = 4;
76 this.lightclr = RGBColour(0x008800);
78 super();
82 class You: Being {
83 Graphics1 graphics;
84 this(Graphics1 g) {
85 fgcolour = RGBColour(0xffffff);
86 bgcolour = RGBColour(0x000000);
87 attrs.reverse = true;
89 this.graphics = g;
90 this.glyph = Glyph.at;
92 this.light_source = true;
93 this.fov_dist = 24;
94 this.light_dist = 12;
96 //this.lightclr = RGBColour(0xee9933);
97 this.lightclr = RGBColour(0xff0000);
99 super();
101 override Action getaction() {
102 static immutable auto vikeys = set!dchar('h', 'j', 'k', 'l', 'y', 'u', 'b', 'n', 'q', '.', 'w');
104 Action tmp;
105 dchar c;
107 while ((c = graphics.getch()) !in vikeys) {}
109 switch (c) {
110 case 'h':
111 tmp = Action.MoveLeft;
112 break;
113 case 'l':
114 tmp = Action.MoveRight;
115 break;
116 case 'j':
117 tmp = Action.MoveSouth;
118 break;
119 case 'k':
120 tmp = Action.MoveNorth;
121 break;
122 case 'y':
123 tmp = Action.MoveNorthLeft;
124 break;
125 case 'u':
126 tmp = Action.MoveNorthRight;
127 break;
128 case 'b':
129 tmp = Action.MoveSouthLeft;
130 break;
131 case 'n':
132 tmp = Action.MoveSouthRight;
133 break;
135 /+ TODO
136 case 'H':
137 tmp = Action.AttackLeft;
138 break;
139 case 'L':
140 tmp = Action.AttackRight;
141 break;
142 case 'J':
143 tmp = Action.AttackSouth;
144 break;
145 case 'K':
146 tmp = Action.AttackNorth;
147 break;
148 case 'Y':
149 tmp = Action.AttackNorthLeft;
150 break;
151 case 'U':
152 tmp = Action.AttackNorthRight;
153 break;
154 case 'B':
155 tmp = Action.AttackSouthLeft;
156 break;
157 case 'N':
158 tmp = Action.AttackSouthRight;
159 break;
162 case 'q':
163 tmp = Action.Quit;
164 break;
165 case '.':
166 tmp = Action.Wait;
167 break;
168 case 'w': // (w)here am I?
169 tmp = Action.Printloc;
170 break;
171 case ',':
172 tmp = Action.Pickup;
173 break;
174 case 'i':
175 tmp = Action.Showinv;
176 break;
177 default: assert(0);
179 return tmp;
187 class Game {
188 Being u;
189 list!Being mons;
190 list!Item items;
191 Graphics1 graphics;
192 Map map;
194 this(string[] args) {
195 graphics = new Default_chargfx!BLT0("SmugglerRL!");
197 this.map = genmap(MapType.caves);
199 mons = [u = new You(graphics)];
200 foreach (_; 1 .. 1000) {
201 mons ~= new Mon();
204 parseargs(args);
206 // If we didn't have this, then if (0, 0) was walkable, then you would automatically get placed there
207 foreach (ref m; mons) {
208 do {
209 m.loc.x = rn1(map_x+1);
210 m.loc.y = rn1(map_y+1);
211 } while ((!map[m.loc].walkable) || !mon_at(m.loc));
215 foreach (_; 1 .. 100) {
216 items ~= Item(choice(all_items));
218 foreach (ref i; items) {
219 do {
220 i.loc.x = rn1(map_x+1);
221 i.loc.y = rn1(map_y+1);
222 } while (!map[i.loc].walkable);
225 u.glyph = Glyph.at;
228 // TODO
229 void parseargs(string[] args) {
230 if (args.length == 2) {
231 if (args[1] == "dumpmap") {
232 dumpmap();
233 exit(0);
234 } else if (args[1] == "dumphtmlmap") {
235 dumphtmlmap();
236 exit(0);
241 // Name credit to lrogue
242 ref Being mon_at(uint y, uint x) {
243 foreach (ref tmpmon; mons) {
244 if ((tmpmon.loc.y == y) && (tmpmon.loc.x == x)) {
245 return tmpmon;
248 // Evil hackery so we can return null
249 static Being tmp = null;
250 return tmp;
252 ref Being mon_at(Vector2n loc) {
253 return mon_at(loc.y, loc.x);
257 void dumpmap() {
258 import std.stdio;
259 foreach (row; map.get_tiles) {
260 foreach (tile; row) {
261 write(cast(dchar)tile.glyph);
263 writeln();
266 void dumphtmlmap() {
267 import std.stdio;
268 writeln(`
269 <!doctype html>
270 <html lang="rl"> <!-- RogueLike! -->
271 <head>
272 <meta charset="utf-8">
273 <title>SmuglerRL HTML dump!</title>
274 </head>
275 <body fgcolor="#ffffff" bgcolor="#000000">
276 <div style="font-family: monospace">`);
278 bool clr_changed, bgclr_changed;
279 RGBColour fg, last_fg, bg, last_bg;
280 foreach (row; map.get_tiles) {
281 foreach (tile; row) {
282 fg = tile.fgcolour;
283 bg = tile.bgcolour;
285 if (fg != last_fg) {
286 clr_changed = true;
287 } else {
288 clr_changed = false;
290 if (bg != last_bg) {
291 bgclr_changed = true;
292 } else {
293 bgclr_changed = false;
296 if (clr_changed) write(`</font>`);
297 if (bgclr_changed) write(`</font>`);
298 if (clr_changed) {
299 with (fg) writef(`<font color="#%02x%02x%02x">`, r, g, b);
300 last_fg = fg;
302 if (bgclr_changed) {
303 with (bg) writef(`<font bgcolor="#%02x%02x%02x">`, r, g, b);
304 last_bg = bg;
306 write(cast(dchar)tile.glyph);
308 writeln("<br>");
310 writeln(`</div>
311 </body>
312 </html>`);
315 ~this() {
316 terminal.close();
318 pragma(inline, true) void refresh() {
319 graphics.refresh(u.loc.y, u.loc.x, mons, map);
321 pragma(inline, true) void pline(T...)(T args) {
322 graphics.pline(args);
325 void mainloop() {
326 bool moving;
327 int deltax, deltay;
328 Action act;
329 foreach (ref mon; mons) {
330 do_fov(map, mon.lightmap, mon.loc.x, mon.loc.y, mon.fov_dist * mon.fov_dist, mon.light_dist * mon.light_dist);
332 do_colour(map, mons);
334 refresh();
335 void handlemove(int yshift, int xshift, ref Being b) {
336 if (map[b.loc.y+yshift, b.loc.x+xshift].walkable && !mon_at(b.loc.y+yshift, b.loc.x+xshift)) {
337 b.loc.y = b.loc.y + yshift;
338 b.loc.x = b.loc.x + xshift;
339 } else {
340 // only when *you* walk into a wall
341 if (cast(You)b) pline("there is a thingy there!");
344 outerloop: while (true) { foreach (ref Being mon; mons) {
345 act = mon.getaction();
346 // If we're moving, then move!
347 if ((Action.MoveNorth <= act) && (act <= Action.MoveSouthRight)) {
348 moving = true;
349 } else {
350 moving = false;
352 switch(act) {
353 case Action.MoveNorth:
354 deltay = -1;
355 break;
356 case Action.MoveSouth:
357 // ayyy
358 deltay = 1;
359 break;
360 case Action.MoveLeft:
361 deltax = -1;
362 break;
363 case Action.MoveRight:
364 deltax = 1;
365 break;
366 case Action.MoveNorthLeft:
367 deltay = deltax = -1;
368 break;
369 case Action.MoveNorthRight:
370 deltay = -1;
371 deltax = 1;
372 break;
373 case Action.MoveSouthLeft:
374 deltay = 1;
375 deltax = -1;
376 break;
377 case Action.MoveSouthRight:
378 deltay = deltax = 1;
379 break;
380 case Action.Wait:
381 break;
382 case Action.Quit:
383 break outerloop;
384 case Action.Printloc:
385 pline("(%s, %s)", mon.loc.x, mon.loc.y);
386 break;
387 default: assert(0);
391 if (moving) {
392 handlemove(deltay, deltax, mon);
395 deltay = deltax = 0;
397 foreach (ref mon; mons) {
398 do_fov(map, mon.lightmap, mon.loc.x, mon.loc.y, mon.fov_dist * mon.fov_dist, mon.light_dist * mon.light_dist);
400 do_colour(map, mons);
402 refresh();
404 graphics.close();