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
202202 char what[SZMONNAME];
203203 /* frequency of spawn, modulo turns */
204204 int freq;
205+ /* turn counter */
206+ int turn;
207+ /* spawn baddies when enemy is around? */
208+ int istrap;
205209 };
206210
207211 struct Tile
@@ -260,12 +264,22 @@ struct Level · 52 unmodified lines
260264 /* level.c */
261265 Level *genlevel(int width, int height, int type);
262266 void freelevel(Level *l);
267+void addspawn(Level *l, Point p, char *what, int freq, int istrap);
263268 Tile *tileat(Level *l, Point p);
264269 #define flagat(l, p) (*(l->flags+(p.y*l->width)+p.x))
265270 #define hasflagat(l, p, F) (*(l->flags+(p.y*l->width)+p.x) & (F))
266271 #define setflagat(l, p, F) (*(l->flags+(p.y*l->width)+p.x) |= (F))
267272 #define clrflagat(l, p, F) (*(l->flags+(p.y*l->width)+p.x) &= ~(F))
268273
274+/* levelgen.c */
275+Level *mklevel(int width, int height, int floor);
276+void several(Level *l, Point *p, int count, char *type, int r);
277+int mkldebug(Level *l);
278+int mklforest(Level *l);
279+int mklgraveyard(Level *l);
280+int mklvolcano(Level *l);
281+int mklcastle(Level *l);
282+
269283 struct Camera
270284 {
271285 Point pos;
hack9.c+3−8
@@ -67,7 +67,7 @@ movemons(void) · 66 unmodified lines
6767 l = player->l;
6868
6969 /* run ai on level, for spawns etc */
70− if(l->ai->exec != nil)
70+ if(l->ai != nil && l->ai->exec != nil)
7171 l->ai->exec(l->ai);
7272
7373 for(p.x = 0; p.x < l->width; p.x++){
@@ -137,13 +137,8 @@ threadmain(int argc, char *argv[]) · 63 unmodified lines
137137 }
138138
139139 /* initial level */
140− if(debug > 0){
141− if((level = genlevel(11, 11, 0)) == nil)
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− }
140+ if((level = genlevel(nrand(LSIZE)+LSIZE, nrand(LSIZE)+LSIZE, nrand(3)+1)) == nil)
141+ sysfatal("genlevel: %r");
147142
148143 /* the player */
149144 player = mbyname("wizard");
level.c+32−357
@@ -6,172 +6,8 @@ · 5 unmodified lines
66 #include "dat.h"
77 #include "alg.h"
88
9−/* clear removes all monsters/features near p. */
10−static void
11−clear(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. */
36−static void
37−drunk1(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− */
72−static int
73−drunken(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−
82−redo:
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 */
110−static Monster*
111−mkmons(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. */
125−static void
126−genmonsters(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− */
151−static void
152−several(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−
174−static void addspawn(Level *l, Point p, char *what, int freq)
9+void
10+addspawn(Level *l, Point p, char *what, int freq, int istrap)
17511 {
17612 Spawn sp;
17713
@@ -179,6 +15,8 @@ static void addspawn(Level *l, Point p, char *what, int freq) · 1 unmodified line
17915 sp.pt = p;
18016 strcpy(sp.what, what);
18117 sp.freq = freq;
18+ sp.turn = nrand(1000);
19+ sp.istrap = istrap;
18220 l->spawns[l->nspawns++] = sp;
18321 tileat(l, p)->feat = TSPAWN;
18422 setflagat(l, p, Fhasfeature);
@@ -186,197 +24,60 @@ static void addspawn(Level *l, Point p, char *what, int freq) · 1 unmodified line
18624 }
18725
18826 static void
189−levelexec(AIState *a)
27+lrandstairs(Level *l)
19028 {
191− int i, j, n;
192− Point p, np, *neigh;
193− Level *l;
29+ Rectangle in;
30+ Point pup, pdown;
19431 Tile *t;
195− MonsterData *md;
196− Monster *m;
19732
198− l = a->aux;
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− }
33+ in = insetrect(l->r, 3);
21734
218− free(neigh);
219− several(l, &p, 1, l->spawns[i].what, 0);
220− }
221− }
35+ pup = (Point){nrand(Dx(in))+3, nrand(Dy(in))+3};
36+ do {
37+ pdown = (Point){nrand(Dx(in))+3, nrand(Dy(in))+3};
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;
22253 }
22354
22455 static int
22556 gen(Level *l, int type)
22657 {
227− int space, npath, try;
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, };
58+ lrandstairs(l);
28059
28160 switch(type){
28261 case 0:
283− drunken(l, TTREE, 3, 3, 3);
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;
62+ mkldebug(l);
28963 break;
29064 case 1:
291− space += drunken(l, TTREE, 2, 0, 0);
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);
65+ mklforest(l);
32266 break;
32367 case 2:
324− space += drunken(l, TGRAVE, 2, 4, 4);
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);
68+ mklgraveyard(l);
35069 break;
35170 case 3:
352− space += drunken(l, TLAVA, 2, 1, 1);
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);
71+ mklvolcano(l);
36872 break;
36973 }
37074
371− l->ai = mkstate("level", nil, l, nil, levelexec, nil);
372−
37375 return 0;
37476 }
37577
37678 Level*
37779 genlevel(int width, int height, int type)
37880 {
379− int x, y;
38081 Level *l;
38182
38283 l = nil;
@@ -385,36 +86,10 @@ genlevel(int width, int height, int type) · 2 unmodified lines
38586 if(l != nil)
38687 freelevel(l);
38788
388− l = mallocz(sizeof(Level), 1);
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− }
89+ l = mklevel(width, height, TFLOOR);
40990 } while(gen(l, type) < 0);
41091
41192 return l;
412−
413−err1:
414− free(l->tiles);
415−err0:
416− free(l);
417− return nil;
41893 }
41994
42095 void
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 */
10+static void
11+lclear(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+static void
36+lclearrect(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+
60+static void
61+lfillrect(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+
75+static void
76+lborder(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. */
107+static void
108+drunk1(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+ */
143+static int
144+drunken(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+
153+redo:
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 */
181+static Monster*
182+mkmons(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+ */
202+void
203+several(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. */
226+static void
227+genmonsters(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+
245+static void
246+levelexec(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+
289+Level*
290+mklevel(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+
323+err2:
324+ free(l->flags);
325+err1:
326+ free(l->tiles);
327+err0:
328+ free(l);
329+ return nil;
330+}
331+
332+int
333+mkldebug(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+
342+int
343+mklforest(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+
384+int
385+mklgraveyard(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+
426+int
427+mklvolcano(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+
457+int
458+mklcastle(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
1010 camera.$O\
1111 hack9.$O\
1212 level.$O\
13+ levelgen.$O\
1314 monst.$O\
1415 path.$O\
1516 tile.$O\
monst.c+3−0
@@ -342,6 +342,9 @@ maction(Monster *m, int what, Point where) · 341 unmodified lines
342342 {
343343 Tile *cur, *targ;
344344
345+ if(!ptinrect(where, m->l->r))
346+ return -1;
347+
345348 switch(what){
346349 default:
347350 fprint(2, "unknown move %d\n", what);
monster.ndb+7−1
@@ -15,7 +15,13 @@ monster="gnome king" · 14 unmodified lines
1515
1616 # undead
1717 monster="lich"
18− tile=185 xpl=2 align=-50 mvr=6 hp=32 ac=3 atk=3d2
18+ tile=185 xpl=11 align=-50 mvr=6 hp=32 ac=0 atk=1d10
19+monster="demilich"
20+ tile=186 xpl=14 align=-50 mvr=9 hp=48 ac=-2 atk=3d4
21+monster="human zombie"
22+ tile=246 xpl=4 align=-50 mvr=6 hp=16 ac=8 atk=1d8
23+monster="skeleton"
24+ tile=250 xpl=12 align=-50 mvr=8 hp=48 ac=4 atk=2d6
1925 monster="ghost"
2026 tile=290 xpl=1 align=-50 mvr=6 hp=16 ac=-5 atk=1d4
2127

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