1 | /* SCCS Id: @(#)mkmaze.c 3.3 2000/01/03 */ 2 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 3 | /* NetHack may be freely redistributed. See license for details. */ 4 | 5 | #include "hack.h" 6 | #include "sp_lev.h" 7 | #include "lev.h" /* save & restore info */ 8 | 9 | /* from sp_lev.c, for fixup_special() */ 10 | extern char *lev_message; 11 | extern lev_region *lregions; 12 | extern int num_lregions; 13 | 14 | STATIC_DCL boolean FDECL(iswall,(int,int)); 15 | STATIC_DCL boolean FDECL(iswall_or_stone,(int,int)); 16 | STATIC_DCL boolean FDECL(is_solid,(int,int)); 17 | STATIC_DCL int FDECL(extend_spine, (int [3][3], int, int, int)); 18 | STATIC_DCL boolean FDECL(okay,(int,int,int)); 19 | STATIC_DCL void FDECL(maze0xy,(coord *)); 20 | STATIC_DCL boolean FDECL(put_lregion_here,(XCHAR_P,XCHAR_P,XCHAR_P, 21 | XCHAR_P,XCHAR_P,XCHAR_P,XCHAR_P,BOOLEAN_P,d_level *)); 22 | STATIC_DCL void NDECL(fixup_special); 23 | STATIC_DCL void FDECL(move, (int *,int *,int)); 24 | STATIC_DCL void NDECL(setup_waterlevel); 25 | STATIC_DCL void NDECL(unsetup_waterlevel); 26 | 27 | 28 | STATIC_OVL boolean 29 | iswall(x,y) 30 | int x,y; 31 | { 32 | if (!isok(x,y)) return FALSE; 33 | return (IS_WALL(levl[x][y].typ) || IS_DOOR(levl[x][y].typ) 34 | || levl[x][y].typ == SDOOR); 35 | } 36 | 37 | STATIC_OVL boolean 38 | iswall_or_stone(x,y) 39 | int x,y; 40 | { 41 | register int type; 42 | 43 | /* out of bounds = stone */ 44 | if (!isok(x,y)) return TRUE; 45 | 46 | type = levl[x][y].typ; 47 | return (type == STONE || IS_WALL(type) || IS_DOOR(type) || type == SDOOR); 48 | } 49 | 50 | /* return TRUE if out of bounds, wall or rock */ 51 | STATIC_OVL boolean 52 | is_solid(x,y) 53 | int x, y; 54 | { 55 | return (!isok(x,y) || IS_STWALL(levl[x][y].typ)); 56 | } 57 | 58 | 59 | /* 60 | * Return 1 (not TRUE - we're doing bit vectors here) if we want to extend 61 | * a wall spine in the (dx,dy) direction. Return 0 otherwise. 62 | * 63 | * To extend a wall spine in that direction, first there must be a wall there. 64 | * Then, extend a spine unless the current position is surrounded by walls 65 | * in the direction given by (dx,dy). E.g. if 'x' is our location, 'W' 66 | * a wall, '.' a room, 'a' anything (we don't care), and our direction is 67 | * (0,1) - South or down - then: 68 | * 69 | * a a a 70 | * W x W This would not extend a spine from x down 71 | * W W W (a corridor of walls is formed). 72 | * 73 | * a a a 74 | * W x W This would extend a spine from x down. 75 | * . W W 76 | */ 77 | STATIC_OVL int 78 | extend_spine(locale, wall_there, dx, dy) 79 | int locale[3][3]; 80 | int wall_there, dx, dy; 81 | { 82 | int spine, nx, ny; 83 | 84 | nx = 1 + dx; 85 | ny = 1 + dy; 86 | 87 | if (wall_there) { /* wall in that direction */ 88 | if (dx) { 89 | if (locale[ 1][0] && locale[ 1][2] && /* EW are wall/stone */ 90 | locale[nx][0] && locale[nx][2]) { /* diag are wall/stone */ 91 | spine = 0; 92 | } else { 93 | spine = 1; 94 | } 95 | } else { /* dy */ 96 | if (locale[0][ 1] && locale[2][ 1] && /* NS are wall/stone */ 97 | locale[0][ny] && locale[2][ny]) { /* diag are wall/stone */ 98 | spine = 0; 99 | } else { 100 | spine = 1; 101 | } 102 | } 103 | } else { 104 | spine = 0; 105 | } 106 | 107 | return spine; 108 | } 109 | 110 | 111 | /* 112 | * Wall cleanup. This function has two purposes: (1) remove walls that 113 | * are totally surrounded by stone - they are redundant. (2) correct 114 | * the types so that they extend and connect to each other. 115 | */ 116 | void 117 | wallification(x1, y1, x2, y2) 118 | int x1, y1, x2, y2; 119 | { 120 | uchar type; 121 | register int x,y; 122 | struct rm *lev; 123 | int bits; 124 | int locale[3][3]; /* rock or wall status surrounding positions */ 125 | /* 126 | * Value 0 represents a free-standing wall. It could be anything, 127 | * so even though this table says VWALL, we actually leave whatever 128 | * typ was there alone. 129 | */ 130 | static xchar spine_array[16] = { 131 | VWALL, HWALL, HWALL, HWALL, 132 | VWALL, TRCORNER, TLCORNER, TDWALL, 133 | VWALL, BRCORNER, BLCORNER, TUWALL, 134 | VWALL, TLWALL, TRWALL, CROSSWALL 135 | }; 136 | 137 | /* sanity check on incoming variables */ 138 | if (x1<0 || x2>=COLNO || x1>x2 || y1<0 || y2>=ROWNO || y1>y2) 139 | panic("wallification: bad bounds (%d,%d) to (%d,%d)",x1,y1,x2,y2); 140 | 141 | /* Step 1: change walls surrounded by rock to rock. */ 142 | for(x = x1; x <= x2; x++) 143 | for(y = y1; y <= y2; y++) { 144 | lev = &levl[x][y]; 145 | type = lev->typ; 146 | if (IS_WALL(type) && type != DBWALL) { 147 | if (is_solid(x-1,y-1) && 148 | is_solid(x-1,y ) && 149 | is_solid(x-1,y+1) && 150 | is_solid(x, y-1) && 151 | is_solid(x, y+1) && 152 | is_solid(x+1,y-1) && 153 | is_solid(x+1,y ) && 154 | is_solid(x+1,y+1)) 155 | lev->typ = STONE; 156 | } 157 | } 158 | 159 | /* 160 | * Step 2: set the correct wall type. We can't combine steps 161 | * 1 and 2 into a single sweep because we depend on knowing if 162 | * the surrounding positions are stone. 163 | */ 164 | for(x = x1; x <= x2; x++) 165 | for(y = y1; y <= y2; y++) { 166 | lev = &levl[x][y]; 167 | type = lev->typ; 168 | if ( !(IS_WALL(type) && type != DBWALL)) continue; 169 | 170 | /* set the locations TRUE if rock or wall or out of bounds */ 171 | locale[0][0] = iswall_or_stone(x-1,y-1); 172 | locale[1][0] = iswall_or_stone( x,y-1); 173 | locale[2][0] = iswall_or_stone(x+1,y-1); 174 | 175 | locale[0][1] = iswall_or_stone(x-1, y); 176 | locale[2][1] = iswall_or_stone(x+1, y); 177 | 178 | locale[0][2] = iswall_or_stone(x-1,y+1); 179 | locale[1][2] = iswall_or_stone( x,y+1); 180 | locale[2][2] = iswall_or_stone(x+1,y+1); 181 | 182 | /* determine if wall should extend to each direction NSEW */ 183 | bits = (extend_spine(locale, iswall(x,y-1), 0, -1) << 3) 184 | | (extend_spine(locale, iswall(x,y+1), 0, 1) << 2) 185 | | (extend_spine(locale, iswall(x+1,y), 1, 0) << 1) 186 | | extend_spine(locale, iswall(x-1,y), -1, 0); 187 | 188 | /* don't change typ if wall is free-standing */ 189 | if (bits) lev->typ = spine_array[bits]; 190 | } 191 | } 192 | 193 | STATIC_OVL boolean 194 | okay(x,y,dir) 195 | int x,y; 196 | register int dir; 197 | { 198 | move(&x,&y,dir); 199 | move(&x,&y,dir); 200 | if(x<3 || y<3 || x>x_maze_max || y>y_maze_max || levl[x][y].typ != 0) 201 | return(FALSE); 202 | return(TRUE); 203 | } 204 | 205 | STATIC_OVL void 206 | maze0xy(cc) /* find random starting point for maze generation */ 207 | coord *cc; 208 | { 209 | cc->x = 3 + 2*rn2((x_maze_max>>1) - 1); 210 | cc->y = 3 + 2*rn2((y_maze_max>>1) - 1); 211 | return; 212 | } 213 | 214 | /* 215 | * Bad if: 216 | * pos is occupied OR 217 | * pos is inside restricted region (lx,ly,hx,hy) OR 218 | * NOT (pos is corridor and a maze level OR pos is a room OR pos is air) 219 | */ 220 | boolean 221 | bad_location(x, y, lx, ly, hx, hy) 222 | xchar x, y; 223 | xchar lx, ly, hx, hy; 224 | { 225 | return((boolean)(occupied(x, y) || 226 | within_bounded_area(x,y, lx,ly, hx,hy) || 227 | !((levl[x][y].typ == CORR && level.flags.is_maze_lev) || 228 | levl[x][y].typ == ROOM || levl[x][y].typ == AIR))); 229 | } 230 | 231 | /* pick a location in area (lx, ly, hx, hy) but not in (nlx, nly, nhx, nhy) */ 232 | /* and place something (based on rtype) in that region */ 233 | void 234 | place_lregion(lx, ly, hx, hy, nlx, nly, nhx, nhy, rtype, lev) 235 | xchar lx, ly, hx, hy; 236 | xchar nlx, nly, nhx, nhy; 237 | xchar rtype; 238 | d_level *lev; 239 | { 240 | int trycnt; 241 | boolean oneshot; 242 | xchar x, y; 243 | 244 | if(!lx) { /* default to whole level */ 245 | /* 246 | * if there are rooms and this a branch, let place_branch choose 247 | * the branch location (to avoid putting branches in corridors). 248 | */ 249 | if(rtype == LR_BRANCH && nroom) { 250 | place_branch(Is_branchlev(&u.uz), 0, 0); 251 | return; 252 | } 253 | 254 | lx = 1; hx = COLNO-1; 255 | ly = 1; hy = ROWNO-1; 256 | } 257 | 258 | /* first a probabilistic approach */ 259 | 260 | oneshot = (lx == hx && ly == hy); 261 | for(trycnt = 0; trycnt < 100; trycnt ++) { 262 | 263 | x = rn1((hx - lx) + 1, lx); 264 | y = rn1((hy - ly) + 1, ly); 265 | 266 | if (put_lregion_here(x,y,nlx,nly,nhx,nhy,rtype,oneshot,lev)) 267 | return; 268 | } 269 | 270 | /* then a deterministic one */ 271 | 272 | oneshot = TRUE; 273 | for (x = lx; x <= hx; x++) 274 | for (y = ly; y <= hy; y++) 275 | if (put_lregion_here(x,y,nlx,nly,nhx,nhy,rtype,oneshot,lev)) 276 | return; 277 | 278 | impossible("Couldn't place lregion type %d!", rtype); 279 | } 280 | 281 | STATIC_OVL boolean 282 | put_lregion_here(x,y,nlx,nly,nhx,nhy,rtype,oneshot,lev) 283 | xchar x, y; 284 | xchar nlx, nly, nhx, nhy; 285 | xchar rtype; 286 | boolean oneshot; 287 | d_level *lev; 288 | { 289 | if(oneshot) { 290 | /* must make due with the only location possible */ 291 | /* avoid failure due to a misplaced trap */ 292 | /* it might still fail if there's a dungeon feature here */ 293 | struct trap *t = t_at(x,y); 294 | if (t) deltrap(t); 295 | } 296 | if(bad_location(x, y, nlx, nly, nhx, nhy)) return(FALSE); 297 | switch (rtype) { 298 | case LR_TELE: 299 | case LR_UPTELE: 300 | case LR_DOWNTELE: 301 | /* "something" means the player in this case */ 302 | if(MON_AT(x, y)) { 303 | /* move the monster if no choice, or just try again */ 304 | if(oneshot) rloc(m_at(x,y)); 305 | else return(FALSE); 306 | } 307 | u_on_newpos(x, y); 308 | break; 309 | case LR_PORTAL: 310 | mkportal(x, y, lev->dnum, lev->dlevel); 311 | break; 312 | case LR_DOWNSTAIR: 313 | case LR_UPSTAIR: 314 | mkstairs(x, y, (char)rtype, (struct mkroom *)0); 315 | break; 316 | case LR_BRANCH: 317 | place_branch(Is_branchlev(&u.uz), x, y); 318 | break; 319 | } 320 | return(TRUE); 321 | } 322 | 323 | static boolean was_waterlevel; /* ugh... this shouldn't be needed */ 324 | 325 | /* this is special stuff that the level compiler cannot (yet) handle */ 326 | STATIC_OVL void 327 | fixup_special() 328 | { 329 | register lev_region *r = lregions; 330 | struct d_level lev; 331 | register int x, y; 332 | struct mkroom *croom; 333 | boolean added_branch = FALSE; 334 | 335 | if (was_waterlevel) { 336 | was_waterlevel = FALSE; 337 | u.uinwater = 0; 338 | unsetup_waterlevel(); 339 | } else if (Is_waterlevel(&u.uz)) { 340 | level.flags.hero_memory = 0; 341 | was_waterlevel = TRUE; 342 | /* water level is an odd beast - it has to be set up 343 | before calling place_lregions etc. */ 344 | setup_waterlevel(); 345 | } 346 | for(x = 0; x < num_lregions; x++, r++) { 347 | switch(r->rtype) { 348 | case LR_BRANCH: 349 | added_branch = TRUE; 350 | goto place_it; 351 | 352 | case LR_PORTAL: 353 | if(*r->rname.str >= '0' && *r->rname.str <= '9') { 354 | /* "chutes and ladders" */ 355 | lev = u.uz; 356 | lev.dlevel = atoi(r->rname.str); 357 | } else { 358 | s_level *sp = find_level(r->rname.str); 359 | lev = sp->dlevel; 360 | } 361 | /* fall into... */ 362 | 363 | case LR_UPSTAIR: 364 | case LR_DOWNSTAIR: 365 | place_it: 366 | place_lregion(r->inarea.x1, r->inarea.y1, 367 | r->inarea.x2, r->inarea.y2, 368 | r->delarea.x1, r->delarea.y1, 369 | r->delarea.x2, r->delarea.y2, 370 | r->rtype, &lev); 371 | break; 372 | 373 | case LR_TELE: 374 | case LR_UPTELE: 375 | case LR_DOWNTELE: 376 | /* save the region outlines for goto_level() */ 377 | if(r->rtype == LR_TELE || r->rtype == LR_UPTELE) { 378 | updest.lx = r->inarea.x1; updest.ly = r->inarea.y1; 379 | updest.hx = r->inarea.x2; updest.hy = r->inarea.y2; 380 | updest.nlx = r->delarea.x1; updest.nly = r->delarea.y1; 381 | updest.nhx = r->delarea.x2; updest.nhy = r->delarea.y2; 382 | } 383 | if(r->rtype == LR_TELE || r->rtype == LR_DOWNTELE) { 384 | dndest.lx = r->inarea.x1; dndest.ly = r->inarea.y1; 385 | dndest.hx = r->inarea.x2; dndest.hy = r->inarea.y2; 386 | dndest.nlx = r->delarea.x1; dndest.nly = r->delarea.y1; 387 | dndest.nhx = r->delarea.x2; dndest.nhy = r->delarea.y2; 388 | } 389 | /* place_lregion gets called from goto_level() */ 390 | break; 391 | } 392 | 393 | if (r->rname.str) free((genericptr_t) r->rname.str), r->rname.str = 0; 394 | } 395 | 396 | /* place dungeon branch if not placed above */ 397 | if (!added_branch && Is_branchlev(&u.uz)) { 398 | place_lregion(0,0,0,0,0,0,0,0,LR_BRANCH,(d_level *)0); 399 | } 400 | 401 | /* KMH -- Sokoban levels */ 402 | if(In_sokoban(&u.uz)) 403 | sokoban_detect(); 404 | 405 | /* Still need to add some stuff to level file */ 406 | if (Is_medusa_level(&u.uz)) { 407 | struct obj *otmp; 408 | int tryct; 409 | 410 | croom = &rooms[0]; /* only one room on the medusa level */ 411 | for (tryct = rnd(4); tryct; tryct--) { 412 | x = somex(croom); y = somey(croom); 413 | if (goodpos(x, y, (struct monst *)0)) { 414 | otmp = mk_tt_object(STATUE, x, y); 415 | while (otmp && (poly_when_stoned(&mons[otmp->corpsenm]) || 416 | pm_resistance(&mons[otmp->corpsenm],MR_STONE))) { 417 | otmp->corpsenm = rndmonnum(); 418 | otmp->owt = weight(otmp); 419 | } 420 | } 421 | } 422 | 423 | if (rn2(2)) 424 | otmp = mk_tt_object(STATUE, somex(croom), somey(croom)); 425 | else /* Medusa statues don't contain books */ 426 | otmp = mkcorpstat(STATUE, (struct monst *)0, (struct permonst *)0, 427 | somex(croom), somey(croom), FALSE); 428 | if (otmp) { 429 | while (pm_resistance(&mons[otmp->corpsenm],MR_STONE) 430 | || poly_when_stoned(&mons[otmp->corpsenm])) { 431 | otmp->corpsenm = rndmonnum(); 432 | otmp->owt = weight(otmp); 433 | } 434 | } 435 | } else if(Is_wiz1_level(&u.uz)) { 436 | croom = search_special(MORGUE); 437 | 438 | create_secret_door(croom, W_SOUTH|W_EAST|W_WEST); 439 | } else if(Is_knox(&u.uz)) { 440 | /* using an unfilled morgue for rm id */ 441 | croom = search_special(MORGUE); 442 | /* avoid inappropriate morgue-related messages */ 443 | level.flags.graveyard = level.flags.has_morgue = 0; 444 | croom->rtype = OROOM; /* perhaps it should be set to VAULT? */ 445 | /* stock the main vault */ 446 | for(x = croom->lx; x <= croom->hx; x++) 447 | for(y = croom->ly; y <= croom->hy; y++) { 448 | (void) mkgold((long) rn1(300, 600), x, y); 449 | if (!rn2(3) && !is_pool(x,y)) 450 | (void)maketrap(x, y, rn2(3) ? LANDMINE : SPIKED_PIT); 451 | } 452 | } else if (Role_if(PM_PRIEST) && In_quest(&u.uz)) { 453 | /* less chance for undead corpses (lured from lower morgues) */ 454 | level.flags.graveyard = 1; 455 | } else if (Is_stronghold(&u.uz)) { 456 | level.flags.graveyard = 1; 457 | } else if(Is_sanctum(&u.uz)) { 458 | croom = search_special(TEMPLE); 459 | 460 | create_secret_door(croom, W_ANY); 461 | } else if(on_level(&u.uz, &orcus_level)) { 462 | register struct monst *mtmp, *mtmp2; 463 | 464 | /* it's a ghost town, get rid of shopkeepers */ 465 | for(mtmp = fmon; mtmp; mtmp = mtmp2) { 466 | mtmp2 = mtmp->nmon; 467 | if(mtmp->isshk) mongone(mtmp); 468 | } 469 | } 470 | 471 | if(lev_message) { 472 | char *str, *nl; 473 | for(str = lev_message; (nl = index(str, '\n')) != 0; str = nl+1) { 474 | *nl = '\0'; 475 | pline("%s", str); 476 | } 477 | if(*str) 478 | pline("%s", str); 479 | free((genericptr_t)lev_message); 480 | lev_message = 0; 481 | } 482 | 483 | if (lregions) 484 | free((genericptr_t) lregions), lregions = 0; 485 | num_lregions = 0; 486 | } 487 | 488 | void 489 | makemaz(s) 490 | register const char *s; 491 | { 492 | int x,y; 493 | char protofile[20]; 494 | s_level *sp = Is_special(&u.uz); 495 | coord mm; 496 | 497 | if(*s) { 498 | if(sp && sp->rndlevs) Sprintf(protofile, "%s-%d", s, 499 | rnd((int) sp->rndlevs)); 500 | else Strcpy(protofile, s); 501 | } else if(*(dungeons[u.uz.dnum].proto)) { 502 | if(dunlevs_in_dungeon(&u.uz) > 1) { 503 | if(sp && sp->rndlevs) 504 | Sprintf(protofile, "%s%d-%d", dungeons[u.uz.dnum].proto, 505 | dunlev(&u.uz), 506 | rnd((int) sp->rndlevs)); 507 | else Sprintf(protofile, "%s%d", dungeons[u.uz.dnum].proto, 508 | dunlev(&u.uz)); 509 | } else if(sp && sp->rndlevs) { 510 | Sprintf(protofile, "%s-%d", dungeons[u.uz.dnum].proto, 511 | rnd((int) sp->rndlevs)); 512 | } else Strcpy(protofile, dungeons[u.uz.dnum].proto); 513 | 514 | } else Strcpy(protofile, ""); 515 | 516 | if(*protofile) { 517 | Strcat(protofile, LEV_EXT); 518 | if(load_special(protofile)) { 519 | fixup_special(); 520 | /* some levels can end up with monsters 521 | on dead mon list, including light source monsters */ 522 | dmonsfree(); 523 | return; /* no mazification right now */ 524 | } 525 | impossible("Couldn't load \"%s\" - making a maze.", protofile); 526 | } 527 | 528 | level.flags.is_maze_lev = TRUE; 529 | 530 | #ifndef WALLIFIED_MAZE 531 | for(x = 2; x < x_maze_max; x++) 532 | for(y = 2; y < y_maze_max; y++) 533 | levl[x][y].typ = STONE; 534 | #else 535 | for(x = 2; x <= x_maze_max; x++) 536 | for(y = 2; y <= y_maze_max; y++) 537 | levl[x][y].typ = ((x % 2) && (y % 2)) ? STONE : HWALL; 538 | #endif 539 | 540 | maze0xy(&mm); 541 | walkfrom((int) mm.x, (int) mm.y); 542 | /* put a boulder at the maze center */ 543 | (void) mksobj_at(BOULDER, (int) mm.x, (int) mm.y, TRUE); 544 | 545 | #ifdef WALLIFIED_MAZE 546 | wallification(2, 2, x_maze_max, y_maze_max); 547 | #endif 548 | mazexy(&mm); 549 | mkstairs(mm.x, mm.y, 1, (struct mkroom *)0); /* up */ 550 | if (!Invocation_lev(&u.uz)) { 551 | mazexy(&mm); 552 | mkstairs(mm.x, mm.y, 0, (struct mkroom *)0); /* down */ 553 | } else { /* choose "vibrating square" location */ 554 | #define x_maze_min 2 555 | #define y_maze_min 2 556 | /* 557 | * Pick a position where the stairs down to Moloch's Sanctum 558 | * level will ultimately be created. At that time, an area 559 | * will be altered: walls removed, moat and traps generated, 560 | * boulders destroyed. The position picked here must ensure 561 | * that that invocation area won't extend off the map. 562 | * 563 | * We actually allow up to 2 squares around the usual edge of 564 | * the area to get truncated; see mkinvokearea(mklev.c). 565 | */ 566 | #define INVPOS_X_MARGIN (6 - 2) 567 | #define INVPOS_Y_MARGIN (5 - 2) 568 | #define INVPOS_DISTANCE 11 569 | int x_range = x_maze_max - x_maze_min - 2*INVPOS_X_MARGIN - 1, 570 | y_range = y_maze_max - y_maze_min - 2*INVPOS_Y_MARGIN - 1; 571 | 572 | #ifdef DEBUG 573 | if (x_range <= INVPOS_X_MARGIN || y_range <= INVPOS_Y_MARGIN || 574 | (x_range * y_range) <= (INVPOS_DISTANCE * INVPOS_DISTANCE)) 575 | panic("inv_pos: maze is too small! (%d x %d)", 576 | x_maze_max, y_maze_max); 577 | #endif 578 | inv_pos.x = inv_pos.y = 0; /*{occupied() => invocation_pos()}*/ 579 | do { 580 | x = rn1(x_range, x_maze_min + INVPOS_X_MARGIN + 1); 581 | y = rn1(y_range, y_maze_min + INVPOS_Y_MARGIN + 1); 582 | /* we don't want it to be too near the stairs, nor 583 | to be on a spot that's already in use (wall|trap) */ 584 | } while (x == xupstair || y == yupstair || /*(direct line)*/ 585 | abs(x - xupstair) == abs(y - yupstair) || 586 | distmin(x, y, xupstair, yupstair) <= INVPOS_DISTANCE || 587 | !SPACE_POS(levl[x][y].typ) || occupied(x, y)); 588 | inv_pos.x = x; 589 | inv_pos.y = y; 590 | #undef INVPOS_X_MARGIN 591 | #undef INVPOS_Y_MARGIN 592 | #undef INVPOS_DISTANCE 593 | #undef x_maze_min 594 | #undef y_maze_min 595 | } 596 | 597 | /* place branch stair or portal */ 598 | place_branch(Is_branchlev(&u.uz), 0, 0); 599 | 600 | for(x = rn1(8,11); x; x--) { 601 | mazexy(&mm); 602 | (void) mkobj_at(rn2(2) ? GEM_CLASS : 0, mm.x, mm.y, TRUE); 603 | } 604 | for(x = rn1(10,2); x; x--) { 605 | mazexy(&mm); 606 | (void) mksobj_at(BOULDER, mm.x, mm.y, TRUE); 607 | } 608 | for (x = rn2(3); x; x--) { 609 | mazexy(&mm); 610 | (void) makemon(&mons[PM_MINOTAUR], mm.x, mm.y, NO_MM_FLAGS); 611 | } 612 | for(x = rn1(5,7); x; x--) { 613 | mazexy(&mm); 614 | (void) makemon((struct permonst *) 0, mm.x, mm.y, NO_MM_FLAGS); 615 | } 616 | for(x = rn1(6,7); x; x--) { 617 | mazexy(&mm); 618 | (void) mkgold(0L,mm.x,mm.y); 619 | } 620 | for(x = rn1(6,7); x; x--) 621 | mktrap(0,1,(struct mkroom *) 0, (coord*) 0); 622 | } 623 | 624 | #ifdef MICRO 625 | /* Make the mazewalk iterative by faking a stack. This is needed to 626 | * ensure the mazewalk is successful in the limited stack space of 627 | * the program. This iterative version uses the minimum amount of stack 628 | * that is totally safe. 629 | */ 630 | void 631 | walkfrom(x,y) 632 | int x,y; 633 | { 634 | #define CELLS (ROWNO * COLNO) / 4 /* a maze cell is 4 squares */ 635 | char mazex[CELLS + 1], mazey[CELLS + 1]; /* char's are OK */ 636 | int q, a, dir, pos; 637 | int dirs[4]; 638 | 639 | pos = 1; 640 | mazex[pos] = (char) x; 641 | mazey[pos] = (char) y; 642 | while (pos) { 643 | x = (int) mazex[pos]; 644 | y = (int) mazey[pos]; 645 | if(!IS_DOOR(levl[x][y].typ)) { 646 | /* might still be on edge of MAP, so don't overwrite */ 647 | #ifndef WALLIFIED_MAZE 648 | levl[x][y].typ = CORR; 649 | #else 650 | levl[x][y].typ = ROOM; 651 | #endif 652 | levl[x][y].flags = 0; 653 | } 654 | q = 0; 655 | for (a = 0; a < 4; a++) 656 | if(okay(x, y, a)) dirs[q++]= a; 657 | if (!q) 658 | pos--; 659 | else { 660 | dir = dirs[rn2(q)]; 661 | move(&x, &y, dir); 662 | #ifndef WALLIFIED_MAZE 663 | levl[x][y].typ = CORR; 664 | #else 665 | levl[x][y].typ = ROOM; 666 | #endif 667 | move(&x, &y, dir); 668 | pos++; 669 | if (pos > CELLS) 670 | panic("Overflow in walkfrom"); 671 | mazex[pos] = (char) x; 672 | mazey[pos] = (char) y; 673 | } 674 | } 675 | } 676 | #else 677 | 678 | void 679 | walkfrom(x,y) 680 | int x,y; 681 | { 682 | register int q,a,dir; 683 | int dirs[4]; 684 | 685 | if(!IS_DOOR(levl[x][y].typ)) { 686 | /* might still be on edge of MAP, so don't overwrite */ 687 | #ifndef WALLIFIED_MAZE 688 | levl[x][y].typ = CORR; 689 | #else 690 | levl[x][y].typ = ROOM; 691 | #endif 692 | levl[x][y].flags = 0; 693 | } 694 | 695 | while(1) { 696 | q = 0; 697 | for(a = 0; a < 4; a++) 698 | if(okay(x,y,a)) dirs[q++]= a; 699 | if(!q) return; 700 | dir = dirs[rn2(q)]; 701 | move(&x,&y,dir); 702 | #ifndef WALLIFIED_MAZE 703 | levl[x][y].typ = CORR; 704 | #else 705 | levl[x][y].typ = ROOM; 706 | #endif 707 | move(&x,&y,dir); 708 | walkfrom(x,y); 709 | } 710 | } 711 | #endif /* MICRO */ 712 | 713 | STATIC_OVL void 714 | move(x,y,dir) 715 | register int *x, *y; 716 | register int dir; 717 | { 718 | switch(dir){ 719 | case 0: --(*y); break; 720 | case 1: (*x)++; break; 721 | case 2: (*y)++; break; 722 | case 3: --(*x); break; 723 | default: panic("move: bad direction"); 724 | } 725 | } 726 | 727 | void 728 | mazexy(cc) /* find random point in generated corridors, 729 | so we don't create items in moats, bunkers, or walls */ 730 | coord *cc; 731 | { 732 | int cpt=0; 733 | 734 | do { 735 | cc->x = 3 + 2*rn2((x_maze_max>>1) - 1); 736 | cc->y = 3 + 2*rn2((y_maze_max>>1) - 1); 737 | cpt++; 738 | } while (cpt < 100 && levl[cc->x][cc->y].typ != 739 | #ifdef WALLIFIED_MAZE 740 | ROOM 741 | #else 742 | CORR 743 | #endif 744 | ); 745 | if (cpt >= 100) { 746 | register int x, y; 747 | /* last try */ 748 | for (x = 0; x < (x_maze_max>>1) - 1; x++) 749 | for (y = 0; y < (y_maze_max>>1) - 1; y++) { 750 | cc->x = 3 + 2 * x; 751 | cc->y = 3 + 2 * y; 752 | if (levl[cc->x][cc->y].typ == 753 | #ifdef WALLIFIED_MAZE 754 | ROOM 755 | #else 756 | CORR 757 | #endif 758 | ) return; 759 | } 760 | panic("mazexy: can't find a place!"); 761 | } 762 | return; 763 | } 764 | 765 | void 766 | bound_digging() 767 | /* put a non-diggable boundary around the initial portion of a level map. 768 | * assumes that no level will initially put things beyond the isok() range. 769 | * 770 | * we can't bound unconditionally on the last line with something in it, 771 | * because that something might be a niche which was already reachable, 772 | * so the boundary would be breached 773 | * 774 | * we can't bound unconditionally on one beyond the last line, because 775 | * that provides a window of abuse for WALLIFIED_MAZE special levels 776 | */ 777 | { 778 | register int x,y; 779 | register unsigned typ; 780 | register struct rm *lev; 781 | boolean found, nonwall; 782 | int xmin,xmax,ymin,ymax; 783 | 784 | if(Is_earthlevel(&u.uz)) return; /* everything diggable here */ 785 | 786 | found = nonwall = FALSE; 787 | for(xmin=0; !found; xmin++) { 788 | lev = &levl[xmin][0]; 789 | for(y=0; y<=ROWNO-1; y++, lev++) { 790 | typ = lev->typ; 791 | if(typ != STONE) { 792 | found = TRUE; 793 | if(!IS_WALL(typ)) nonwall = TRUE; 794 | } 795 | } 796 | } 797 | xmin -= (nonwall || !level.flags.is_maze_lev) ? 2 : 1; 798 | if (xmin < 0) xmin = 0; 799 | 800 | found = nonwall = FALSE; 801 | for(xmax=COLNO-1; !found; xmax--) { 802 | lev = &levl[xmax][0]; 803 | for(y=0; y<=ROWNO-1; y++, lev++) { 804 | typ = lev->typ; 805 | if(typ != STONE) { 806 | found = TRUE; 807 | if(!IS_WALL(typ)) nonwall = TRUE; 808 | } 809 | } 810 | } 811 | xmax += (nonwall || !level.flags.is_maze_lev) ? 2 : 1; 812 | if (xmax >= COLNO) xmax = COLNO-1; 813 | 814 | found = nonwall = FALSE; 815 | for(ymin=0; !found; ymin++) { 816 | lev = &levl[xmin][ymin]; 817 | for(x=xmin; x<=xmax; x++, lev += ROWNO) { 818 | typ = lev->typ; 819 | if(typ != STONE) { 820 | found = TRUE; 821 | if(!IS_WALL(typ)) nonwall = TRUE; 822 | } 823 | } 824 | } 825 | ymin -= (nonwall || !level.flags.is_maze_lev) ? 2 : 1; 826 | 827 | found = nonwall = FALSE; 828 | for(ymax=ROWNO-1; !found; ymax--) { 829 | lev = &levl[xmin][ymax]; 830 | for(x=xmin; x<=xmax; x++, lev += ROWNO) { 831 | typ = lev->typ; 832 | if(typ != STONE) { 833 | found = TRUE; 834 | if(!IS_WALL(typ)) nonwall = TRUE; 835 | } 836 | } 837 | } 838 | ymax += (nonwall || !level.flags.is_maze_lev) ? 2 : 1; 839 | 840 | for (x = 0; x < COLNO; x++) 841 | for (y = 0; y < ROWNO; y++) 842 | if (y <= ymin || y >= ymax || x <= xmin || x >= xmax) { 843 | #ifdef DCC30_BUG 844 | lev = &levl[x][y]; 845 | lev->wall_info |= W_NONDIGGABLE; 846 | #else 847 | levl[x][y].wall_info |= W_NONDIGGABLE; 848 | #endif 849 | } 850 | } 851 | 852 | void 853 | mkportal(x, y, todnum, todlevel) 854 | register xchar x, y, todnum, todlevel; 855 | { 856 | /* a portal "trap" must be matched by a */ 857 | /* portal in the destination dungeon/dlevel */ 858 | register struct trap *ttmp = maketrap(x, y, MAGIC_PORTAL); 859 | 860 | if (!ttmp) { 861 | impossible("portal on top of portal??"); 862 | return; 863 | } 864 | #ifdef DEBUG 865 | pline("mkportal: at (%d,%d), to %s, level %d", 866 | x, y, dungeons[todnum].dname, todlevel); 867 | #endif 868 | ttmp->dst.dnum = todnum; 869 | ttmp->dst.dlevel = todlevel; 870 | return; 871 | } 872 | 873 | /* 874 | * Special waterlevel stuff in endgame (TH). 875 | * 876 | * Some of these functions would probably logically belong to some 877 | * other source files, but they are all so nicely encapsulated here. 878 | */ 879 | 880 | /* to ease the work of debuggers at this stage */ 881 | #define register 882 | 883 | struct container { 884 | struct container *next; 885 | xchar x, y; 886 | short what; 887 | genericptr_t list; 888 | }; 889 | #define CONS_OBJ 0 890 | #define CONS_MON 1 891 | #define CONS_HERO 2 892 | #define CONS_TRAP 3 893 | 894 | static struct bubble { 895 | xchar x, y; /* coordinates of the upper left corner */ 896 | schar dx, dy; /* the general direction of the bubble's movement */ 897 | uchar *bm; /* pointer to the bubble bit mask */ 898 | struct bubble *prev, *next; /* need to traverse the list up and down */ 899 | struct container *cons; 900 | } *bbubbles, *ebubbles; 901 | 902 | static struct trap *wportal; 903 | static int xmin, ymin, xmax, ymax; /* level boundaries */ 904 | /* bubble movement boundaries */ 905 | #define bxmin (xmin + 1) 906 | #define bymin (ymin + 1) 907 | #define bxmax (xmax - 1) 908 | #define bymax (ymax - 1) 909 | 910 | STATIC_DCL void NDECL(set_wportal); 911 | STATIC_DCL void FDECL(mk_bubble, (int,int,int)); 912 | STATIC_DCL void FDECL(mv_bubble, (struct bubble *,int,int,BOOLEAN_P)); 913 | 914 | void 915 | movebubbles() 916 | { 917 | static boolean up; 918 | register struct bubble *b; 919 | register int x, y, i, j; 920 | struct trap *btrap; 921 | static const struct rm water_pos = 922 | { cmap_to_glyph(S_water), WATER, 0, 0, 0, 0, 0, 0, 0 }; 923 | 924 | /* set up the portal the first time bubbles are moved */ 925 | if (!wportal) set_wportal(); 926 | 927 | vision_recalc(2); 928 | /* keep attached ball&chain separate from bubble objects */ 929 | if (Punished) unplacebc(); 930 | 931 | /* 932 | * Pick up everything inside of a bubble then fill all bubble 933 | * locations. 934 | */ 935 | 936 | for (b = up ? bbubbles : ebubbles; b; b = up ? b->next : b->prev) { 937 | if (b->cons) panic("movebubbles: cons != null"); 938 | for (i = 0, x = b->x; i < (int) b->bm[0]; i++, x++) 939 | for (j = 0, y = b->y; j < (int) b->bm[1]; j++, y++) 940 | if (b->bm[j + 2] & (1 << i)) { 941 | if (!isok(x,y)) { 942 | impossible("movebubbles: bad pos (%d,%d)", x,y); 943 | continue; 944 | } 945 | 946 | /* pick up objects, monsters, hero, and traps */ 947 | if (OBJ_AT(x,y)) { 948 | struct obj *olist = (struct obj *) 0, *otmp; 949 | struct container *cons = (struct container *) 950 | alloc(sizeof(struct container)); 951 | 952 | while ((otmp = level.objects[x][y]) != 0) { 953 | remove_object(otmp); 954 | otmp->ox = otmp->oy = 0; 955 | otmp->nexthere = olist; 956 | olist = otmp; 957 | } 958 | 959 | cons->x = x; 960 | cons->y = y; 961 | cons->what = CONS_OBJ; 962 | cons->list = (genericptr_t) olist; 963 | cons->next = b->cons; 964 | b->cons = cons; 965 | } 966 | if (MON_AT(x,y)) { 967 | struct monst *mon = m_at(x,y); 968 | struct container *cons = (struct container *) 969 | alloc(sizeof(struct container)); 970 | 971 | cons->x = x; 972 | cons->y = y; 973 | cons->what = CONS_MON; 974 | cons->list = (genericptr_t) mon; 975 | 976 | cons->next = b->cons; 977 | b->cons = cons; 978 | 979 | if(mon->wormno) 980 | remove_worm(mon); 981 | else 982 | remove_monster(x, y); 983 | 984 | newsym(x,y); /* clean up old position */ 985 | mon->mx = mon->my = 0; 986 | } 987 | if (!u.uswallow && x == u.ux && y == u.uy) { 988 | struct container *cons = (struct container *) 989 | alloc(sizeof(struct container)); 990 | 991 | cons->x = x; 992 | cons->y = y; 993 | cons->what = CONS_HERO; 994 | cons->list = (genericptr_t) 0; 995 | 996 | cons->next = b->cons; 997 | b->cons = cons; 998 | } 999 | if ((btrap = t_at(x,y)) != 0) { 1000 | struct container *cons = (struct container *) 1001 | alloc(sizeof(struct container)); 1002 | 1003 | cons->x = x; 1004 | cons->y = y; 1005 | cons->what = CONS_TRAP; 1006 | cons->list = (genericptr_t) btrap; 1007 | 1008 | cons->next = b->cons; 1009 | b->cons = cons; 1010 | } 1011 | 1012 | levl[x][y] = water_pos; 1013 | block_point(x,y); 1014 | } 1015 | } 1016 | 1017 | /* 1018 | * Every second time traverse down. This is because otherwise 1019 | * all the junk that changes owners when bubbles overlap 1020 | * would eventually end up in the last bubble in the chain. 1021 | */ 1022 | 1023 | up = !up; 1024 | for (b = up ? bbubbles : ebubbles; b; b = up ? b->next : b->prev) { 1025 | register int rx = rn2(3), ry = rn2(3); 1026 | 1027 | mv_bubble(b,b->dx + 1 - (!b->dx ? rx : (rx ? 1 : 0)), 1028 | b->dy + 1 - (!b->dy ? ry : (ry ? 1 : 0)), 1029 | FALSE); 1030 | } 1031 | 1032 | /* put attached ball&chain back */ 1033 | if (Punished) placebc(); 1034 | vision_full_recalc = 1; 1035 | } 1036 | 1037 | /* when moving in water, possibly (1 in 3) alter the intended destination */ 1038 | void 1039 | water_friction() 1040 | { 1041 | register int x, y, dx, dy; 1042 | register boolean eff = FALSE; 1043 | 1044 | if (Swimming && rn2(4)) 1045 | return; /* natural swimmers have advantage */ 1046 | 1047 | if (u.dx && !rn2(!u.dy ? 3 : 6)) { /* 1/3 chance or half that */ 1048 | /* cancel delta x and choose an arbitrary delta y value */ 1049 | x = u.ux; 1050 | do { 1051 | dy = rn2(3) - 1; /* -1, 0, 1 */ 1052 | y = u.uy + dy; 1053 | } while (dy && (!isok(x,y) || !is_pool(x,y))); 1054 | u.dx = 0; 1055 | u.dy = dy; 1056 | eff = TRUE; 1057 | } else if (u.dy && !rn2(!u.dx ? 3 : 5)) { /* 1/3 or 1/5*(5/6) */ 1058 | /* cancel delta y and choose an arbitrary delta x value */ 1059 | y = u.uy; 1060 | do { 1061 | dx = rn2(3) - 1; /* -1 .. 1 */ 1062 | x = u.ux + dx; 1063 | } while (dx && (!isok(x,y) || !is_pool(x,y))); 1064 | u.dy = 0; 1065 | u.dx = dx; 1066 | eff = TRUE; 1067 | } 1068 | if (eff) pline("Water turbulence affects your movements."); 1069 | } 1070 | 1071 | void 1072 | save_waterlevel(fd, mode) 1073 | int fd, mode; 1074 | { 1075 | register struct bubble *b; 1076 | 1077 | if (!Is_waterlevel(&u.uz)) return; 1078 | 1079 | if (perform_bwrite(mode)) { 1080 | int n = 0; 1081 | for (b = bbubbles; b; b = b->next) ++n; 1082 | bwrite(fd, (genericptr_t)&n, sizeof (int)); 1083 | bwrite(fd, (genericptr_t)&xmin, sizeof (int)); 1084 | bwrite(fd, (genericptr_t)&ymin, sizeof (int)); 1085 | bwrite(fd, (genericptr_t)&xmax, sizeof (int)); 1086 | bwrite(fd, (genericptr_t)&ymax, sizeof (int)); 1087 | for (b = bbubbles; b; b = b->next) 1088 | bwrite(fd, (genericptr_t)b, sizeof (struct bubble)); 1089 | } 1090 | if (release_data(mode)) 1091 | unsetup_waterlevel(); 1092 | } 1093 | 1094 | void 1095 | restore_waterlevel(fd) 1096 | register int fd; 1097 | { 1098 | register struct bubble *b = (struct bubble *)0, *btmp; 1099 | register int i; 1100 | int n; 1101 | 1102 | if (!Is_waterlevel(&u.uz)) return; 1103 | 1104 | set_wportal(); 1105 | mread(fd,(genericptr_t)&n,sizeof(int)); 1106 | mread(fd,(genericptr_t)&xmin,sizeof(int)); 1107 | mread(fd,(genericptr_t)&ymin,sizeof(int)); 1108 | mread(fd,(genericptr_t)&xmax,sizeof(int)); 1109 | mread(fd,(genericptr_t)&ymax,sizeof(int)); 1110 | for (i = 0; i < n; i++) { 1111 | btmp = b; 1112 | b = (struct bubble *)alloc(sizeof(struct bubble)); 1113 | mread(fd,(genericptr_t)b,sizeof(struct bubble)); 1114 | if (bbubbles) { 1115 | btmp->next = b; 1116 | b->prev = btmp; 1117 | } else { 1118 | bbubbles = b; 1119 | b->prev = (struct bubble *)0; 1120 | } 1121 | mv_bubble(b,0,0,TRUE); 1122 | } 1123 | ebubbles = b; 1124 | b->next = (struct bubble *)0; 1125 | was_waterlevel = TRUE; 1126 | } 1127 | 1128 | STATIC_OVL void 1129 | set_wportal() 1130 | { 1131 | /* there better be only one magic portal on water level... */ 1132 | for (wportal = ftrap; wportal; wportal = wportal->ntrap) 1133 | if (wportal->ttyp == MAGIC_PORTAL) return; 1134 | impossible("set_wportal(): no portal!"); 1135 | } 1136 | 1137 | STATIC_OVL void 1138 | setup_waterlevel() 1139 | { 1140 | register int x, y; 1141 | register int xskip, yskip; 1142 | register int water_glyph = cmap_to_glyph(S_water); 1143 | 1144 | /* ouch, hardcoded... */ 1145 | 1146 | xmin = 3; 1147 | ymin = 1; 1148 | xmax = 78; 1149 | ymax = 20; 1150 | 1151 | /* set hero's memory to water */ 1152 | 1153 | for (x = xmin; x <= xmax; x++) 1154 | for (y = ymin; y <= ymax; y++) 1155 | levl[x][y].glyph = water_glyph; 1156 | 1157 | /* make bubbles */ 1158 | 1159 | xskip = 10 + rn2(10); 1160 | yskip = 4 + rn2(4); 1161 | for (x = bxmin; x <= bxmax; x += xskip) 1162 | for (y = bymin; y <= bymax; y += yskip) 1163 | mk_bubble(x,y,rn2(7)); 1164 | } 1165 | 1166 | STATIC_OVL void 1167 | unsetup_waterlevel() 1168 | { 1169 | register struct bubble *b, *bb; 1170 | 1171 | /* free bubbles */ 1172 | 1173 | for (b = bbubbles; b; b = bb) { 1174 | bb = b->next; 1175 | free((genericptr_t)b); 1176 | } 1177 | bbubbles = ebubbles = (struct bubble *)0; 1178 | } 1179 | 1180 | STATIC_OVL void 1181 | mk_bubble(x,y,n) 1182 | register int x, y, n; 1183 | { 1184 | /* 1185 | * These bit masks make visually pleasing bubbles on a normal aspect 1186 | * 25x80 terminal, which naturally results in them being mathematically 1187 | * anything but symmetric. For this reason they cannot be computed 1188 | * in situ, either. The first two elements tell the dimensions of 1189 | * the bubble's bounding box. 1190 | */ 1191 | static uchar 1192 | bm2[] = {2,1,0x3}, 1193 | bm3[] = {3,2,0x7,0x7}, 1194 | bm4[] = {4,3,0x6,0xf,0x6}, 1195 | bm5[] = {5,3,0xe,0x1f,0xe}, 1196 | bm6[] = {6,4,0x1e,0x3f,0x3f,0x1e}, 1197 | bm7[] = {7,4,0x3e,0x7f,0x7f,0x3e}, 1198 | bm8[] = {8,4,0x7e,0xff,0xff,0x7e}, 1199 | *bmask[] = {bm2,bm3,bm4,bm5,bm6,bm7,bm8}; 1200 | 1201 | register struct bubble *b; 1202 | 1203 | if (x >= bxmax || y >= bymax) return; 1204 | if (n >= SIZE(bmask)) { 1205 | impossible("n too large (mk_bubble)"); 1206 | n = SIZE(bmask) - 1; 1207 | } 1208 | b = (struct bubble *)alloc(sizeof(struct bubble)); 1209 | if ((x + (int) bmask[n][0] - 1) > bxmax) x = bxmax - bmask[n][0] + 1; 1210 | if ((y + (int) bmask[n][1] - 1) > bymax) y = bymax - bmask[n][1] + 1; 1211 | b->x = x; 1212 | b->y = y; 1213 | b->dx = 1 - rn2(3); 1214 | b->dy = 1 - rn2(3); 1215 | b->bm = bmask[n]; 1216 | b->cons = 0; 1217 | if (!bbubbles) bbubbles = b; 1218 | if (ebubbles) { 1219 | ebubbles->next = b; 1220 | b->prev = ebubbles; 1221 | } 1222 | else 1223 | b->prev = (struct bubble *)0; 1224 | b->next = (struct bubble *)0; 1225 | ebubbles = b; 1226 | mv_bubble(b,0,0,TRUE); 1227 | } 1228 | 1229 | /* 1230 | * The player, the portal and all other objects and monsters 1231 | * float along with their associated bubbles. Bubbles may overlap 1232 | * freely, and the contents may get associated with other bubbles in 1233 | * the process. Bubbles are "sticky", meaning that if the player is 1234 | * in the immediate neighborhood of one, he/she may get sucked inside. 1235 | * This property also makes leaving a bubble slightly difficult. 1236 | */ 1237 | STATIC_OVL void 1238 | mv_bubble(b,dx,dy,ini) 1239 | register struct bubble *b; 1240 | register int dx, dy; 1241 | register boolean ini; 1242 | { 1243 | register int x, y, i, j, colli = 0; 1244 | struct container *cons, *ctemp; 1245 | 1246 | /* move bubble */ 1247 | if (dx < -1 || dx > 1 || dy < -1 || dy > 1) { 1248 | /* pline("mv_bubble: dx = %d, dy = %d", dx, dy); */ 1249 | dx = sgn(dx); 1250 | dy = sgn(dy); 1251 | } 1252 | 1253 | /* 1254 | * collision with level borders? 1255 | * 1 = horizontal border, 2 = vertical, 3 = corner 1256 | */ 1257 | if (b->x <= bxmin) colli |= 2; 1258 | if (b->y <= bymin) colli |= 1; 1259 | if ((int) (b->x + b->bm[0] - 1) >= bxmax) colli |= 2; 1260 | if ((int) (b->y + b->bm[1] - 1) >= bymax) colli |= 1; 1261 | 1262 | if (b->x < bxmin) { 1263 | pline("bubble xmin: x = %d, xmin = %d", b->x, bxmin); 1264 | b->x = bxmin; 1265 | } 1266 | if (b->y < bymin) { 1267 | pline("bubble ymin: y = %d, ymin = %d", b->y, bymin); 1268 | b->y = bymin; 1269 | } 1270 | if ((int) (b->x + b->bm[0] - 1) > bxmax) { 1271 | pline("bubble xmax: x = %d, xmax = %d", 1272 | b->x + b->bm[0] - 1, bxmax); 1273 | b->x = bxmax - b->bm[0] + 1; 1274 | } 1275 | if ((int) (b->y + b->bm[1] - 1) > bymax) { 1276 | pline("bubble ymax: y = %d, ymax = %d", 1277 | b->y + b->bm[1] - 1, bymax); 1278 | b->y = bymax - b->bm[1] + 1; 1279 | } 1280 | 1281 | /* bounce if we're trying to move off the border */ 1282 | if (b->x == bxmin && dx < 0) dx = -dx; 1283 | if (b->x + b->bm[0] - 1 == bxmax && dx > 0) dx = -dx; 1284 | if (b->y == bymin && dy < 0) dy = -dy; 1285 | if (b->y + b->bm[1] - 1 == bymax && dy > 0) dy = -dy; 1286 | 1287 | b->x += dx; 1288 | b->y += dy; 1289 | 1290 | /* void positions inside bubble */ 1291 | 1292 | for (i = 0, x = b->x; i < (int) b->bm[0]; i++, x++) 1293 | for (j = 0, y = b->y; j < (int) b->bm[1]; j++, y++) 1294 | if (b->bm[j + 2] & (1 << i)) { 1295 | levl[x][y].typ = AIR; 1296 | levl[x][y].lit = 1; 1297 | unblock_point(x,y); 1298 | } 1299 | 1300 | /* replace contents of bubble */ 1301 | for (cons = b->cons; cons; cons = ctemp) { 1302 | ctemp = cons->next; 1303 | cons->x += dx; 1304 | cons->y += dy; 1305 | 1306 | switch(cons->what) { 1307 | case CONS_OBJ: { 1308 | struct obj *olist, *otmp; 1309 | 1310 | for (olist=(struct obj *)cons->list; olist; olist=otmp) { 1311 | otmp = olist->nexthere; 1312 | place_object(olist, cons->x, cons->y); 1313 | } 1314 | break; 1315 | } 1316 | 1317 | case CONS_MON: { 1318 | struct monst *mon = (struct monst *) cons->list; 1319 | (void) mnearto(mon, cons->x, cons->y, TRUE); 1320 | break; 1321 | } 1322 | 1323 | case CONS_HERO: { 1324 | int ux0 = u.ux, uy0 = u.uy; 1325 | 1326 | /* change u.ux0 and u.uy0? */ 1327 | u.ux = cons->x; 1328 | u.uy = cons->y; 1329 | newsym(ux0, uy0); /* clean up old position */ 1330 | 1331 | if (MON_AT(cons->x, cons->y)) { 1332 | mnexto(m_at(cons->x,cons->y)); 1333 | } 1334 | break; 1335 | } 1336 | 1337 | case CONS_TRAP: { 1338 | struct trap *btrap = (struct trap *) cons->list; 1339 | btrap->tx = cons->x; 1340 | btrap->ty = cons->y; 1341 | break; 1342 | } 1343 | 1344 | default: 1345 | impossible("mv_bubble: unknown bubble contents"); 1346 | break; 1347 | } 1348 | free((genericptr_t)cons); 1349 | } 1350 | b->cons = 0; 1351 | 1352 | /* boing? */ 1353 | 1354 | switch (colli) { 1355 | case 1: b->dy = -b->dy; break; 1356 | case 3: b->dy = -b->dy; /* fall through */ 1357 | case 2: b->dx = -b->dx; break; 1358 | default: 1359 | /* sometimes alter direction for fun anyway 1360 | (higher probability for stationary bubbles) */ 1361 | if (!ini && ((b->dx || b->dy) ? !rn2(20) : !rn2(5))) { 1362 | b->dx = 1 - rn2(3); 1363 | b->dy = 1 - rn2(3); 1364 | } 1365 | } 1366 | } 1367 | 1368 | /*mkmaze.c*/