| @@ -61,18 +61,41 @@ lclearrect(Level *l, Rectangle r) · 60 unmodified lines |
| 61 | 61 | } |
| 62 | 62 | |
| 63 | 63 | static void |
| 64 | | −lfillrect(Level *l, Rectangle r, int type) |
| 64 | +lfeature(Level *l, Point p, int type, int flags) |
| 65 | +{ |
| 66 | + Tile *t; |
| 67 | + |
| 68 | + t = tileat(l, p); |
| 69 | + t->feat = type; |
| 70 | + |
| 71 | + flagat(l, p) = flags; |
| 72 | +} |
| 73 | + |
| 74 | +static void |
| 75 | +lfeatrect(Level *l, Rectangle r, int type, int flags) |
| 65 | 76 | { |
| 66 | 77 | Point p; |
| 78 | + |
| 79 | + for(p.x = r.min.x; p.x <= r.max.x; p.x++) |
| 80 | + for(p.y = r.min.y; p.y <= r.max.y; p.y++) |
| 81 | + lfeature(l, p, type, flags); |
| 82 | +} |
| 83 | + |
| 84 | +static void |
| 85 | +lportal(Level *l, Point p, int feat, Level *to, Point tp) |
| 86 | +{ |
| 67 | 87 | Tile *t; |
| 88 | + Portal *port; |
| 68 | 89 | |
| 69 | | − for(p.x = r.min.x; p.x <= r.max.x; p.x++){ |
| 70 | | − for(p.y = r.min.y; p.y <= r.max.y; p.y++){ |
| 71 | | − t = tileat(l, p); |
| 72 | | − t->feat = type; |
| 73 | | − flagat(l, p) = (Fblocked|Fhasfeature); |
| 74 | | − } |
| 75 | | − } |
| 90 | + lfeature(l, p, feat, Fhasfeature); |
| 91 | + |
| 92 | + port = emalloc(sizeof(Portal)); |
| 93 | + port->to = to; |
| 94 | + port->pt = tp; |
| 95 | + |
| 96 | + t = tileat(l, p); |
| 97 | + |
| 98 | + t->portal = port; |
| 76 | 99 | } |
| 77 | 100 | |
| 78 | 101 | static void |
| @@ -91,93 +114,79 @@ lborder(Level *l, Rectangle r, int i, int type) · 12 unmodified lines |
| 91 | 114 | |
| 92 | 115 | tmp = Rect(r.min.x, r.min.y, r.max.x, r.min.y+i); |
| 93 | 116 | lclearrect(l, tmp); |
| 94 | | − lfillrect(l, tmp, type); |
| 117 | + lfeatrect(l, tmp, type, Fblocked|Fhasfeature); |
| 95 | 118 | |
| 96 | 119 | tmp = Rect(r.min.x, r.max.y-i, r.max.x, r.max.y); |
| 97 | 120 | lclearrect(l, tmp); |
| 98 | | − lfillrect(l, tmp, type); |
| 121 | + lfeatrect(l, tmp, type, Fblocked|Fhasfeature); |
| 99 | 122 | |
| 100 | 123 | tmp = Rect(r.min.x, r.min.y+i, r.min.x+i, r.max.y-i); |
| 101 | 124 | lclearrect(l, tmp); |
| 102 | | − lfillrect(l, tmp, type); |
| 125 | + lfeatrect(l, tmp, type, Fblocked|Fhasfeature); |
| 103 | 126 | |
| 104 | 127 | tmp = Rect(r.max.x-i, r.min.y+i, r.max.x, r.max.y-i); |
| 105 | 128 | lclearrect(l, tmp); |
| 106 | | − lfillrect(l, tmp, type); |
| 129 | + lfeatrect(l, tmp, type, Fblocked|Fhasfeature); |
| 107 | 130 | } |
| 108 | 131 | |
| 109 | | −/* more sbias makes it straighter. */ |
| 110 | 132 | static void |
| 111 | | −drunk1(Level *l, Point p, uint count, uint sbias) |
| 133 | +lspawn(Level *l, Point p, char *what, int freq, int istrap) |
| 112 | 134 | { |
| 113 | | − int cur, last; |
| 114 | | − Point next; |
| 115 | | − Rectangle clipr; |
| 116 | | − Tile *t; |
| 117 | | − |
| 118 | | − clipr = insetrect(l->or, 1); |
| 119 | | − |
| 120 | | − last = nrand(NCARDINAL); |
| 121 | | − while(count > 0){ |
| 122 | | − if(nrand(sbias+1) == 0) |
| 123 | | − cur = nrand(NCARDINAL); |
| 124 | | − else |
| 125 | | − cur = last; |
| 126 | | − next = addpt(p, cardinals[cur]); |
| 127 | | − if(!ptinrect(next, clipr)) |
| 128 | | − continue; |
| 129 | | − last = cur; |
| 130 | | − p = next; |
| 131 | | − if(!eqpt(p, l->up) && !eqpt(p, l->down) && hasflagat(l, p, Fblocked|Fhasfeature)){ |
| 132 | | − t = tileat(l, p); |
| 133 | | − t->feat = 0; |
| 134 | | − flagat(l, p) = 0; |
| 135 | | − count--; |
| 136 | | − } |
| 135 | + Spawn sp; |
| 136 | + |
| 137 | + if(l->nspawns < nelem(l->spawns)-1){ |
| 138 | + sp.pt = p; |
| 139 | + strcpy(sp.what, what); |
| 140 | + sp.freq = freq; |
| 141 | + sp.turn = nrand(1000); |
| 142 | + sp.istrap = istrap; |
| 143 | + l->spawns[l->nspawns++] = sp; |
| 144 | + tileat(l, p)->feat = TSPAWN; |
| 145 | + setflagat(l, p, Fhasfeature); |
| 137 | 146 | } |
| 138 | 147 | } |
| 139 | 148 | |
| 140 | | −/* |
| 141 | | − * drunken performs the drunkard's walk. as s1 and |
| 142 | | − * s2 increase, the paths become more biased toward |
| 143 | | − * 'straight'. it loops until the downstairs is |
| 144 | | − * reachable from the upstairs. |
| 145 | | − */ |
| 146 | | −static int |
| 147 | | −drunken(Level *l, int type, int howmuch, int s1, int s2) |
| 149 | +static void |
| 150 | +lrandstairs(Level *l) |
| 148 | 151 | { |
| 149 | | − int cnt, npath, redos; |
| 150 | | − Point p, *path; |
| 151 | | − Tile *t; |
| 152 | + Rectangle in; |
| 153 | + Point pup, pdown; |
| 152 | 154 | |
| 153 | | − cnt = ((l->width * l->height) / howmuch); //* 2; |
| 154 | | − redos = 0; |
| 155 | + in = insetrect(l->r, 3); |
| 155 | 156 | |
| 156 | | −redo: |
| 157 | | − if(redos++ > 10) |
| 158 | | − return 0; |
| 159 | | − /* fill */ |
| 160 | | − for(p.x = 0; p.x < l->width; p.x++){ |
| 161 | | − for(p.y = 0; p.y < l->height; p.y++){ |
| 162 | | − if(!hasflagat(l, p, Fblocked|Fhasfeature)){ |
| 163 | | − t = tileat(l, p); |
| 164 | | − t->feat = type; |
| 165 | | − flagat(l, p) = (Fblocked|Fhasfeature); |
| 166 | | − } |
| 167 | | − } |
| 168 | | − } |
| 157 | + pup = (Point){nrand(Dx(in))+3, nrand(Dy(in))+3}; |
| 158 | + do { |
| 159 | + pdown = (Point){nrand(Dx(in))+3, nrand(Dy(in))+3}; |
| 160 | + } while(eqpt(pdown, pup) || manhattan(pdown, pup) < ORTHOCOST*15); |
| 169 | 161 | |
| 170 | | − drunk1(l, l->up, cnt/2, s1); |
| 171 | | − drunk1(l, l->down, cnt/2, s2); |
| 162 | + l->up = pup; |
| 163 | + l->down = pdown; |
| 172 | 164 | |
| 173 | | − /* check reachability */ |
| 174 | | − npath = pathfind(l, l->up, l->down, &path, Fblocked); |
| 175 | | − if(npath < 0) |
| 176 | | − goto redo; |
| 177 | | − else |
| 178 | | − free(path); |
| 165 | + lportal(l, pup, TUPSTAIR, nil, ZP); |
| 166 | + lportal(l, pdown, TDOWNSTAIR, nil, ZP); |
| 167 | +} |
| 179 | 168 | |
| 180 | | − return cnt * 2; |
| 169 | +static int |
| 170 | +lgen(Level *l, int type) |
| 171 | +{ |
| 172 | + lrandstairs(l); |
| 173 | + |
| 174 | + switch(type){ |
| 175 | + case 0: |
| 176 | + mkldebug(l); |
| 177 | + break; |
| 178 | + case 1: |
| 179 | + mklforest(l); |
| 180 | + break; |
| 181 | + case 2: |
| 182 | + mklgraveyard(l); |
| 183 | + break; |
| 184 | + case 3: |
| 185 | + mklvolcano(l); |
| 186 | + break; |
| 187 | + } |
| 188 | + |
| 189 | + return 0; |
| 181 | 190 | } |
| 182 | 191 | |
| 183 | 192 | /* create a monster in the idle state */ |
| @@ -229,28 +238,9 @@ several(Level *l, Point *p, int count, char *type, int r) · 45 unmodified lines |
| 229 | 238 | } |
| 230 | 239 | } |
| 231 | 240 | |
| 232 | | −/* genmonsters spawns count of type monsters at random places on l. */ |
| 233 | | −static void |
| 234 | | −genmonsters(Level *l, char *type, int count) |
| 235 | | −{ |
| 236 | | − int i; |
| 237 | | − Point p; |
| 238 | | − Tile *t; |
| 239 | | − |
| 240 | | − for(i = 0; i < count; i++){ |
| 241 | | − do{ |
| 242 | | − p = (Point){nrand(l->width), nrand(l->height)}; |
| 243 | | − }while(hasflagat(l, p, Fhasmonster|Fhasfeature|Fblocked)); |
| 244 | | − |
| 245 | | − t = tileat(l, p); |
| 246 | | − t->monst = mkmons(l, p, type); |
| 247 | | − t->unit = t->monst->md->tile; |
| 248 | | − setflagat(l, p, Fhasmonster); |
| 249 | | − } |
| 250 | | −} |
| 251 | | − |
| 241 | +/* AI function for levels */ |
| 252 | 242 | static int |
| 253 | | −levelexec(void *v) |
| 243 | +lexec(void *v) |
| 254 | 244 | { |
| 255 | 245 | int i, j, n; |
| 256 | 246 | Point p, np, *neigh; |
| @@ -312,8 +302,8 @@ levelexec(void *v) · 55 unmodified lines |
| 312 | 302 | return TASKSUCCESS; |
| 313 | 303 | } |
| 314 | 304 | |
| 315 | | −Level* |
| 316 | | −mklevel(int width, int height, int floor, char *name) |
| 305 | +static Level* |
| 306 | +lmk(int width, int height, int floor, char *name) |
| 317 | 307 | { |
| 318 | 308 | int i; |
| 319 | 309 | Point p; |
| @@ -338,7 +328,7 @@ mklevel(int width, int height, int floor, char *name) · 18 unmodified lines |
| 338 | 328 | if(l->flags == nil) |
| 339 | 329 | goto err1; |
| 340 | 330 | |
| 341 | | − l->bt = btleaf("level", levelexec); |
| 331 | + l->bt = btleaf("level", lexec); |
| 342 | 332 | if(l->bt == nil) |
| 343 | 333 | goto err2; |
| 344 | 334 | |
| @@ -362,6 +352,112 @@ err0: · 17 unmodified lines |
| 362 | 352 | return nil; |
| 363 | 353 | } |
| 364 | 354 | |
| 355 | +Level* |
| 356 | +lgenerate(int width, int height, int type) |
| 357 | +{ |
| 358 | + Level *l; |
| 359 | + |
| 360 | + l = nil; |
| 361 | + |
| 362 | + do { |
| 363 | + if(l != nil) |
| 364 | + lfree(l); |
| 365 | + |
| 366 | + l = lmk(width, height, TFLOOR, "somewhere"); |
| 367 | + } while(lgen(l, type) < 0); |
| 368 | + |
| 369 | + return l; |
| 370 | +} |
| 371 | +/* more sbias makes it straighter. */ |
| 372 | +static void |
| 373 | +drunk1(Level *l, Point p, uint count, uint sbias) |
| 374 | +{ |
| 375 | + int cur, last; |
| 376 | + Point next; |
| 377 | + Rectangle clipr; |
| 378 | + Tile *t; |
| 379 | + |
| 380 | + clipr = insetrect(l->or, 1); |
| 381 | + |
| 382 | + last = nrand(NCARDINAL); |
| 383 | + while(count > 0){ |
| 384 | + if(nrand(sbias+1) == 0) |
| 385 | + cur = nrand(NCARDINAL); |
| 386 | + else |
| 387 | + cur = last; |
| 388 | + next = addpt(p, cardinals[cur]); |
| 389 | + if(!ptinrect(next, clipr)) |
| 390 | + continue; |
| 391 | + last = cur; |
| 392 | + p = next; |
| 393 | + if(!eqpt(p, l->up) && !eqpt(p, l->down) && hasflagat(l, p, Fblocked|Fhasfeature)){ |
| 394 | + t = tileat(l, p); |
| 395 | + t->feat = 0; |
| 396 | + flagat(l, p) = 0; |
| 397 | + count--; |
| 398 | + } |
| 399 | + } |
| 400 | +} |
| 401 | + |
| 402 | +/* |
| 403 | + * drunken performs the drunkard's walk. as s1 and |
| 404 | + * s2 increase, the paths become more biased toward |
| 405 | + * 'straight'. it loops until the downstairs is |
| 406 | + * reachable from the upstairs. |
| 407 | + */ |
| 408 | +static int |
| 409 | +drunken(Level *l, int type, int howmuch, int s1, int s2) |
| 410 | +{ |
| 411 | + int cnt, npath, redos; |
| 412 | + Point p, *path; |
| 413 | + |
| 414 | + cnt = ((l->width * l->height) / howmuch); //* 2; |
| 415 | + redos = 0; |
| 416 | + |
| 417 | +redo: |
| 418 | + if(redos++ > 10) |
| 419 | + return 0; |
| 420 | + /* fill */ |
| 421 | + for(p.x = 0; p.x < l->width; p.x++){ |
| 422 | + for(p.y = 0; p.y < l->height; p.y++){ |
| 423 | + if(!hasflagat(l, p, Fblocked|Fhasfeature)) |
| 424 | + lfeature(l, p, type, Fblocked|Fhasfeature); |
| 425 | + } |
| 426 | + } |
| 427 | + |
| 428 | + drunk1(l, l->up, cnt/2, s1); |
| 429 | + drunk1(l, l->down, cnt/2, s2); |
| 430 | + |
| 431 | + /* check reachability */ |
| 432 | + npath = pathfind(l, l->up, l->down, &path, Fblocked); |
| 433 | + if(npath < 0) |
| 434 | + goto redo; |
| 435 | + else |
| 436 | + free(path); |
| 437 | + |
| 438 | + return cnt * 2; |
| 439 | +} |
| 440 | + |
| 441 | +/* genmonsters spawns count of type monsters at random places on l. */ |
| 442 | +static void |
| 443 | +genmonsters(Level *l, char *type, int count) |
| 444 | +{ |
| 445 | + int i; |
| 446 | + Point p; |
| 447 | + Tile *t; |
| 448 | + |
| 449 | + for(i = 0; i < count; i++){ |
| 450 | + do{ |
| 451 | + p = (Point){nrand(l->width), nrand(l->height)}; |
| 452 | + }while(hasflagat(l, p, Fhasmonster|Fhasfeature|Fblocked)); |
| 453 | + |
| 454 | + t = tileat(l, p); |
| 455 | + t->monst = mkmons(l, p, type); |
| 456 | + t->unit = t->monst->md->tile; |
| 457 | + setflagat(l, p, Fhasmonster); |
| 458 | + } |
| 459 | +} |
| 460 | + |
| 365 | 461 | int |
| 366 | 462 | mkldebug(Level *l) |
| 367 | 463 | { |
| @@ -401,18 +497,18 @@ mklforest(Level *l) · 33 unmodified lines |
| 401 | 497 | lclear(l, p, 2); |
| 402 | 498 | several(l, &p, 1, "captain", 0); |
| 403 | 499 | several(l, &p, 1, "lieutenant", 1); |
| 404 | | − addspawn(l, p, "soldier", 57, 2); |
| 405 | | − addspawn(l, p, "sergeant", 91, 1); |
| 406 | | − addspawn(l, p, "lieutenant", 97, 1); |
| 407 | | − addspawn(l, p, "captain", 131, 1); |
| 500 | + lspawn(l, p, "soldier", 57, 2); |
| 501 | + lspawn(l, p, "sergeant", 91, 1); |
| 502 | + lspawn(l, p, "lieutenant", 97, 1); |
| 503 | + lspawn(l, p, "captain", 131, 1); |
| 408 | 504 | |
| 409 | 505 | lclear(l, p2, 2); |
| 410 | 506 | several(l, &p2, 1, "gnome king", 0); |
| 411 | 507 | several(l, &p2, 1, "gnome wizard", 1); |
| 412 | | − addspawn(l, p2, "gnome", 13, 1); |
| 413 | | − addspawn(l, p2, "gnome lord", 17, 2); |
| 414 | | − addspawn(l, p2, "gnome wizard", 27, 1); |
| 415 | | − addspawn(l, p2, "gnome king", 101, 1); |
| 508 | + lspawn(l, p2, "gnome", 13, 1); |
| 509 | + lspawn(l, p2, "gnome lord", 17, 2); |
| 510 | + lspawn(l, p2, "gnome wizard", 27, 1); |
| 511 | + lspawn(l, p2, "gnome king", 101, 1); |
| 416 | 512 | |
| 417 | 513 | /* don't start empty */ |
| 418 | 514 | genmonsters(l, "gnome lord", space/128); |
| @@ -444,18 +540,18 @@ mklgraveyard(Level *l) · 25 unmodified lines |
| 444 | 540 | lclear(l, p, 2); |
| 445 | 541 | lclear(l, p2, 2); |
| 446 | 542 | |
| 447 | | − addspawn(l, p, "lich", 101, 1); |
| 448 | | − addspawn(l, p, "demilich", 157, 0); |
| 449 | | − addspawn(l, p, "ghost", 15, 2); |
| 450 | | − addspawn(l, p, "human zombie", 27, 2); |
| 451 | | − addspawn(l, p, "skeleton", 71, 1); |
| 543 | + lspawn(l, p, "lich", 101, 1); |
| 544 | + lspawn(l, p, "demilich", 157, 0); |
| 545 | + lspawn(l, p, "ghost", 15, 2); |
| 546 | + lspawn(l, p, "human zombie", 27, 2); |
| 547 | + lspawn(l, p, "skeleton", 71, 1); |
| 452 | 548 | |
| 453 | 549 | several(l, &p2, 1, "gnome king", 0); |
| 454 | 550 | several(l, &p2, 1, "gnome wizard", 1); |
| 455 | | − addspawn(l, p2, "gnome", 29, 1); |
| 456 | | − addspawn(l, p2, "gnome lord", 45, 2); |
| 457 | | − addspawn(l, p2, "gnome wizard", 57, 2); |
| 458 | | − addspawn(l, p2, "gnome king", 71, 1); |
| 551 | + lspawn(l, p2, "gnome", 29, 1); |
| 552 | + lspawn(l, p2, "gnome lord", 45, 2); |
| 553 | + lspawn(l, p2, "gnome wizard", 57, 2); |
| 554 | + lspawn(l, p2, "gnome king", 71, 1); |
| 459 | 555 | |
| 460 | 556 | genmonsters(l, "ghost", space/64); |
| 461 | 557 | genmonsters(l, "gnome", space/64); |
| @@ -486,8 +582,8 @@ mklvolcano(Level *l) · 24 unmodified lines |
| 486 | 582 | lclear(l, p, 2); |
| 487 | 583 | lclear(l, p2, 2); |
| 488 | 584 | |
| 489 | | − addspawn(l, p, "large cat", 77, 0); |
| 490 | | − addspawn(l, p2, "gnome lord", 27, 2); |
| 585 | + lspawn(l, p, "large cat", 77, 0); |
| 586 | + lspawn(l, p2, "gnome lord", 27, 2); |
| 491 | 587 | |
| 492 | 588 | genmonsters(l, "large cat", space/128); |
| 493 | 589 | genmonsters(l, "gnome lord", space/64); |
| @@ -519,7 +615,7 @@ mklcastle(Level *l) · 25 unmodified lines |
| 519 | 615 | |
| 520 | 616 | lclearrect(l, Rpt(p, addpt(p, Pt(0,1)))); |
| 521 | 617 | |
| 522 | | − addspawn(l, addpt(mid.min, Pt(Dx(mid)/2, Dy(mid)/2)), "soldier", 21, 0); |
| 618 | + lspawn(l, addpt(mid.min, Pt(Dx(mid)/2, Dy(mid)/2)), "soldier", 21, 0); |
| 523 | 619 | |
| 524 | 620 | l->up = Pt(1,1); |
| 525 | 621 | l->down = Pt(4,1); |