| @@ -61,18 +61,41 @@ lclearrect(Level *l, Rectangle r) · 60 unmodified lines |
| 61 | } | 61 | } |
| 62 | | 62 | |
| 63 | static void | 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 | Point p; | 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 | Tile *t; | 87 | Tile *t; |
| | 88 | Portal *port; |
| 68 | | 89 | |
| 69 | for(p.x = r.min.x; p.x <= r.max.x; p.x++){ | 90 | lfeature(l, p, feat, Fhasfeature); |
| 70 | for(p.y = r.min.y; p.y <= r.max.y; p.y++){ | 91 | |
| 71 | t = tileat(l, p); | 92 | port = emalloc(sizeof(Portal)); |
| 72 | t->feat = type; | 93 | port->to = to; |
| 73 | flagat(l, p) = (Fblocked|Fhasfeature); | 94 | port->pt = tp; |
| 74 | } | 95 | |
| 75 | } | 96 | t = tileat(l, p); |
| | 97 | |
| | 98 | t->portal = port; |
| 76 | } | 99 | } |
| 77 | | 100 | |
| 78 | static void | 101 | static void |
| @@ -91,93 +114,79 @@ lborder(Level *l, Rectangle r, int i, int type) · 12 unmodified lines |
| 91 | | 114 | |
| 92 | tmp = Rect(r.min.x, r.min.y, r.max.x, r.min.y+i); | 115 | tmp = Rect(r.min.x, r.min.y, r.max.x, r.min.y+i); |
| 93 | lclearrect(l, tmp); | 116 | lclearrect(l, tmp); |
| 94 | lfillrect(l, tmp, type); | 117 | lfeatrect(l, tmp, type, Fblocked|Fhasfeature); |
| 95 | | 118 | |
| 96 | tmp = Rect(r.min.x, r.max.y-i, r.max.x, r.max.y); | 119 | tmp = Rect(r.min.x, r.max.y-i, r.max.x, r.max.y); |
| 97 | lclearrect(l, tmp); | 120 | lclearrect(l, tmp); |
| 98 | lfillrect(l, tmp, type); | 121 | lfeatrect(l, tmp, type, Fblocked|Fhasfeature); |
| 99 | | 122 | |
| 100 | tmp = Rect(r.min.x, r.min.y+i, r.min.x+i, r.max.y-i); | 123 | tmp = Rect(r.min.x, r.min.y+i, r.min.x+i, r.max.y-i); |
| 101 | lclearrect(l, tmp); | 124 | lclearrect(l, tmp); |
| 102 | lfillrect(l, tmp, type); | 125 | lfeatrect(l, tmp, type, Fblocked|Fhasfeature); |
| 103 | | 126 | |
| 104 | tmp = Rect(r.max.x-i, r.min.y+i, r.max.x, r.max.y-i); | 127 | tmp = Rect(r.max.x-i, r.min.y+i, r.max.x, r.max.y-i); |
| 105 | lclearrect(l, tmp); | 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 | static void | 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; | 135 | Spawn sp; |
| 114 | Point next; | 136 | |
| 115 | Rectangle clipr; | 137 | if(l->nspawns < nelem(l->spawns)-1){ |
| 116 | Tile *t; | 138 | sp.pt = p; |
| 117 | | 139 | strcpy(sp.what, what); |
| 118 | clipr = insetrect(l->or, 1); | 140 | sp.freq = freq; |
| 119 | | 141 | sp.turn = nrand(1000); |
| 120 | last = nrand(NCARDINAL); | 142 | sp.istrap = istrap; |
| 121 | while(count > 0){ | 143 | l->spawns[l->nspawns++] = sp; |
| 122 | if(nrand(sbias+1) == 0) | 144 | tileat(l, p)->feat = TSPAWN; |
| 123 | cur = nrand(NCARDINAL); | 145 | setflagat(l, p, Fhasfeature); |
| 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 | } | | |
| 137 | } | 146 | } |
| 138 | } | 147 | } |
| 139 | | 148 | |
| 140 | /* | 149 | static void |
| 141 | * drunken performs the drunkard's walk. as s1 and | 150 | lrandstairs(Level *l) |
| 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) | | |
| 148 | { | 151 | { |
| 149 | int cnt, npath, redos; | 152 | Rectangle in; |
| 150 | Point p, *path; | 153 | Point pup, pdown; |
| 151 | Tile *t; | | |
| 152 | | 154 | |
| 153 | cnt = ((l->width * l->height) / howmuch); //* 2; | 155 | in = insetrect(l->r, 3); |
| 154 | redos = 0; | | |
| 155 | | 156 | |
| 156 | redo: | 157 | pup = (Point){nrand(Dx(in))+3, nrand(Dy(in))+3}; |
| 157 | if(redos++ > 10) | 158 | do { |
| 158 | return 0; | 159 | pdown = (Point){nrand(Dx(in))+3, nrand(Dy(in))+3}; |
| 159 | /* fill */ | 160 | } while(eqpt(pdown, pup) || manhattan(pdown, pup) < ORTHOCOST*15); |
| 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 | } | | |
| 169 | | 161 | |
| 170 | drunk1(l, l->up, cnt/2, s1); | 162 | l->up = pup; |
| 171 | drunk1(l, l->down, cnt/2, s2); | 163 | l->down = pdown; |
| 172 | | 164 | |
| 173 | /* check reachability */ | 165 | lportal(l, pup, TUPSTAIR, nil, ZP); |
| 174 | npath = pathfind(l, l->up, l->down, &path, Fblocked); | 166 | lportal(l, pdown, TDOWNSTAIR, nil, ZP); |
| 175 | if(npath < 0) | 167 | } |
| 176 | goto redo; | | |
| 177 | else | | |
| 178 | free(path); | | |
| 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 | /* create a monster in the idle state */ | 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. */ | 241 | /* AI function for levels */ |
| 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 | | | |
| 252 | static int | 242 | static int |
| 253 | levelexec(void *v) | 243 | lexec(void *v) |
| 254 | { | 244 | { |
| 255 | int i, j, n; | 245 | int i, j, n; |
| 256 | Point p, np, *neigh; | 246 | Point p, np, *neigh; |
| @@ -312,8 +302,8 @@ levelexec(void *v) · 55 unmodified lines |
| 312 | return TASKSUCCESS; | 302 | return TASKSUCCESS; |
| 313 | } | 303 | } |
| 314 | | 304 | |
| 315 | Level* | 305 | static Level* |
| 316 | mklevel(int width, int height, int floor, char *name) | 306 | lmk(int width, int height, int floor, char *name) |
| 317 | { | 307 | { |
| 318 | int i; | 308 | int i; |
| 319 | Point p; | 309 | Point p; |
| @@ -338,7 +328,7 @@ mklevel(int width, int height, int floor, char *name) · 18 unmodified lines |
| 338 | if(l->flags == nil) | 328 | if(l->flags == nil) |
| 339 | goto err1; | 329 | goto err1; |
| 340 | | 330 | |
| 341 | l->bt = btleaf("level", levelexec); | 331 | l->bt = btleaf("level", lexec); |
| 342 | if(l->bt == nil) | 332 | if(l->bt == nil) |
| 343 | goto err2; | 333 | goto err2; |
| 344 | | 334 | |
| @@ -362,6 +352,112 @@ err0: · 17 unmodified lines |
| 362 | return nil; | 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 | int | 461 | int |
| 366 | mkldebug(Level *l) | 462 | mkldebug(Level *l) |
| 367 | { | 463 | { |
| @@ -401,18 +497,18 @@ mklforest(Level *l) · 33 unmodified lines |
| 401 | lclear(l, p, 2); | 497 | lclear(l, p, 2); |
| 402 | several(l, &p, 1, "captain", 0); | 498 | several(l, &p, 1, "captain", 0); |
| 403 | several(l, &p, 1, "lieutenant", 1); | 499 | several(l, &p, 1, "lieutenant", 1); |
| 404 | addspawn(l, p, "soldier", 57, 2); | 500 | lspawn(l, p, "soldier", 57, 2); |
| 405 | addspawn(l, p, "sergeant", 91, 1); | 501 | lspawn(l, p, "sergeant", 91, 1); |
| 406 | addspawn(l, p, "lieutenant", 97, 1); | 502 | lspawn(l, p, "lieutenant", 97, 1); |
| 407 | addspawn(l, p, "captain", 131, 1); | 503 | lspawn(l, p, "captain", 131, 1); |
| 408 | | 504 | |
| 409 | lclear(l, p2, 2); | 505 | lclear(l, p2, 2); |
| 410 | several(l, &p2, 1, "gnome king", 0); | 506 | several(l, &p2, 1, "gnome king", 0); |
| 411 | several(l, &p2, 1, "gnome wizard", 1); | 507 | several(l, &p2, 1, "gnome wizard", 1); |
| 412 | addspawn(l, p2, "gnome", 13, 1); | 508 | lspawn(l, p2, "gnome", 13, 1); |
| 413 | addspawn(l, p2, "gnome lord", 17, 2); | 509 | lspawn(l, p2, "gnome lord", 17, 2); |
| 414 | addspawn(l, p2, "gnome wizard", 27, 1); | 510 | lspawn(l, p2, "gnome wizard", 27, 1); |
| 415 | addspawn(l, p2, "gnome king", 101, 1); | 511 | lspawn(l, p2, "gnome king", 101, 1); |
| 416 | | 512 | |
| 417 | /* don't start empty */ | 513 | /* don't start empty */ |
| 418 | genmonsters(l, "gnome lord", space/128); | 514 | genmonsters(l, "gnome lord", space/128); |
| @@ -444,18 +540,18 @@ mklgraveyard(Level *l) · 25 unmodified lines |
| 444 | lclear(l, p, 2); | 540 | lclear(l, p, 2); |
| 445 | lclear(l, p2, 2); | 541 | lclear(l, p2, 2); |
| 446 | | 542 | |
| 447 | addspawn(l, p, "lich", 101, 1); | 543 | lspawn(l, p, "lich", 101, 1); |
| 448 | addspawn(l, p, "demilich", 157, 0); | 544 | lspawn(l, p, "demilich", 157, 0); |
| 449 | addspawn(l, p, "ghost", 15, 2); | 545 | lspawn(l, p, "ghost", 15, 2); |
| 450 | addspawn(l, p, "human zombie", 27, 2); | 546 | lspawn(l, p, "human zombie", 27, 2); |
| 451 | addspawn(l, p, "skeleton", 71, 1); | 547 | lspawn(l, p, "skeleton", 71, 1); |
| 452 | | 548 | |
| 453 | several(l, &p2, 1, "gnome king", 0); | 549 | several(l, &p2, 1, "gnome king", 0); |
| 454 | several(l, &p2, 1, "gnome wizard", 1); | 550 | several(l, &p2, 1, "gnome wizard", 1); |
| 455 | addspawn(l, p2, "gnome", 29, 1); | 551 | lspawn(l, p2, "gnome", 29, 1); |
| 456 | addspawn(l, p2, "gnome lord", 45, 2); | 552 | lspawn(l, p2, "gnome lord", 45, 2); |
| 457 | addspawn(l, p2, "gnome wizard", 57, 2); | 553 | lspawn(l, p2, "gnome wizard", 57, 2); |
| 458 | addspawn(l, p2, "gnome king", 71, 1); | 554 | lspawn(l, p2, "gnome king", 71, 1); |
| 459 | | 555 | |
| 460 | genmonsters(l, "ghost", space/64); | 556 | genmonsters(l, "ghost", space/64); |
| 461 | genmonsters(l, "gnome", space/64); | 557 | genmonsters(l, "gnome", space/64); |
| @@ -486,8 +582,8 @@ mklvolcano(Level *l) · 24 unmodified lines |
| 486 | lclear(l, p, 2); | 582 | lclear(l, p, 2); |
| 487 | lclear(l, p2, 2); | 583 | lclear(l, p2, 2); |
| 488 | | 584 | |
| 489 | addspawn(l, p, "large cat", 77, 0); | 585 | lspawn(l, p, "large cat", 77, 0); |
| 490 | addspawn(l, p2, "gnome lord", 27, 2); | 586 | lspawn(l, p2, "gnome lord", 27, 2); |
| 491 | | 587 | |
| 492 | genmonsters(l, "large cat", space/128); | 588 | genmonsters(l, "large cat", space/128); |
| 493 | genmonsters(l, "gnome lord", space/64); | 589 | genmonsters(l, "gnome lord", space/64); |
| @@ -519,7 +615,7 @@ mklcastle(Level *l) · 25 unmodified lines |
| 519 | | 615 | |
| 520 | lclearrect(l, Rpt(p, addpt(p, Pt(0,1)))); | 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 | l->up = Pt(1,1); | 620 | l->up = Pt(1,1); |
| 525 | l->down = Pt(4,1); | 621 | l->down = Pt(4,1); |