History

clean up and move level generation to levelgen.c

mischief committed 11 years ago

Commit 7cf66ce1 parent 770e6ae7 files · +549 −366Browse files

Unified · Split

7 files
  1. dat.h +14 −0
  2. hack9.c +3 −8
  3. level.c +32 −357
  4. levelgen.c +489 −0
  5. mk.common +1 −0
  6. monst.c +3 −0
  7. monster.ndb +7 −1
dat.h+14−0
@@ -202,6 +202,10 @@ struct Spawn · 201 unmodified lines
202 char what[SZMONNAME];202 char what[SZMONNAME];
203 /* frequency of spawn, modulo turns */203 /* frequency of spawn, modulo turns */
204 int freq;204 int freq;
205 /* turn counter */
206 int turn;
207 /* spawn baddies when enemy is around? */
208 int istrap;
205};209};
206 210
207struct Tile211struct Tile
@@ -260,12 +264,22 @@ struct Level · 52 unmodified lines
260/* level.c */264/* level.c */
261Level *genlevel(int width, int height, int type);265Level *genlevel(int width, int height, int type);
262void freelevel(Level *l);266void freelevel(Level *l);
267void addspawn(Level *l, Point p, char *what, int freq, int istrap);
263Tile *tileat(Level *l, Point p);268Tile *tileat(Level *l, Point p);
264#define flagat(l, p) (*(l->flags+(p.y*l->width)+p.x))269#define flagat(l, p) (*(l->flags+(p.y*l->width)+p.x))
265#define hasflagat(l, p, F) (*(l->flags+(p.y*l->width)+p.x) & (F))270#define hasflagat(l, p, F) (*(l->flags+(p.y*l->width)+p.x) & (F))
266#define setflagat(l, p, F) (*(l->flags+(p.y*l->width)+p.x) |= (F))271#define setflagat(l, p, F) (*(l->flags+(p.y*l->width)+p.x) |= (F))
267#define clrflagat(l, p, F) (*(l->flags+(p.y*l->width)+p.x) &= ~(F))272#define clrflagat(l, p, F) (*(l->flags+(p.y*l->width)+p.x) &= ~(F))
268 273
274/* levelgen.c */
275Level *mklevel(int width, int height, int floor);
276void several(Level *l, Point *p, int count, char *type, int r);
277int mkldebug(Level *l);
278int mklforest(Level *l);
279int mklgraveyard(Level *l);
280int mklvolcano(Level *l);
281int mklcastle(Level *l);
282
269struct Camera283struct Camera
270{284{
271 Point pos;285 Point pos;
hack9.c+3−8
@@ -67,7 +67,7 @@ movemons(void) · 66 unmodified lines
67 l = player->l;67 l = player->l;
68 68
69 /* run ai on level, for spawns etc */69 /* run ai on level, for spawns etc */
70 if(l->ai->exec != nil)70 if(l->ai != nil && l->ai->exec != nil)
71 l->ai->exec(l->ai);71 l->ai->exec(l->ai);
72 72
73 for(p.x = 0; p.x < l->width; p.x++){73 for(p.x = 0; p.x < l->width; p.x++){
@@ -137,13 +137,8 @@ threadmain(int argc, char *argv[]) · 63 unmodified lines
137 }137 }
138 138
139 /* initial level */139 /* initial level */
140 if(debug > 0){140 if((level = genlevel(nrand(LSIZE)+LSIZE, nrand(LSIZE)+LSIZE, nrand(3)+1)) == nil)
141 if((level = genlevel(11, 11, 0)) == nil)141 sysfatal("genlevel: %r");
142 sysfatal("genlevel: %r");
143 } else {
144 if((level = genlevel(nrand(LSIZE)+LSIZE, nrand(LSIZE)+LSIZE, nrand(3)+1)) == nil)
145 sysfatal("genlevel: %r");
146 }
147 142
148 /* the player */143 /* the player */
149 player = mbyname("wizard");144 player = mbyname("wizard");
level.c+32−357
@@ -6,172 +6,8 @@ · 5 unmodified lines
6#include "dat.h"6#include "dat.h"
7#include "alg.h"7#include "alg.h"
8 8
9/* clear removes all monsters/features near p. */9void
10static void10addspawn(Level *l, Point p, char *what, int freq, int istrap)
11clear(Level *l, Point p, int dist)
12{
13 Rectangle r;
14 Point p2;
15 Tile *t;
16
17 r = Rect(p.x-dist, p.y-dist, p.x+dist, p.y+dist);
18 rectclip(&r, l->r);
19
20 for(p2.x = r.min.x; p2.x <= r.max.x; p2.x++){
21 for(p2.y = r.min.y; p2.y <= r.max.y; p2.y++){
22 if(eqpt(p, p2))
23 continue;
24 t = tileat(l, p2);
25 t->unit = t->feat = 0;
26 if(t->monst){
27 mfree(t->monst);
28 t->monst = nil;
29 }
30 flagat(l, p2) = 0;
31 }
32 }
33}
34
35/* more sbias makes it straighter. */
36static void
37drunk1(Level *l, Point p, uint count, uint sbias)
38{
39 int cur, last;
40 Point next;
41 Rectangle clipr;
42 Tile *t;
43
44 clipr = insetrect(l->r, 1);
45
46 last = nrand(NCARDINAL);
47 while(count > 0){
48 if(nrand(sbias+1) == 0)
49 cur = nrand(NCARDINAL);
50 else
51 cur = last;
52 next = addpt(p, cardinals[cur]);
53 if(!ptinrect(next, clipr))
54 continue;
55 last = cur;
56 p = next;
57 if(!eqpt(p, l->up) && !eqpt(p, l->down) && hasflagat(l, p, Fblocked|Fhasfeature)){
58 t = tileat(l, p);
59 t->feat = 0;
60 flagat(l, p) = 0;
61 count--;
62 }
63 }
64}
65
66/*
67 * drunken performs the drunkard's walk. as s1 and
68 * s2 increase, the paths become more biased toward
69 * 'straight'. it loops until the downstairs is
70 * reachable from the upstairs.
71 */
72static int
73drunken(Level *l, int type, int howmuch, int s1, int s2)
74{
75 int cnt, npath, redos;
76 Point p, *path;
77 Tile *t;
78
79 cnt = ((l->width * l->height) / howmuch); //* 2;
80 redos = 0;
81
82redo:
83 if(redos++ > 10)
84 return 0;
85 /* fill */
86 for(p.x = 0; p.x < l->width; p.x++){
87 for(p.y = 0; p.y < l->height; p.y++){
88 if(!hasflagat(l, p, Fblocked|Fhasfeature)){
89 t = tileat(l, p);
90 t->feat = type;
91 flagat(l, p) = (Fblocked|Fhasfeature);
92 }
93 }
94 }
95
96 drunk1(l, l->up, cnt/2, s1);
97 drunk1(l, l->down, cnt/2, s2);
98
99 /* check reachability */
100 npath = pathfind(l, l->up, l->down, &path, Fblocked);
101 if(npath < 0)
102 goto redo;
103 else
104 free(path);
105
106 return cnt * 2;
107}
108
109/* create a monster in the idle state */
110static Monster*
111mkmons(Level *l, Point p, char *type)
112{
113 Monster *m;
114 m = mbyname(type);
115 if(m == nil)
116 sysfatal("monstbyname: %r");
117 m->l = l;
118 m->pt = p;
119 /* setup ai state */
120 idle(m);
121 return m;
122}
123
124/* genmonsters spawns count of type monsters at random places on l. */
125static void
126genmonsters(Level *l, char *type, int count)
127{
128 int i;
129 Point p;
130 Tile *t;
131
132 for(i = 0; i < count; i++){
133 do{
134 p = (Point){nrand(l->width), nrand(l->height)};
135 }while(hasflagat(l, p, Fhasmonster|Fhasfeature|Fblocked));
136
137 t = tileat(l, p);
138 t->monst = mkmons(l, p, type);
139 t->unit = t->monst->md->tile;
140 setflagat(l, p, Fhasmonster);
141 }
142}
143
144/*
145 * several is a recursive function, that spawns monsters
146 * centered at p[0] expaning outward according to a
147 * von neumann neighborhood with radius r. for example:
148 * several(l, p, 1, TGHOST, 2);
149 * spawns 13 ghosts centered at p on l.
150 */
151static void
152several(Level *l, Point *p, int count, char *type, int r)
153{
154 int i, j, n;
155 Point *neigh;
156 Tile *t;
157 for(i = 0; i < count; i++){
158 if(!hasflagat(l, p[i], Fhasmonster|Fblocked)){
159 t = tileat(l, p[i]);
160 t->monst = mkmons(l, p[i], type);
161 t->unit = t->monst->md->tile;
162 setflagat(l, p[i], Fhasmonster);
163 }
164 if(r > 0){
165 neigh = neighbor(l->r, p[i], &n);
166 for(j = 0; j < n; j++){
167 several(l, neigh, n, type, r-1);
168 }
169 free(neigh);
170 }
171 }
172}
173
174static void addspawn(Level *l, Point p, char *what, int freq)
175{11{
176 Spawn sp;12 Spawn sp;
177 13
@@ -179,6 +15,8 @@ static void addspawn(Level *l, Point p, char *what, int freq) · 1 unmodified line
179 sp.pt = p;15 sp.pt = p;
180 strcpy(sp.what, what);16 strcpy(sp.what, what);
181 sp.freq = freq;17 sp.freq = freq;
18 sp.turn = nrand(1000);
19 sp.istrap = istrap;
182 l->spawns[l->nspawns++] = sp;20 l->spawns[l->nspawns++] = sp;
183 tileat(l, p)->feat = TSPAWN;21 tileat(l, p)->feat = TSPAWN;
184 setflagat(l, p, Fhasfeature);22 setflagat(l, p, Fhasfeature);
@@ -186,197 +24,60 @@ static void addspawn(Level *l, Point p, char *what, int freq) · 1 unmodified line
186}24}
187 25
188static void26static void
189levelexec(AIState *a)27lrandstairs(Level *l)
190{28{
191 int i, j, n;29 Rectangle in;
192 Point p, np, *neigh;30 Point pup, pdown;
193 Level *l;
194 Tile *t;31 Tile *t;
195 MonsterData *md;
196 Monster *m;
197 32
198 l = a->aux;33 in = insetrect(l->r, 3);
199
200 for(i = 0; i < l->nspawns; i++){
201 if(turn % l->spawns[i].freq == 0){
202 p = l->spawns[i].pt;
203
204 /* le trap */
205 neigh = neighbor(l->r, p, &n);
206 for(j = 0; j < n; j++){
207 np = neigh[j];
208 if(hasflagat(l, np, Fhasmonster)){
209 t = tileat(l, np);
210 m = t->monst;
211 md = mdbyname(l->spawns[i].what);
212 if(strcmp(m->md->name, l->spawns[i].what) != 0 && abs(m->align - md->align) > 15){
213 several(l, &p, 1, l->spawns[i].what, 2);
214 }
215 }
216 }
217 34
218 free(neigh);35 pup = (Point){nrand(Dx(in))+3, nrand(Dy(in))+3};
219 several(l, &p, 1, l->spawns[i].what, 0);36 do {
220 }37 pdown = (Point){nrand(Dx(in))+3, nrand(Dy(in))+3};
221 }38 } while(eqpt(pdown, pup) || manhattan(pdown, pup) < ORTHOCOST*15);
39
40 setflagat(l, pup, Fhasfeature|Fportal);
41 t = tileat(l, pup);
42 t->feat = TUPSTAIR;
43 t->portal = mallocz(sizeof(Portal), 1);
44 strcpy(t->portal->name, "staircase upstairs");
45 l->up = pup;
46
47 setflagat(l, pdown, Fhasfeature|Fportal);
48 t = tileat(l, pdown);
49 t->feat = TDOWNSTAIR;
50 t->portal = mallocz(sizeof(Portal), 1);
51 strcpy(t->portal->name, "staircase downstairs");
52 l->down = pdown;
222}53}
223 54
224static int55static int
225gen(Level *l, int type)56gen(Level *l, int type)
226{57{
227 int space, npath, try;58 lrandstairs(l);
228 Point pup, pdown, p, p2, *path;
229 Rectangle in;
230 Tile *t;
231
232 space = 0;
233 try = 0;
234
235 in = insetrect(l->r, 3);
236 pup = ZP;
237 pdown = ZP;
238
239 while(1){
240 if(!eqpt(pup,ZP))
241 flagat(l, pup) = 0;
242 do{
243 pup = (Point){nrand(l->width), nrand(l->height)};
244 }while(!ptinrect(pup, in));
245
246 if(!eqpt(pdown, ZP))
247 flagat(l, pup) = 0;
248 do{
249 pdown = (Point){nrand(l->width), nrand(l->height)};
250 }while(!ptinrect(pdown, in));
251
252 /* already upstair? */
253 if(flagat(l, pdown) & Fportal)
254 continue;
255 /* too close? */
256 p = subpt(pup, pdown);
257 if((npath = pathfind(l, pup, pdown, &path, Fblocked)) < 0)
258 continue;
259 free(path);
260 if(npath < sqrt(l->width * l->height)-4)
261 continue;
262
263 setflagat(l, pup, Fhasfeature|Fportal);
264 t = tileat(l, pup);
265 t->feat = TUPSTAIR;
266 t->portal = mallocz(sizeof(Portal), 1);
267 strcpy(t->portal->name, "staircase upstairs");
268 l->up = pup;
269
270 setflagat(l, pdown, Fhasfeature|Fportal);
271 t = tileat(l, pdown);
272 t->feat = TDOWNSTAIR;
273 t->portal = mallocz(sizeof(Portal), 1);
274 strcpy(t->portal->name, "staircase downstairs");
275 l->down = pdown;
276 break;
277 }
278
279 enum { DIST = ORTHOCOST*10, };
280 59
281 switch(type){60 switch(type){
282 case 0:61 case 0:
283 drunken(l, TTREE, 3, 3, 3);62 mkldebug(l);
284 clear(l, pup, 1);
285 clear(l, pdown, 1);
286 several(l, &l->down, 1, "lich", 1);
287 several(l, &l->down, 1, "gnome wizard", 1);
288 break;
289 break;63 break;
290 case 1:64 case 1:
291 space += drunken(l, TTREE, 2, 0, 0);65 mklforest(l);
292 clear(l, pup, 1);
293 clear(l, pdown, 1);
294 do{
295 p = (Point){nrand(l->width), nrand(l->height)};
296 if(try++ > 10)
297 return -1;
298 }while(!ptinrect(p, in) || manhattan(p, l->down) < DIST || manhattan(p, l->up) < DIST || hasflagat(l, p, Fblocked|Fhasmonster|Fhasfeature));
299 clear(l, p, 2);
300 several(l, &p, 1, "captain", 0);
301 several(l, &p, 1, "lieutenant", 1);
302 addspawn(l, p, "soldier", 37);
303 addspawn(l, p, "sergeant", 51);
304 addspawn(l, p, "lieutenant", 97);
305 addspawn(l, p, "captain", 131);
306 do{
307 p2 = (Point){nrand(l->width), nrand(l->height)};
308 if(try++ > 10)
309 return -1;
310 }while(!ptinrect(p2, in) || manhattan(p, p2) < DIST || manhattan(p, p2) > DIST*2 || manhattan(p2, l->down) < DIST || manhattan(p2, l->up) < DIST || hasflagat(l, p2, Fblocked|Fhasmonster|Fhasfeature));
311 clear(l, p2, 2);
312 several(l, &p2, 1, "gnome king", 0);
313 several(l, &p2, 1, "gnome wizard", 1);
314 addspawn(l, p2, "gnome", 11);
315 addspawn(l, p2, "gnome lord", 25);
316 addspawn(l, p2, "gnome wizard", 73);
317 addspawn(l, p2, "gnome king", 131);
318
319 /* don't start empty */
320 genmonsters(l, "gnome lord", space/128);
321 genmonsters(l, "sergeant", space/128);
322 break;66 break;
323 case 2:67 case 2:
324 space += drunken(l, TGRAVE, 2, 4, 4);68 mklgraveyard(l);
325 clear(l, l->up, 2);
326 clear(l, l->down, 2);
327 do{
328 p = (Point){nrand(l->width), nrand(l->height)};
329 if(try++ > 10)
330 return -1;
331 }while(!ptinrect(p, in) || manhattan(p, l->down) < DIST || manhattan(p, l->up) < DIST || hasflagat(l, p, Fblocked|Fhasmonster|Fhasfeature));
332 clear(l, p, 2);
333 addspawn(l, p, "lich", 31);
334 addspawn(l, p, "ghost", 5);
335 do{
336 p2 = (Point){nrand(l->width), nrand(l->height)};
337 if(try++ > 10)
338 return -1;
339 }while(!ptinrect(p2, in) || manhattan(p, p2) < DIST*3 || manhattan(p2, l->down) < DIST || manhattan(p2, l->up) < DIST || hasflagat(l, p2, Fblocked|Fhasmonster|Fhasfeature));
340 clear(l, p2, 2);
341 several(l, &p2, 1, "gnome king", 0);
342 several(l, &p2, 1, "gnome wizard", 1);
343 addspawn(l, p2, "gnome", 11);
344 addspawn(l, p2, "gnome lord", 25);
345 addspawn(l, p2, "gnome wizard", 37);
346 addspawn(l, p2, "gnome king", 53);
347
348 genmonsters(l, "ghost", space/64);
349 genmonsters(l, "gnome", space/64);
350 break;69 break;
351 case 3:70 case 3:
352 space += drunken(l, TLAVA, 2, 1, 1);71 mklvolcano(l);
353 clear(l, pup, 2);
354 clear(l, pdown, 2);
355 do{
356 p = (Point){nrand(l->width), nrand(l->height)};
357 }while(!ptinrect(p, in) || hasflagat(l, p, Fblocked|Fhasfeature));
358 clear(l, p, 2);
359 addspawn(l, p, "large cat", 67);
360 do{
361 p = (Point){nrand(l->width), nrand(l->height)};
362 }while(!ptinrect(p, in) || hasflagat(l, p, Fblocked|Fhasfeature));
363 clear(l, p, 2);
364 addspawn(l, p, "gnome lord", 27);
365
366 genmonsters(l, "large cat", space/64);
367 genmonsters(l, "gnome lord", space/64);
368 break;72 break;
369 }73 }
370 74
371 l->ai = mkstate("level", nil, l, nil, levelexec, nil);
372
373 return 0;75 return 0;
374}76}
375 77
376Level*78Level*
377genlevel(int width, int height, int type)79genlevel(int width, int height, int type)
378{80{
379 int x, y;
380 Level *l;81 Level *l;
381 82
382 l = nil;83 l = nil;
@@ -385,36 +86,10 @@ genlevel(int width, int height, int type) · 2 unmodified lines
385 if(l != nil)86 if(l != nil)
386 freelevel(l);87 freelevel(l);
387 88
388 l = mallocz(sizeof(Level), 1);89 l = mklevel(width, height, TFLOOR);
389 if(l == nil)
390 return nil;
391
392 l->width = width;
393 l->height = height;
394 l->r = Rect(0, 0, width, height);
395
396 l->tiles = mallocz(sizeof(Tile) * width * height, 1);
397 if(l->tiles == nil)
398 goto err0;
399
400 l->flags = mallocz(sizeof(int) * width * height, 1);
401 if(l->flags == nil)
402 goto err1;
403
404 for(x = 0; x < width; x++){
405 for(y = 0; y < height; y++){
406 tileat(l, Pt(x, y))->terrain = TFLOOR;
407 }
408 }
409 } while(gen(l, type) < 0);90 } while(gen(l, type) < 0);
410 91
411 return l;92 return l;
412
413err1:
414 free(l->tiles);
415err0:
416 free(l);
417 return nil;
418}93}
419 94
420void95void
levelgen.c+489−0
@@ -0,0 +1,489 @@
1#include <u.h>
2#include <libc.h>
3#include <draw.h>
4#include <thread.h>
5
6#include "dat.h"
7#include "alg.h"
8
9/* removes all monsters/features near p, except p itself */
10static void
11lclear(Level *l, Point p, int dist)
12{
13 Rectangle r;
14 Point p2;
15 Tile *t;
16
17 r = Rect(p.x-dist, p.y-dist, p.x+dist, p.y+dist);
18 rectclip(&r, l->r);
19
20 for(p2.x = r.min.x; p2.x <= r.max.x; p2.x++){
21 for(p2.y = r.min.y; p2.y <= r.max.y; p2.y++){
22 if(eqpt(p, p2))
23 continue;
24 t = tileat(l, p2);
25 t->unit = t->feat = 0;
26 if(t->monst){
27 mfree(t->monst);
28 t->monst = nil;
29 }
30 flagat(l, p2) = 0;
31 }
32 }
33}
34
35static void
36lclearrect(Level *l, Rectangle r)
37{
38 Point p;
39 Tile *t;
40
41 rectclip(&r, l->r);
42
43 for(p.x = r.min.x; p.x <= r.max.x; p.x++){
44 for(p.y = r.min.y; p.y <= r.max.y; p.y++){
45 t = tileat(l, p);
46 t->unit = t->feat = 0;
47 if(t->monst != nil){
48 mfree(t->monst);
49 t->monst = nil;
50 }
51 if(t->portal != nil){
52 free(t->portal);
53 t->portal = nil;
54 }
55 flagat(l, p) = 0;
56 }
57 }
58}
59
60static void
61lfillrect(Level *l, Rectangle r, int type)
62{
63 Point p;
64 Tile *t;
65
66 for(p.x = r.min.x; p.x <= r.max.x; p.x++){
67 for(p.y = r.min.y; p.y <= r.max.y; p.y++){
68 t = tileat(l, p);
69 t->feat = type;
70 flagat(l, p) = (Fblocked|Fhasfeature);
71 }
72 }
73}
74
75static void
76lborder(Level *l, Rectangle r, int i, int type)
77{
78 Rectangle tmp;
79
80 assert(i != 0);
81
82 if(i < 0){
83 r = insetrect(r, i);
84 i = -i;
85 }
86
87 i -= 1;
88
89 tmp = Rect(r.min.x, r.min.y, r.max.x, r.min.y+i);
90 lclearrect(l, tmp);
91 lfillrect(l, tmp, type);
92
93 tmp = Rect(r.min.x, r.max.y-i, r.max.x, r.max.y);
94 lclearrect(l, tmp);
95 lfillrect(l, tmp, type);
96
97 tmp = Rect(r.min.x, r.min.y+i, r.min.x+i, r.max.y-i);
98 lclearrect(l, tmp);
99 lfillrect(l, tmp, type);
100
101 tmp = Rect(r.max.x-i, r.min.y+i, r.max.x, r.max.y-i);
102 lclearrect(l, tmp);
103 lfillrect(l, tmp, type);
104}
105
106/* more sbias makes it straighter. */
107static void
108drunk1(Level *l, Point p, uint count, uint sbias)
109{
110 int cur, last;
111 Point next;
112 Rectangle clipr;
113 Tile *t;
114
115 clipr = insetrect(l->r, 1);
116
117 last = nrand(NCARDINAL);
118 while(count > 0){
119 if(nrand(sbias+1) == 0)
120 cur = nrand(NCARDINAL);
121 else
122 cur = last;
123 next = addpt(p, cardinals[cur]);
124 if(!ptinrect(next, clipr))
125 continue;
126 last = cur;
127 p = next;
128 if(!eqpt(p, l->up) && !eqpt(p, l->down) && hasflagat(l, p, Fblocked|Fhasfeature)){
129 t = tileat(l, p);
130 t->feat = 0;
131 flagat(l, p) = 0;
132 count--;
133 }
134 }
135}
136
137/*
138 * drunken performs the drunkard's walk. as s1 and
139 * s2 increase, the paths become more biased toward
140 * 'straight'. it loops until the downstairs is
141 * reachable from the upstairs.
142 */
143static int
144drunken(Level *l, int type, int howmuch, int s1, int s2)
145{
146 int cnt, npath, redos;
147 Point p, *path;
148 Tile *t;
149
150 cnt = ((l->width * l->height) / howmuch); //* 2;
151 redos = 0;
152
153redo:
154 if(redos++ > 10)
155 return 0;
156 /* fill */
157 for(p.x = 0; p.x < l->width; p.x++){
158 for(p.y = 0; p.y < l->height; p.y++){
159 if(!hasflagat(l, p, Fblocked|Fhasfeature)){
160 t = tileat(l, p);
161 t->feat = type;
162 flagat(l, p) = (Fblocked|Fhasfeature);
163 }
164 }
165 }
166
167 drunk1(l, l->up, cnt/2, s1);
168 drunk1(l, l->down, cnt/2, s2);
169
170 /* check reachability */
171 npath = pathfind(l, l->up, l->down, &path, Fblocked);
172 if(npath < 0)
173 goto redo;
174 else
175 free(path);
176
177 return cnt * 2;
178}
179
180/* create a monster in the idle state */
181static Monster*
182mkmons(Level *l, Point p, char *type)
183{
184 Monster *m;
185 m = mbyname(type);
186 if(m == nil)
187 sysfatal("monstbyname: %r");
188 m->l = l;
189 m->pt = p;
190 /* setup ai state */
191 idle(m);
192 return m;
193}
194
195/*
196 * several is a recursive function, that spawns monsters
197 * centered at p[0] expaning outward according to a
198 * von neumann neighborhood with radius r. for example:
199 * several(l, p, 1, TGHOST, 2);
200 * spawns 13 ghosts centered at p on l.
201 */
202void
203several(Level *l, Point *p, int count, char *type, int r)
204{
205 int i, j, n;
206 Point *neigh;
207 Tile *t;
208 for(i = 0; i < count; i++){
209 if(!hasflagat(l, p[i], Fhasmonster|Fblocked)){
210 t = tileat(l, p[i]);
211 t->monst = mkmons(l, p[i], type);
212 t->unit = t->monst->md->tile;
213 setflagat(l, p[i], Fhasmonster);
214 }
215 if(r > 0){
216 neigh = neighbor(l->r, p[i], &n);
217 for(j = 0; j < n; j++){
218 several(l, neigh, n, type, r-1);
219 }
220 free(neigh);
221 }
222 }
223}
224
225/* genmonsters spawns count of type monsters at random places on l. */
226static void
227genmonsters(Level *l, char *type, int count)
228{
229 int i;
230 Point p;
231 Tile *t;
232
233 for(i = 0; i < count; i++){
234 do{
235 p = (Point){nrand(l->width), nrand(l->height)};
236 }while(hasflagat(l, p, Fhasmonster|Fhasfeature|Fblocked));
237
238 t = tileat(l, p);
239 t->monst = mkmons(l, p, type);
240 t->unit = t->monst->md->tile;
241 setflagat(l, p, Fhasmonster);
242 }
243}
244
245static void
246levelexec(AIState *a)
247{
248 int i, j, n;
249 Point p, np, *neigh;
250 Level *l;
251 Spawn *sp;
252 Tile *t;
253 MonsterData *md;
254 Monster *m;
255
256 l = a->aux;
257
258 for(i = 0; i < l->nspawns; i++){
259 sp = &l->spawns[i];
260 if(sp->turn++ % sp->freq == 0){
261 p = sp->pt;
262
263 /* le trap */
264 if(sp->istrap > 0){
265 neigh = neighbor(l->r, p, &n);
266 for(j = 0; j < n; j++){
267 np = neigh[j];
268 if(hasflagat(l, np, Fhasmonster)){
269 t = tileat(l, np);
270 m = t->monst;
271 md = mdbyname(sp->what);
272 if(md == nil){
273 dbg("programming error: monster %s not found: %r", sp->what);
274 continue;
275 }
276 if(strcmp(m->md->name, sp->what) != 0 && abs(m->align - md->align) > 15){
277 several(l, &p, 1, sp->what, sp->istrap);
278 }
279 }
280 }
281
282 free(neigh);
283 }
284 several(l, &p, 1, sp->what, 0);
285 }
286 }
287}
288
289Level*
290mklevel(int width, int height, int floor)
291{
292 Point p;
293 Level *l;
294
295 l = mallocz(sizeof(Level), 1);
296 if(l == nil)
297 return nil;
298
299 l->width = width;
300 l->height = height;
301 l->r = Rect(0, 0, width, height);
302
303 l->tiles = mallocz(sizeof(Tile) * width * height, 1);
304 if(l->tiles == nil)
305 goto err0;
306
307 l->flags = mallocz(sizeof(int) * width * height, 1);
308 if(l->flags == nil)
309 goto err1;
310
311 l->ai = mkstate("level", nil, l, nil, levelexec, nil);
312 if(l->ai == nil)
313 goto err2;
314
315 for(p.x = 0; p.x < width; p.x++){
316 for(p.y = 0; p.y < height; p.y++){
317 tileat(l, p)->terrain = floor;
318 }
319 }
320
321 return l;
322
323err2:
324 free(l->flags);
325err1:
326 free(l->tiles);
327err0:
328 free(l);
329 return nil;
330}
331
332int
333mkldebug(Level *l)
334{
335 drunken(l, TTREE, 3, 3, 3);
336 lclear(l, l->up, 1);
337 lclear(l, l->down, 1);
338 several(l, &l->down, 1, "lich", 1);
339 return 0;
340}
341
342int
343mklforest(Level *l)
344{
345 int space;
346 Point p, p2;
347 Rectangle in;
348
349 in = insetrect(l->r, 3);
350
351 space = drunken(l, TTREE, 2, 0, 0);
352 lclear(l, l->up, 1);
353 lclear(l, l->down, 1);
354
355 do {
356 p = (Point){nrand(Dx(in))+3, nrand(Dy(in))+3};
357 } while(flagat(l, p) != 0);
358 do {
359 p2 = (Point){nrand(Dx(in))+3, nrand(Dy(in))+3};
360 } while(flagat(l, p2) != 0 || manhattan(p, p2) < ORTHOCOST*10 || manhattan(p, p2) > ORTHOCOST*20);
361
362 lclear(l, p, 2);
363 several(l, &p, 1, "captain", 0);
364 several(l, &p, 1, "lieutenant", 1);
365 addspawn(l, p, "soldier", 57, 2);
366 addspawn(l, p, "sergeant", 91, 1);
367 addspawn(l, p, "lieutenant", 97, 1);
368 addspawn(l, p, "captain", 131, 1);
369
370 lclear(l, p2, 2);
371 several(l, &p2, 1, "gnome king", 0);
372 several(l, &p2, 1, "gnome wizard", 1);
373 addspawn(l, p2, "gnome", 13, 1);
374 addspawn(l, p2, "gnome lord", 17, 2);
375 addspawn(l, p2, "gnome wizard", 27, 1);
376 addspawn(l, p2, "gnome king", 101, 1);
377
378 /* don't start empty */
379 genmonsters(l, "gnome lord", space/128);
380 genmonsters(l, "sergeant", space/128);
381 return 0;
382}
383
384int
385mklgraveyard(Level *l)
386{
387 int space;
388 Point p, p2;
389 Rectangle in;
390
391 in = insetrect(l->r, 3);
392
393 space = drunken(l, TGRAVE, 2, 4, 4);
394 lclear(l, l->up, 2);
395 lclear(l, l->down, 2);
396
397 do {
398 p = (Point){nrand(Dx(in))+3, nrand(Dy(in))+3};
399 } while(flagat(l, p) != 0);
400 do {
401 p2 = (Point){nrand(Dx(in))+3, nrand(Dy(in))+3};
402 } while(flagat(l, p2) != 0 || manhattan(p, p2) < ORTHOCOST*15 || manhattan(p, p2) > ORTHOCOST*20);
403
404 lclear(l, p, 2);
405 lclear(l, p2, 2);
406
407 addspawn(l, p, "lich", 101, 1);
408 addspawn(l, p, "demilich", 157, 0);
409 addspawn(l, p, "ghost", 39, 2);
410 addspawn(l, p, "human zombie", 43, 2);
411 addspawn(l, p, "skeleton", 127, 1);
412
413 several(l, &p2, 1, "gnome king", 0);
414 several(l, &p2, 1, "gnome wizard", 1);
415 addspawn(l, p2, "gnome", 23, 1);
416 addspawn(l, p2, "gnome lord", 25, 2);
417 addspawn(l, p2, "gnome wizard", 37, 2);
418 addspawn(l, p2, "gnome king", 53, 1);
419
420 genmonsters(l, "ghost", space/64);
421 genmonsters(l, "gnome", space/64);
422
423 return 0;
424}
425
426int
427mklvolcano(Level *l)
428{
429 int space;
430 Point p, p2;
431 Rectangle in;
432
433 in = insetrect(l->r, 3);
434
435 space = drunken(l, TLAVA, 2, 1, 1);
436 lclear(l, l->up, 2);
437 lclear(l, l->down, 2);
438
439 do {
440 p = (Point){nrand(Dx(in))+3, nrand(Dy(in))+3};
441 } while(flagat(l, p) != 0);
442 do {
443 p2 = (Point){nrand(Dx(in))+3, nrand(Dy(in))+3};
444 } while(flagat(l, p2) != 0 || manhattan(p, p2) < ORTHOCOST*10 || manhattan(p, p2) > ORTHOCOST*20);
445
446 lclear(l, p, 2);
447 lclear(l, p2, 2);
448
449 addspawn(l, p, "large cat", 77, 0);
450 addspawn(l, p2, "gnome lord", 27, 2);
451
452 genmonsters(l, "large cat", space/128);
453 genmonsters(l, "gnome lord", space/64);
454 return 0;
455}
456
457int
458mklcastle(Level *l)
459{
460 int n;
461 Point p;
462 Rectangle mid;
463
464 mid = l->r;
465
466 n = Dx(l->r)/5;
467 mid.min.x += n;
468 mid.max.x -= n;
469 n = Dy(l->r)/5;
470 mid.min.y += n;
471 mid.max.y -= n;
472
473 lborder(l, mid, 1, TWALL);
474 lborder(l, mid, -1, TWATER);
475
476 /* create path in */
477 p = (Point){mid.min.x, mid.max.y};
478 p.x += nrand(Dx(mid)-2)+2;
479
480 lclearrect(l, Rpt(p, addpt(p, Pt(0,1))));
481
482 addspawn(l, addpt(mid.min, Pt(Dx(mid)/2, Dy(mid)/2)), "soldier", 21, 0);
483
484 l->up = Pt(1,1);
485 l->down = Pt(4,1);
486
487 return 0;
488}
489
mk.common+1−0
@@ -10,6 +10,7 @@ OFILES=\ · 9 unmodified lines
10 camera.$O\10 camera.$O\
11 hack9.$O\11 hack9.$O\
12 level.$O\12 level.$O\
13 levelgen.$O\
13 monst.$O\14 monst.$O\
14 path.$O\15 path.$O\
15 tile.$O\16 tile.$O\
monst.c+3−0
@@ -342,6 +342,9 @@ maction(Monster *m, int what, Point where) · 341 unmodified lines
342{342{
343 Tile *cur, *targ;343 Tile *cur, *targ;
344 344
345 if(!ptinrect(where, m->l->r))
346 return -1;
347
345 switch(what){348 switch(what){
346 default:349 default:
347 fprint(2, "unknown move %d\n", what);350 fprint(2, "unknown move %d\n", what);
monster.ndb+7−1
@@ -15,7 +15,13 @@ monster="gnome king" · 14 unmodified lines
15 15
16# undead16# undead
17monster="lich"17monster="lich"
18 tile=185 xpl=2 align=-50 mvr=6 hp=32 ac=3 atk=3d218 tile=185 xpl=11 align=-50 mvr=6 hp=32 ac=0 atk=1d10
19monster="demilich"
20 tile=186 xpl=14 align=-50 mvr=9 hp=48 ac=-2 atk=3d4
21monster="human zombie"
22 tile=246 xpl=4 align=-50 mvr=6 hp=16 ac=8 atk=1d8
23monster="skeleton"
24 tile=250 xpl=12 align=-50 mvr=8 hp=48 ac=4 atk=2d6
19monster="ghost"25monster="ghost"
20 tile=290 xpl=1 align=-50 mvr=6 hp=16 ac=-5 atk=1d426 tile=290 xpl=1 align=-50 mvr=6 hp=16 ac=-5 atk=1d4
21 27

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t