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*/