Unified · Split
| @@ -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 | ||
| 207 | struct Tile | 211 | struct Tile |
| @@ -260,12 +264,22 @@ struct Level · 52 unmodified lines | |||
| 260 | /* level.c */ | 264 | /* level.c */ |
| 261 | Level *genlevel(int width, int height, int type); | 265 | Level *genlevel(int width, int height, int type); |
| 262 | void freelevel(Level *l); | 266 | void freelevel(Level *l); |
| 267 | void addspawn(Level *l, Point p, char *what, int freq, int istrap); | ||
| 263 | Tile *tileat(Level *l, Point p); | 268 | Tile *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 */ | ||
| 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 | |||
| 269 | struct Camera | 283 | struct Camera |
| 270 | { | 284 | { |
| 271 | Point pos; | 285 | Point pos; |
| @@ -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"); |
| @@ -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. */ | 9 | void |
| 10 | static void | 10 | addspawn(Level *l, Point p, char *what, int freq, int istrap) |
| 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) | ||
| 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 | ||
| 188 | static void | 26 | static void |
| 189 | levelexec(AIState *a) | 27 | lrandstairs(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 | ||
| 224 | static int | 55 | static int |
| 225 | gen(Level *l, int type) | 56 | gen(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 | ||
| 376 | Level* | 78 | Level* |
| 377 | genlevel(int width, int height, int type) | 79 | genlevel(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 | |||
| 413 | err1: | ||
| 414 | free(l->tiles); | ||
| 415 | err0: | ||
| 416 | free(l); | ||
| 417 | return nil; | ||
| 418 | } | 93 | } |
| 419 | 94 | ||
| 420 | void | 95 | void |
| @@ -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 | |||
| @@ -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\ |
| @@ -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); |
| @@ -15,7 +15,13 @@ monster="gnome king" · 14 unmodified lines | |||
| 15 | 15 | ||
| 16 | # undead | 16 | # undead |
| 17 | monster="lich" | 17 | 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 | ||
| 19 | monster="ghost" | 25 | monster="ghost" |
| 20 | tile=290 xpl=1 align=-50 mvr=6 hp=16 ac=-5 atk=1d4 | 26 | tile=290 xpl=1 align=-50 mvr=6 hp=16 ac=-5 atk=1d4 |
| 21 | 27 | ||