preliminary fov. The algorithm doesn't work, though
[SmugglerRL.git] / src / game.d
blob455a24faab9cd40be1c8afe10087ab3f07bd48d7
1 private import std.algorithm: canFind;
2 private import tile;
4 import constants;
5 import graphix;
6 import mapgen;
7 import util;
8 import BearLibTerminal;
9 import fov: do_fov;
13 abstract class Being {
14 int i;
15 private bool gender;
16 ubyte xlvl;
17 int xp, hp = 30, maxhp, mp, maxmp;
18 uint x, y;
19 RGBColour fgcolour, bgcolour;
20 Glyph glyph;
22 private struct _attr { bool reverse, italic, bold, underline; }
23 _attr attrs;
25 pragma(inline, true) {
26 pure void inchp(int amount=1) {
27 if ((amount + hp) > maxhp) {
28 hp = maxhp;
29 } else {
30 hp += amount;
33 pure bool ismale() {
34 return gender;
36 pure bool isfemale() {
37 return !gender;
41 Action getaction();
44 class Mon: Being {
45 override Action getaction() {
46 import std.random: uniform;
47 return uniform(Action.MoveNorth, Action.MoveSouthRight);
49 this() {
50 fgcolour = RGBColour(0xff00ff);
51 bgcolour = RGBColour(0x00ff00);
52 attrs.reverse = true;
53 this.glyph = Glyph.r;
57 class You: Being {
58 Graphicshandler graphics;
59 this(Graphicshandler g) {
60 fgcolour = RGBColour(0xffffff);
61 fgcolour = RGBColour(0x000000);
62 attrs.reverse = true;
63 this.graphics = g;
64 this.glyph = Glyph.at;
66 override Action getaction() {
67 import std.container: RedBlackTree; // Like a python set; i.e. faster lookups but unordered
68 immutable RedBlackTree!int vikeys = new RedBlackTree!int('h', 'j', 'k', 'l', 'y', 'u', 'b', 'n', 'q', '.');
70 Action tmp;
71 int c;
73 while (!((c = graphics.getch()) in vikeys)) {}
75 switch (c) {
76 case 'h':
77 tmp = Action.MoveLeft;
78 break;
79 case 'l':
80 tmp = Action.MoveRight;
81 break;
82 case 'j':
83 tmp = Action.MoveSouth;
84 break;
85 case 'k':
86 tmp = Action.MoveNorth;
87 break;
88 case 'y':
89 tmp = Action.MoveNorthLeft;
90 break;
91 case 'u':
92 tmp = Action.MoveNorthRight;
93 break;
94 case 'b':
95 tmp = Action.MoveSouthLeft;
96 break;
97 case 'n':
98 tmp = Action.MoveSouthRight;
99 break;
100 case 'q':
101 tmp = Action.Quit;
102 break;
103 case '.':
104 tmp = Action.Wait;
105 break;
106 default: assert(0);
108 return tmp;
113 struct Tile {
114 Glyph glyph;
115 int x, y, dijkstra = 1000;
116 bool walkable, visible = false;
117 RGBColour fgcolour, bgcolour;
118 private struct _attr { bool reverse, italic, bold, underline; }
119 _attr attrs;
121 void setdefglyph() {
122 if (walkable) {
123 glyph = Glyph.middot;
124 } else {
125 import std.random: uniform;
126 glyph = Glyph.hash;
127 fgcolour = RGBColour(0x663300);
128 bgcolour = RGBColour(0x000000);
134 Tile[][] mkdijkstra(Tile[][] map) {
135 auto newmap = map.dup;
136 foreach (line; map) {
137 foreach (tile; line) {
138 if (tile.walkable) {
139 newmap[tile.y][tile.x] = dijkstrapass(tile, newmap);
144 if (newmap != map) {
145 return mkdijkstra(newmap);
146 } else {
147 return newmap;
151 Tile dijkstrapass(Tile t, ref Tile[][] map) {
152 import std.algorithm.sorting: sort;
153 Tile[] neighbors = [map[t.y-1][t.x], map[t.y][t.x-1], map[t.y+1][t.x], map[t.y][t.x+1]];
155 neighbors.sort!((a, b) => a.dijkstra < b.dijkstra);
157 // The 0th one is the smallest one
158 if (t.dijkstra >= 2+neighbors[0].dijkstra) {
159 t.dijkstra = neighbors[0].dijkstra+1;
162 assert (neighbors[0].dijkstra <= neighbors[1].dijkstra);
164 return t;
168 class Game {
169 int counter;
170 Being u;
171 Being[] mons;
172 Graphicshandler graphics;
173 int width, height;
174 // it's weird, because although it's declared as [x][y], you assign to it with [y][x]
175 Tile[][] map;
176 // I mean, it makes sense, but it's weird
178 this(string[] args) {
179 import std.stdio: write, writeln, stdout;
180 graphics = new BLThandler(this);
182 write("Allocating map..."); stdout.flush();
183 // workaround because d doesn't want us to have static arrays larger than
184 // 16 MB
185 this.map.length = map_y+2;
187 foreach (ref row; map) {
188 row.length = map_x+2;
190 writeln("done!");
192 write("Generating map..."); stdout.flush();
193 this.genmap(MapType.caves);
194 writeln("done!");
196 write("Allocating monsters..."); stdout.flush();
197 u = new You(graphics);
198 mons = [u];
199 foreach (_; 1..1000) {
200 mons ~= new Mon();
202 writeln("done!");
204 write("Performing operations on map..."); stdout.flush();
205 // Fill with " "
206 foreach (int tmpy; 0..map_y+1) {
207 foreach (int tmpx; 0..map_x+1) {
208 map[tmpy][tmpx].y = tmpy+1;
209 map[tmpy][tmpx].x = tmpx+1;
211 // map[tmpy][tmpx].walkable = true;
213 if (map[tmpy][tmpx].walkable) {
214 map[tmpy][tmpx].fgcolour = RGBColour(0x4c4c4c);
215 } else {
216 map[tmpy][tmpx].fgcolour = RGBColour(0xeeeeee);
218 map[tmpy][tmpx].bgcolour = RGBColour(0x000000);
220 if ((tmpy == 0) || (tmpx == 0) || (tmpy == map_y+2) || (tmpx == map_x+2))
221 map[tmpy][tmpx].walkable = false;
222 map[tmpy][tmpx].setdefglyph();
226 writeln("done!");
228 write("Placing monsters..."); stdout.flush();
229 // If we didn't have this, then if (0, 0) was walkable, then you would automatically get placed there
230 foreach (ref m; mons) {
231 do {
232 m.x = rnd(1, map_x+1);
233 m.y = rnd(1, map_y+1);
234 } while ((!map[m.y][m.x].walkable) || !mon_at(m.y, m.x));
236 writeln("done!");
238 u.glyph = Glyph.at;
239 map[u.y][u.x].dijkstra = 0;
241 parseargs(args);
243 write("Generating dijkstra map...");
244 //map = mkdijkstra(map);
245 writeln("done!");
249 // TODO
250 void parseargs(string[] args) {
251 if (args.length == 2) {
252 if (args[1] == "dumpmap") {
253 dumpmap();
254 exit(0);
259 uint mon_at_index(uint y, uint x) {
260 foreach (uint index; 0..cast(uint)mons.length-1) {
261 if ((mons[index].y == y) && (mons[index].x == x)) {
262 return index;
265 return -1;
268 // Name credit to lrogue
269 ref Being mon_at(uint y, uint x) {
270 foreach (ref tmpmon; mons) {
271 if ((tmpmon.y == y) && (tmpmon.x == x)) {
272 return tmpmon;
275 // Evil hackery so we can return null
276 static Being tmp = null;
277 return tmp;
278 // return *(cast(Being*)null);
282 void dumpmap() {
283 import std.stdio;
284 import std.conv: to;
285 foreach (row; map) {
286 foreach (tile; row) {
287 write(cast(string)tile.glyph);
289 writeln();
293 ~this() {
294 terminal.close();
296 pragma(inline, true) void refresh() {
297 graphics.refresh();
299 pragma(inline, true) void pline(T...)(string s, T args) {
300 graphics.pline(s, args);
303 void mainloop() {
304 bool moving;
305 int deltax, deltay;
306 Action act;
308 refresh();
309 void handlemove(int yshift, int xshift, ref Being b) {
310 if (map[b.y+yshift][b.x+xshift].walkable && !mon_at(b.y+yshift, b.x+xshift)) {
311 map[b.y][b.x].setdefglyph();
312 b.y += yshift;
313 b.x += xshift;
314 // pline("another message, of exceeding length. You should eat it, if you feel like, but make sure it's still long enough. Ugh, it has to be *LONGER*?? FINE>>>");
315 } else {
316 // only when *you* walk into a wall
317 if (cast(You)b) pline("there is a thingy there!");
320 outerloop: while (true) { refresh(); foreach (ref Being mon; mons) {
321 act = mon.getaction();
322 // If we're moving, then move!
323 if ((Action.MoveNorth <= act) && (act <= Action.MoveSouthRight)) {
324 moving = true;
325 } else {
326 moving = false;
328 switch(act) {
329 case Action.MoveNorth:
330 deltay = -1;
331 break;
332 case Action.MoveSouth:
333 // ayyy
334 deltay = 1;
335 break;
336 case Action.MoveLeft:
337 deltax = -1;
338 break;
339 case Action.MoveRight:
340 deltax = 1;
341 break;
342 case Action.MoveNorthLeft:
343 deltay = deltax = -1;
344 break;
345 case Action.MoveNorthRight:
346 deltay = -1;
347 deltax = 1;
348 break;
349 case Action.MoveSouthLeft:
350 deltay = 1;
351 deltax = -1;
352 break;
353 case Action.MoveSouthRight:
354 deltay = deltax = 1;
355 break;
356 case Action.Wait:
357 break;
358 case Action.Quit:
359 break outerloop;
360 default: assert(0);
364 if (moving) {
365 handlemove(deltay, deltax, mon);
367 if (cast(You)mon) { refresh(); do_fov(this.map, u.x, u.y, 100); }
369 deltay = deltax = 0;
370 } refresh(); }