1 /* SCCS Id: @(#)dbridge.c 3.4 2003/02/08 */
2 /* Copyright (c) 1989 by Jean-Christophe Collet */
3 /* NetHack may be freely redistributed. See license for details. */
6 * This file contains the drawbridge manipulation (create, open, close,
9 * Added comprehensive monster-handling, and the "entity" structure to
10 * deal with players as well. - 11/89
16 STATIC_DCL void FDECL(get_wall_for_db, (int *, int *));
17 STATIC_DCL struct entity *FDECL(e_at, (int, int));
18 STATIC_DCL void FDECL(m_to_e, (struct monst *, int, int, struct entity *));
19 STATIC_DCL void FDECL(u_to_e, (struct entity *));
20 STATIC_DCL void FDECL(set_entity, (int, int, struct entity *));
21 STATIC_DCL const char *FDECL(e_nam, (struct entity *));
23 static const char *FDECL(Enam, (struct entity *)); /* unused */
25 STATIC_DCL const char *FDECL(E_phrase, (struct entity *, const char *));
26 STATIC_DCL boolean FDECL(e_survives_at, (struct entity *, int, int));
27 STATIC_DCL void FDECL(e_died, (struct entity *, int, int));
28 STATIC_DCL boolean FDECL(automiss, (struct entity *));
29 STATIC_DCL boolean FDECL(e_missed, (struct entity *, BOOLEAN_P));
30 STATIC_DCL boolean FDECL(e_jumps, (struct entity *));
31 STATIC_DCL void FDECL(do_entity, (struct entity *));
42 if (!isok(x,y)) return FALSE;
43 ltyp = levl[x][y].typ;
44 if (ltyp == POOL || ltyp == MOAT || ltyp == WATER) return TRUE;
45 if (ltyp == DRAWBRIDGE_UP &&
46 (levl[x][y].drawbridgemask & DB_UNDER) == DB_MOAT) return TRUE;
56 if (!isok(x,y)) return FALSE;
57 ltyp = levl[x][y].typ;
59 || (ltyp == DRAWBRIDGE_UP
60 && (levl[x][y].drawbridgemask & DB_UNDER) == DB_LAVA)) return TRUE;
70 if (!isok(x,y)) return FALSE;
71 ltyp = levl[x][y].typ;
73 || (ltyp == DRAWBRIDGE_UP
74 && (levl[x][y].drawbridgemask & DB_UNDER) == DB_ICE)) return TRUE;
83 * We want to know whether a wall (or a door) is the portcullis (passageway)
84 * of an eventual drawbridge.
86 * Return value: the direction of the drawbridge.
90 is_drawbridge_wall(x,y)
96 if (lev->typ != DOOR && lev->typ != DBWALL)
99 if (IS_DRAWBRIDGE(levl[x+1][y].typ) &&
100 (levl[x+1][y].drawbridgemask & DB_DIR) == DB_WEST)
102 if (IS_DRAWBRIDGE(levl[x-1][y].typ) &&
103 (levl[x-1][y].drawbridgemask & DB_DIR) == DB_EAST)
105 if (IS_DRAWBRIDGE(levl[x][y-1].typ) &&
106 (levl[x][y-1].drawbridgemask & DB_DIR) == DB_SOUTH)
108 if (IS_DRAWBRIDGE(levl[x][y+1].typ) &&
109 (levl[x][y+1].drawbridgemask & DB_DIR) == DB_NORTH)
116 * Use is_db_wall where you want to verify that a
117 * drawbridge "wall" is UP in the location x, y
118 * (instead of UP or DOWN, as with is_drawbridge_wall).
124 return((boolean)( levl[x][y].typ == DBWALL ));
129 * Return true with x,y pointing to the drawbridge if x,y initially indicate
130 * a drawbridge or drawbridge wall.
138 if (IS_DRAWBRIDGE(levl[*x][*y].typ))
140 dir = is_drawbridge_wall(*x,*y);
143 case DB_NORTH: (*y)++; break;
144 case DB_SOUTH: (*y)--; break;
145 case DB_EAST: (*x)--; break;
146 case DB_WEST: (*x)++; break;
157 * Find the drawbridge wall associated with a drawbridge.
163 switch (levl[*x][*y].drawbridgemask & DB_DIR) {
164 case DB_NORTH: (*y)--; break;
165 case DB_SOUTH: (*y)++; break;
166 case DB_EAST: (*x)++; break;
167 case DB_WEST: (*x)--; break;
172 * Creation of a drawbridge at pos x,y.
173 * dir is the direction.
174 * flag must be put to TRUE if we want the drawbridge to be opened.
178 create_drawbridge(x,y,dir,flag)
184 boolean lava = levl[x][y].typ == LAVAPOOL; /* assume initialized map */
201 impossible("bad direction in create_drawbridge");
208 if (!IS_WALL(levl[x2][y2].typ))
210 if (flag) { /* We want the bridge open */
211 levl[x][y].typ = DRAWBRIDGE_DOWN;
212 levl[x2][y2].typ = DOOR;
213 levl[x2][y2].doormask = D_NODOOR;
215 levl[x][y].typ = DRAWBRIDGE_UP;
216 levl[x2][y2].typ = DBWALL;
217 /* Drawbridges are non-diggable. */
218 levl[x2][y2].wall_info = W_NONDIGGABLE;
220 levl[x][y].horizontal = !horiz;
221 levl[x2][y2].horizontal = horiz;
222 levl[x][y].drawbridgemask = dir;
223 if(lava) levl[x][y].drawbridgemask |= DB_LAVA;
228 struct monst *emon; /* youmonst for the player */
229 struct permonst *edata; /* must be non-zero for record to be valid */
235 static NEARDATA struct entity occupants[ENTITIES];
244 for (entitycnt = 0; entitycnt < ENTITIES; entitycnt++)
245 if ((occupants[entitycnt].edata) &&
246 (occupants[entitycnt].ex == x) &&
247 (occupants[entitycnt].ey == y))
250 pline("entitycnt = %d", entitycnt);
253 return((entitycnt == ENTITIES)?
254 (struct entity *)0 : &(occupants[entitycnt]));
258 m_to_e(mtmp, x, y, etmp)
267 if (mtmp->wormno && (x != mtmp->mx || y != mtmp->my))
268 etmp->edata = &mons[PM_LONG_WORM_TAIL];
270 etmp->edata = mtmp->data;
272 etmp->edata = (struct permonst *)0;
279 etmp->emon = &youmonst;
282 etmp->edata = youmonst.data;
286 set_entity(x, y, etmp)
290 if ((x == u.ux) && (y == u.uy))
292 else if (MON_AT(x, y))
293 m_to_e(m_at(x, y), x, y, etmp);
295 etmp->edata = (struct permonst *)0;
298 #define is_u(etmp) (etmp->emon == &youmonst)
299 #define e_canseemon(etmp) (is_u(etmp) ? (boolean)TRUE : canseemon(etmp->emon))
302 * e_strg is a utility routine which is not actually in use anywhere, since
303 * the specialized routines below suffice for all current purposes.
306 /* #define e_strg(etmp, func) (is_u(etmp)? (char *)0 : func(etmp->emon)) */
308 STATIC_OVL const char *
312 return(is_u(etmp)? "you" : mon_nam(etmp->emon));
317 * Enam is another unused utility routine: E_phrase is preferable.
324 return(is_u(etmp)? "You" : Monnam(etmp->emon));
329 * Generates capitalized entity name, makes 2nd -> 3rd person conversion on
330 * verb, where necessary.
333 STATIC_OVL const char *
338 static char wholebuf[80];
340 Strcpy(wholebuf, is_u(etmp) ? "You" : Monnam(etmp->emon));
341 if (!*verb) return(wholebuf);
342 Strcat(wholebuf, " ");
344 Strcat(wholebuf, verb);
346 Strcat(wholebuf, vtense((char *)0, verb));
351 * Simple-minded "can it be here?" routine
355 e_survives_at(etmp, x, y)
359 if (noncorporeal(etmp->edata))
362 return (boolean)((is_u(etmp) &&
363 (Wwalking || Amphibious || Swimming ||
364 Flying || Levitation)) ||
365 is_swimmer(etmp->edata) || is_flyer(etmp->edata) ||
366 is_floater(etmp->edata));
367 /* must force call to lava_effects in e_died if is_u */
369 return (boolean)((is_u(etmp) && (Levitation || Flying)) ||
370 likes_lava(etmp->edata) || is_flyer(etmp->edata));
371 if (is_db_wall(x, y))
372 return((boolean)(is_u(etmp) ? Passes_walls :
373 passes_walls(etmp->edata)));
378 e_died(etmp, dest, how)
383 if (how == DROWNING) {
384 killer = 0; /* drown() sets its own killer */
386 } else if (how == BURNING) {
387 killer = 0; /* lava_effects() sets its own killer */
388 (void) lava_effects();
392 /* use more specific killer if specified */
394 killer_format = KILLED_BY_AN;
395 killer = "falling drawbridge";
398 /* So, you didn't die */
399 if (!e_survives_at(etmp, etmp->ex, etmp->ey)) {
400 if (enexto(&xy, etmp->ex, etmp->ey, etmp->edata)) {
401 pline("A %s force teleports you away...",
402 Hallucination ? "normal" : "strange");
403 teleds(xy.x, xy.y, FALSE);
405 /* otherwise on top of the drawbridge is the
406 * only viable spot in the dungeon, so stay there
410 /* we might have crawled out of the moat to survive */
411 etmp->ex = u.ux, etmp->ey = u.uy;
416 /* fake "digested to death" damage-type suppresses corpse */
417 #define mk_message(dest) ((dest & 1) ? "" : (char *)0)
418 #define mk_corpse(dest) ((dest & 2) ? AD_DGST : AD_PHYS)
419 /* if monsters are moving, one of them caused the destruction */
420 if (flags.mon_moving)
421 monkilled(etmp->emon, mk_message(dest), mk_corpse(dest));
422 else /* you caused it */
423 xkilled(etmp->emon, dest);
424 etmp->edata = (struct permonst *)0;
426 /* dead long worm handling */
427 for (entitycnt = 0; entitycnt < ENTITIES; entitycnt++) {
428 if (etmp != &(occupants[entitycnt]) &&
429 etmp->emon == occupants[entitycnt].emon)
430 occupants[entitycnt].edata = (struct permonst *)0;
439 * These are never directly affected by a bridge or portcullis.
446 return (boolean)((is_u(etmp) ? Passes_walls :
447 passes_walls(etmp->edata)) || noncorporeal(etmp->edata));
451 * Does falling drawbridge or portcullis miss etmp?
455 e_missed(etmp, chunks)
463 pline("Do chunks miss?");
468 if (is_flyer(etmp->edata) &&
469 (is_u(etmp)? !Sleeping :
470 (etmp->emon->mcanmove && !etmp->emon->msleeping)))
471 /* flying requires mobility */
472 misses = 5; /* out of 8 */
473 else if (is_floater(etmp->edata) ||
474 (is_u(etmp) && Levitation)) /* doesn't require mobility */
476 else if (chunks && is_pool(etmp->ex, etmp->ey))
477 misses = 2; /* sitting ducks */
481 if (is_db_wall(etmp->ex, etmp->ey))
482 misses -= 3; /* less airspace */
485 pline("Miss chance = %d (out of 8)", misses);
488 return((boolean)((misses >= rnd(8))? TRUE : FALSE));
492 * Can etmp jump from death?
499 int tmp = 4; /* out of 10 */
501 if (is_u(etmp)? (Sleeping || Fumbling) :
502 (!etmp->emon->mcanmove || etmp->emon->msleeping ||
503 !etmp->edata->mmove || etmp->emon->wormno))
506 if (is_u(etmp)? Confusion : etmp->emon->mconf)
509 if (is_u(etmp)? Stunned : etmp->emon->mstun)
512 if (is_db_wall(etmp->ex, etmp->ey))
513 tmp -= 2; /* less room to maneuver */
516 pline("%s to jump (%d chances in 10)", E_phrase(etmp, "try"), tmp);
518 return((boolean)((tmp >= rnd(10))? TRUE : FALSE));
525 int newx, newy, at_portcullis, oldx, oldy;
526 boolean must_jump = FALSE, relocates = FALSE, e_inview;
532 e_inview = e_canseemon(etmp);
535 at_portcullis = is_db_wall(oldx, oldy);
536 crm = &levl[oldx][oldy];
538 if (automiss(etmp) && e_survives_at(etmp, oldx, oldy)) {
539 if (e_inview && (at_portcullis || IS_DRAWBRIDGE(crm->typ)))
540 pline_The("%s passes through %s!",
541 at_portcullis ? "portcullis" : "drawbridge",
543 if (is_u(etmp)) spoteffects(FALSE);
546 if (e_missed(etmp, FALSE)) {
548 pline_The("portcullis misses %s!",
552 pline_The("drawbridge misses %s!",
555 if (e_survives_at(etmp, oldx, oldy))
559 pline("Mon can't survive here");
564 relocates = TRUE; /* just ride drawbridge in */
567 if (crm->typ == DRAWBRIDGE_DOWN) {
568 pline("%s crushed underneath the drawbridge.",
569 E_phrase(etmp, "are")); /* no jump */
570 e_died(etmp, e_inview? 3 : 2, CRUSHING);/* no corpse */
571 return; /* Note: Beyond this point, we know we're */
572 } /* not at an opened drawbridge, since all */
573 must_jump = TRUE; /* *missable* creatures survive on the */
574 } /* square, and all the unmissed ones die. */
580 pline("Jump succeeds!");
584 pline("%s crushed by the falling portcullis!",
585 E_phrase(etmp, "are"));
586 else if (flags.soundok)
587 You_hear("a crushing sound.");
588 e_died(etmp, e_inview? 3 : 2, CRUSHING);
592 } else { /* tries to jump off bridge to original square */
593 relocates = !e_jumps(etmp);
595 pline("Jump %s!", (relocates)? "fails" : "succeeds");
601 * Here's where we try to do relocation. Assumes that etmp is not arriving
602 * at the portcullis square while the drawbridge is falling, since this square
603 * would be inaccessible (i.e. etmp started on drawbridge square) or
604 * unnecessary (i.e. etmp started here) in such a situation.
607 pline("Doing relocation.");
611 (void)find_drawbridge(&newx, &newy);
612 if ((newx == oldx) && (newy == oldy))
613 get_wall_for_db(&newx, &newy);
615 pline("Checking new square for occupancy.");
617 if (relocates && (e_at(newx, newy))) {
620 * Standoff problem: one or both entities must die, and/or both switch
621 * places. Avoid infinite recursion by checking first whether the other
622 * entity is staying put. Clean up if we happen to move/die in recursion.
624 struct entity *other;
626 other = e_at(newx, newy);
628 pline("New square is occupied by %s", e_nam(other));
630 if (e_survives_at(other, newx, newy) && automiss(other)) {
631 relocates = FALSE; /* "other" won't budge */
633 pline("%s suicide.", E_phrase(etmp, "commit"));
638 pline("Handling %s", e_nam(other));
640 while ((e_at(newx, newy) != 0) &&
641 (e_at(newx, newy) != etmp))
644 pline("Checking existence of %s", e_nam(etmp));
647 if (e_at(oldx, oldy) != etmp) {
649 pline("%s moved or died in recursion somewhere",
650 E_phrase(etmp, "have"));
657 if (relocates && !e_at(newx, newy)) {/* if e_at() entity = worm tail */
659 pline("Moving %s", e_nam(etmp));
662 remove_monster(etmp->ex, etmp->ey);
663 place_monster(etmp->emon, newx, newy);
664 update_monster_region(etmp->emon);
671 e_inview = e_canseemon(etmp);
674 pline("Final disposition of %s", e_nam(etmp));
677 if (is_db_wall(etmp->ex, etmp->ey)) {
679 pline("%s in portcullis chamber", E_phrase(etmp, "are"));
684 You("tumble towards the closed portcullis!");
686 You("pass through it!");
688 pline_The("drawbridge closes in...");
690 pline("%s behind the drawbridge.",
691 E_phrase(etmp, "disappear"));
693 if (!e_survives_at(etmp, etmp->ex, etmp->ey)) {
694 killer_format = KILLED_BY_AN;
695 killer = "closing drawbridge";
696 e_died(etmp, 0, CRUSHING); /* no message */
700 pline("%s in here", E_phrase(etmp, "survive"));
704 pline("%s on drawbridge square", E_phrase(etmp, "are"));
706 if (is_pool(etmp->ex, etmp->ey) && !e_inview)
708 You_hear("a splash.");
709 if (e_survives_at(etmp, etmp->ex, etmp->ey)) {
710 if (e_inview && !is_flyer(etmp->edata) &&
711 !is_floater(etmp->edata))
712 pline("%s from the bridge.",
713 E_phrase(etmp, "fall"));
717 pline("%s cannot survive on the drawbridge square",Enam(etmp));
719 if (is_pool(etmp->ex, etmp->ey) || is_lava(etmp->ex, etmp->ey))
720 if (e_inview && !is_u(etmp)) {
721 /* drown() will supply msgs if nec. */
722 boolean lava = is_lava(etmp->ex, etmp->ey);
725 pline("%s the %s and disappears.",
726 E_phrase(etmp, "drink"),
727 lava ? "lava" : "moat");
729 pline("%s into the %s.",
730 E_phrase(etmp, "fall"),
731 lava ? "lava" : "moat");
733 killer_format = NO_KILLER_PREFIX;
734 killer = "fell from a drawbridge";
735 e_died(etmp, e_inview ? 3 : 2, /* CRUSHING is arbitrary */
736 (is_pool(etmp->ex, etmp->ey)) ? DROWNING :
737 (is_lava(etmp->ex, etmp->ey)) ? BURNING :
738 CRUSHING); /*no corpse*/
744 * Close the drawbridge located at x,y
748 close_drawbridge(x,y)
751 register struct rm *lev1, *lev2;
756 if (lev1->typ != DRAWBRIDGE_DOWN) return;
758 get_wall_for_db(&x2,&y2);
759 if (cansee(x,y) || cansee(x2,y2))
760 You("see a drawbridge %s up!",
761 (((u.ux == x || u.uy == y) && !Underwater) ||
762 distu(x2,y2) < distu(x,y)) ? "coming" : "going");
763 lev1->typ = DRAWBRIDGE_UP;
764 lev2 = &levl[x2][y2];
766 switch (lev1->drawbridgemask & DB_DIR) {
769 lev2->horizontal = TRUE;
773 lev2->horizontal = FALSE;
776 lev2->wall_info = W_NONDIGGABLE;
777 set_entity(x, y, &(occupants[0]));
778 set_entity(x2, y2, &(occupants[1]));
779 do_entity(&(occupants[0])); /* Do set_entity after first */
780 set_entity(x2, y2, &(occupants[1])); /* do_entity for worm tail */
781 do_entity(&(occupants[1]));
782 if(OBJ_AT(x,y) && flags.soundok)
783 You_hear("smashing and crushing.");
784 (void) revive_nasty(x,y,(char *)0);
785 (void) revive_nasty(x2,y2,(char *)0);
788 if ((t = t_at(x, y)) != 0) deltrap(t);
789 if ((t = t_at(x2, y2)) != 0) deltrap(t);
792 block_point(x2,y2); /* vision */
796 * Open the drawbridge located at x,y
803 register struct rm *lev1, *lev2;
808 if (lev1->typ != DRAWBRIDGE_UP) return;
810 get_wall_for_db(&x2,&y2);
811 if (cansee(x,y) || cansee(x2,y2))
812 You("see a drawbridge %s down!",
813 (distu(x2,y2) < distu(x,y)) ? "going" : "coming");
814 lev1->typ = DRAWBRIDGE_DOWN;
815 lev2 = &levl[x2][y2];
817 lev2->doormask = D_NODOOR;
818 set_entity(x, y, &(occupants[0]));
819 set_entity(x2, y2, &(occupants[1]));
820 do_entity(&(occupants[0])); /* do set_entity after first */
821 set_entity(x2, y2, &(occupants[1])); /* do_entity for worm tails */
822 do_entity(&(occupants[1]));
823 (void) revive_nasty(x,y,(char *)0);
825 if ((t = t_at(x, y)) != 0) deltrap(t);
826 if ((t = t_at(x2, y2)) != 0) deltrap(t);
829 unblock_point(x2,y2); /* vision */
830 if (Is_stronghold(&u.uz)) u.uevent.uopened_dbridge = TRUE;
834 * Let's destroy the drawbridge located at x,y
838 destroy_drawbridge(x,y)
841 register struct rm *lev1, *lev2;
845 struct entity *etmp1 = &(occupants[0]), *etmp2 = &(occupants[1]);
848 if (!IS_DRAWBRIDGE(lev1->typ))
851 get_wall_for_db(&x2,&y2);
852 lev2 = &levl[x2][y2];
853 if ((lev1->drawbridgemask & DB_UNDER) == DB_MOAT ||
854 (lev1->drawbridgemask & DB_UNDER) == DB_LAVA) {
856 boolean lava = (lev1->drawbridgemask & DB_UNDER) == DB_LAVA;
857 if (lev1->typ == DRAWBRIDGE_UP) {
859 pline_The("portcullis of the drawbridge falls into the %s!",
860 lava ? "lava" : "moat");
861 else if (flags.soundok)
862 You_hear("a loud *SPLASH*!");
865 pline_The("drawbridge collapses into the %s!",
866 lava ? "lava" : "moat");
867 else if (flags.soundok)
868 You_hear("a loud *SPLASH*!");
870 lev1->typ = lava ? LAVAPOOL : MOAT;
871 lev1->drawbridgemask = 0;
872 if ((otmp = sobj_at(BOULDER,x,y)) != 0) {
873 obj_extract_self(otmp);
874 (void) flooreffects(otmp,x,y,"fall");
878 pline_The("drawbridge disintegrates!");
880 You_hear("a loud *CRASH*!");
882 ((lev1->drawbridgemask & DB_ICE) ? ICE : ROOM);
884 ((lev1->drawbridgemask & DB_ICE) ? ICED_MOAT : 0);
886 wake_nearto(x, y, 500);
888 lev2->doormask = D_NODOOR;
889 if ((t = t_at(x, y)) != 0) deltrap(t);
890 if ((t = t_at(x2, y2)) != 0) deltrap(t);
893 if (!does_block(x2,y2,lev2)) unblock_point(x2,y2); /* vision */
894 if (Is_stronghold(&u.uz)) u.uevent.uopened_dbridge = TRUE;
896 set_entity(x2, y2, etmp2); /* currently only automissers can be here */
898 e_inview = e_canseemon(etmp2);
899 if (!automiss(etmp2)) {
901 pline("%s blown apart by flying debris.",
902 E_phrase(etmp2, "are"));
903 killer_format = KILLED_BY_AN;
904 killer = "exploding drawbridge";
905 e_died(etmp2, e_inview? 3 : 2, CRUSHING); /*no corpse*/
906 } /* nothing which is vulnerable can survive this */
908 set_entity(x, y, etmp1);
910 e_inview = e_canseemon(etmp1);
911 if (e_missed(etmp1, TRUE)) {
913 pline("%s spared!", E_phrase(etmp1, "are"));
917 if (!is_u(etmp1) && Hallucination)
918 pline("%s into some heavy metal!",
919 E_phrase(etmp1, "get"));
921 pline("%s hit by a huge chunk of metal!",
922 E_phrase(etmp1, "are"));
924 if (flags.soundok && !is_u(etmp1) && !is_pool(x,y))
925 You_hear("a crushing sound.");
928 pline("%s from shrapnel",
929 E_phrase(etmp1, "die"));
932 killer_format = KILLED_BY_AN;
933 killer = "collapsing drawbridge";
934 e_died(etmp1, e_inview? 3 : 2, CRUSHING); /*no corpse*/
935 if(lev1->typ == MOAT) do_entity(etmp1);