diff --git a/src/dbridge.c b/src/dbridge.c new file mode 100644 index 000000000..f92f744ed --- /dev/null +++ b/src/dbridge.c @@ -0,0 +1,957 @@ +/* SCCS Id: @(#)dbridge.c 3.3 2000/02/05 */ +/* Copyright (c) 1989 by Jean-Christophe Collet */ +/* NetHack may be freely redistributed. See license for details. */ + +/* + * This file contains the drawbridge manipulation (create, open, close, + * destroy). + * + * Added comprehensive monster-handling, and the "entity" structure to + * deal with players as well. - 11/89 + */ + +#include "hack.h" + +#ifdef OVLB +STATIC_DCL void FDECL(get_wall_for_db, (int *, int *)); +STATIC_DCL struct entity *FDECL(e_at, (int, int)); +STATIC_DCL void FDECL(m_to_e, (struct monst *, int, int, struct entity *)); +STATIC_DCL void FDECL(u_to_e, (struct entity *)); +STATIC_DCL void FDECL(set_entity, (int, int, struct entity *)); +STATIC_DCL const char *FDECL(e_nam, (struct entity *)); +#ifdef D_DEBUG +static const char *FDECL(Enam, (struct entity *)); /* unused */ +#endif +STATIC_DCL const char *FDECL(E_phrase, (struct entity *, const char *)); +STATIC_DCL boolean FDECL(e_survives_at, (struct entity *, int, int)); +STATIC_DCL void FDECL(e_died, (struct entity *, int, int)); +STATIC_DCL boolean FDECL(automiss, (struct entity *)); +STATIC_DCL boolean FDECL(e_missed, (struct entity *, BOOLEAN_P)); +STATIC_DCL boolean FDECL(e_jumps, (struct entity *)); +STATIC_DCL void FDECL(do_entity, (struct entity *)); +#endif /* OVLB */ + +#ifdef OVL0 + +boolean +is_pool(x,y) +int x,y; +{ + schar ltyp; + + if (!isok(x,y)) return FALSE; + ltyp = levl[x][y].typ; + if (ltyp == POOL || ltyp == MOAT || ltyp == WATER) return TRUE; + if (ltyp == DRAWBRIDGE_UP && + (levl[x][y].drawbridgemask & DB_UNDER) == DB_MOAT) return TRUE; + return FALSE; +} + +boolean +is_lava(x,y) +int x,y; +{ + schar ltyp; + + if (!isok(x,y)) return FALSE; + ltyp = levl[x][y].typ; + if (ltyp == LAVAPOOL + || (ltyp == DRAWBRIDGE_UP + && (levl[x][y].drawbridgemask & DB_UNDER) == DB_LAVA)) return TRUE; + return FALSE; +} + +boolean +is_ice(x,y) +int x,y; +{ + schar ltyp; + + if (!isok(x,y)) return FALSE; + ltyp = levl[x][y].typ; + if (ltyp == ICE + || (ltyp == DRAWBRIDGE_UP + && (levl[x][y].drawbridgemask & DB_UNDER) == DB_ICE)) return TRUE; + return FALSE; +} + +#endif /* OVL0 */ + +#ifdef OVL1 + +/* + * We want to know whether a wall (or a door) is the portcullis (passageway) + * of an eventual drawbridge. + * + * Return value: the direction of the drawbridge. + */ + +int +is_drawbridge_wall(x,y) +int x,y; +{ + struct rm *lev; + + lev = &levl[x][y]; + if (lev->typ != DOOR && lev->typ != DBWALL) + return (-1); + + if (IS_DRAWBRIDGE(levl[x+1][y].typ) && + (levl[x+1][y].drawbridgemask & DB_DIR) == DB_WEST) + return (DB_WEST); + if (IS_DRAWBRIDGE(levl[x-1][y].typ) && + (levl[x-1][y].drawbridgemask & DB_DIR) == DB_EAST) + return (DB_EAST); + if (IS_DRAWBRIDGE(levl[x][y-1].typ) && + (levl[x][y-1].drawbridgemask & DB_DIR) == DB_SOUTH) + return (DB_SOUTH); + if (IS_DRAWBRIDGE(levl[x][y+1].typ) && + (levl[x][y+1].drawbridgemask & DB_DIR) == DB_NORTH) + return (DB_NORTH); + + return (-1); +} + +/* + * Use is_db_wall where you want to verify that a + * drawbridge "wall" is UP in the location x, y + * (instead of UP or DOWN, as with is_drawbridge_wall). + */ +boolean +is_db_wall(x,y) +int x,y; +{ + return((boolean)( levl[x][y].typ == DBWALL )); +} + + +/* + * Return true with x,y pointing to the drawbridge if x,y initially indicate + * a drawbridge or drawbridge wall. + */ +boolean +find_drawbridge(x,y) +int *x,*y; +{ + int dir; + + if (IS_DRAWBRIDGE(levl[*x][*y].typ)) + return TRUE; + dir = is_drawbridge_wall(*x,*y); + if (dir >= 0) { + switch(dir) { + case DB_NORTH: (*y)++; break; + case DB_SOUTH: (*y)--; break; + case DB_EAST: (*x)--; break; + case DB_WEST: (*x)++; break; + } + return TRUE; + } + return FALSE; +} + +#endif /* OVL1 */ +#ifdef OVLB + +/* + * Find the drawbridge wall associated with a drawbridge. + */ +STATIC_OVL void +get_wall_for_db(x,y) +int *x,*y; +{ + switch (levl[*x][*y].drawbridgemask & DB_DIR) { + case DB_NORTH: (*y)--; break; + case DB_SOUTH: (*y)++; break; + case DB_EAST: (*x)++; break; + case DB_WEST: (*x)--; break; + } +} + +/* + * Creation of a drawbridge at pos x,y. + * dir is the direction. + * flag must be put to TRUE if we want the drawbridge to be opened. + */ + +boolean +create_drawbridge(x,y,dir,flag) +int x,y,dir; +boolean flag; +{ + int x2,y2; + boolean horiz; + boolean lava = levl[x][y].typ == LAVAPOOL; /* assume initialized map */ + + x2 = x; y2 = y; + switch(dir) { + case DB_NORTH: + horiz = TRUE; + y2--; + break; + case DB_SOUTH: + horiz = TRUE; + y2++; + break; + case DB_EAST: + horiz = FALSE; + x2++; + break; + default: + impossible("bad direction in create_drawbridge"); + /* fall through */ + case DB_WEST: + horiz = FALSE; + x2--; + break; + } + if (!IS_WALL(levl[x2][y2].typ)) + return(FALSE); + if (flag) { /* We want the bridge open */ + levl[x][y].typ = DRAWBRIDGE_DOWN; + levl[x2][y2].typ = DOOR; + levl[x2][y2].doormask = D_NODOOR; + } else { + levl[x][y].typ = DRAWBRIDGE_UP; + levl[x2][y2].typ = DBWALL; + /* Drawbridges are non-diggable. */ + levl[x2][y2].wall_info = W_NONDIGGABLE; + } + levl[x][y].horizontal = !horiz; + levl[x2][y2].horizontal = horiz; + levl[x][y].drawbridgemask = dir; + if(lava) levl[x][y].drawbridgemask |= DB_LAVA; + return(TRUE); +} + +struct entity { + struct monst *emon; /* youmonst for the player */ + struct permonst *edata; /* must be non-zero for record to be valid */ + int ex, ey; +}; + +#define ENTITIES 2 + +static NEARDATA struct entity occupants[ENTITIES]; + +STATIC_OVL +struct entity * +e_at(x, y) +int x, y; +{ + int entitycnt; + + for (entitycnt = 0; entitycnt < ENTITIES; entitycnt++) + if ((occupants[entitycnt].edata) && + (occupants[entitycnt].ex == x) && + (occupants[entitycnt].ey == y)) + break; +#ifdef D_DEBUG + pline("entitycnt = %d", entitycnt); + wait_synch(); +#endif + return((entitycnt == ENTITIES)? + (struct entity *)0 : &(occupants[entitycnt])); +} + +STATIC_OVL void +m_to_e(mtmp, x, y, etmp) +struct monst *mtmp; +int x, y; +struct entity *etmp; +{ + etmp->emon = mtmp; + if (mtmp) { + etmp->ex = x; + etmp->ey = y; + if (mtmp->wormno && (x != mtmp->mx || y != mtmp->my)) + etmp->edata = &mons[PM_LONG_WORM_TAIL]; + else + etmp->edata = mtmp->data; + } else + etmp->edata = (struct permonst *)0; +} + +STATIC_OVL void +u_to_e(etmp) +struct entity *etmp; +{ + etmp->emon = &youmonst; + etmp->ex = u.ux; + etmp->ey = u.uy; + etmp->edata = youmonst.data; +} + +STATIC_OVL void +set_entity(x, y, etmp) +int x, y; +struct entity *etmp; +{ + if ((x == u.ux) && (y == u.uy)) + u_to_e(etmp); + else if (MON_AT(x, y)) + m_to_e(m_at(x, y), x, y, etmp); + else + etmp->edata = (struct permonst *)0; +} + +#define is_u(etmp) (etmp->emon == &youmonst) +#define e_canseemon(etmp) (is_u(etmp) ? (boolean)TRUE : canseemon(etmp->emon)) + +/* + * e_strg is a utility routine which is not actually in use anywhere, since + * the specialized routines below suffice for all current purposes. + */ + +/* #define e_strg(etmp, func) (is_u(etmp)? (char *)0 : func(etmp->emon)) */ + +STATIC_OVL const char * +e_nam(etmp) +struct entity *etmp; +{ + return(is_u(etmp)? "you" : mon_nam(etmp->emon)); +} + +#ifdef D_DEBUG +/* + * Enam is another unused utility routine: E_phrase is preferable. + */ + +static const char * +Enam(etmp) +struct entity *etmp; +{ + return(is_u(etmp)? "You" : Monnam(etmp->emon)); +} +#endif /* D_DEBUG */ + +/* + * Generates capitalized entity name, makes 2nd -> 3rd person conversion on + * verb, where necessary. + */ + +STATIC_OVL const char * +E_phrase(etmp, verb) +struct entity *etmp; +const char *verb; +{ + static char wholebuf[80]; + char verbbuf[30]; + + Strcpy(wholebuf, is_u(etmp) ? "You" : Monnam(etmp->emon)); + if (!*verb) + return(wholebuf); + Strcat(wholebuf, " "); + verbbuf[0] = '\0'; + if (is_u(etmp)) + Strcpy(verbbuf, verb); + else { + if (!strcmp(verb, "are")) + Strcpy(verbbuf, "is"); + if (!strcmp(verb, "have")) + Strcpy(verbbuf, "has"); + if (!verbbuf[0]) { + Strcpy(verbbuf, verb); + switch (verbbuf[strlen(verbbuf) - 1]) { + case 'y': + verbbuf[strlen(verbbuf) - 1] = '\0'; + Strcat(verbbuf, "ies"); + break; + case 'h': + case 'o': + case 's': + Strcat(verbbuf, "es"); + break; + default: + Strcat(verbbuf, "s"); + break; + } + } + } + Strcat(wholebuf, verbbuf); + return(wholebuf); +} + +/* + * Simple-minded "can it be here?" routine + */ + +STATIC_OVL boolean +e_survives_at(etmp, x, y) +struct entity *etmp; +int x, y; +{ + if (noncorporeal(etmp->edata)) + return(TRUE); + if (is_pool(x, y)) + return (boolean)((is_u(etmp) && + (Wwalking || Amphibious || Swimming || + Flying || Levitation)) || + is_swimmer(etmp->edata) || is_flyer(etmp->edata) || + is_floater(etmp->edata)); + /* must force call to lava_effects in e_died if is_u */ + if (is_lava(x, y)) + return (boolean)((is_u(etmp) && (Levitation || Flying)) || + likes_lava(etmp->edata) || is_flyer(etmp->edata)); + if (is_db_wall(x, y)) + return((boolean)(is_u(etmp) ? Passes_walls : + passes_walls(etmp->edata))); + return(TRUE); +} + +STATIC_OVL void +e_died(etmp, dest, how) +struct entity *etmp; +int dest, how; +{ + if (is_u(etmp)) { + if (how == DROWNING) { + killer = 0; /* drown() sets its own killer */ + (void) drown(); + } else if (how == BURNING) { + killer = 0; /* lava_effects() sets its own killer */ + (void) lava_effects(); + } else { + coord xy; + + /* use more specific killer if specified */ + if (!killer) { + killer_format = KILLED_BY_AN; + killer = "falling drawbridge"; + } + done(how); + /* So, you didn't die */ + if (!e_survives_at(etmp, etmp->ex, etmp->ey)) { + if (enexto(&xy, etmp->ex, etmp->ey, etmp->edata)) { + pline("A %s force teleports you away...", + Hallucination ? "normal" : "strange"); + teleds(xy.x, xy.y); + } + /* otherwise on top of the drawbridge is the + * only viable spot in the dungeon, so stay there + */ + } + } + /* we might have crawled out of the moat to survive */ + etmp->ex = u.ux, etmp->ey = u.uy; + } else { + killer = 0; + /* fake "digested to death" damage-type suppresses corpse */ +#define mk_message(dest) ((dest & 1) ? "" : (char *)0) +#define mk_corpse(dest) ((dest & 2) ? AD_DGST : AD_PHYS) + /* if monsters are moving, one of them caused the destruction */ + if (flags.mon_moving) + monkilled(etmp->emon, mk_message(dest), mk_corpse(dest)); + else /* you caused it */ + xkilled(etmp->emon, dest); + etmp->edata = (struct permonst *)0; +#undef mk_message +#undef mk_corpse + } +} + + +/* + * These are never directly affected by a bridge or portcullis. + */ + +STATIC_OVL boolean +automiss(etmp) +struct entity *etmp; +{ + return (boolean)((is_u(etmp) ? Passes_walls : + passes_walls(etmp->edata)) || noncorporeal(etmp->edata)); +} + +/* + * Does falling drawbridge or portcullis miss etmp? + */ + +STATIC_OVL boolean +e_missed(etmp, chunks) +struct entity *etmp; +boolean chunks; +{ + int misses; + +#ifdef D_DEBUG + if (chunks) + pline("Do chunks miss?"); +#endif + if (automiss(etmp)) + return(TRUE); + + if (is_flyer(etmp->edata) && + (is_u(etmp)? !Sleeping : + (etmp->emon->mcanmove && !etmp->emon->msleeping))) + /* flying requires mobility */ + misses = 5; /* out of 8 */ + else if (is_floater(etmp->edata) || + (is_u(etmp) && Levitation)) /* doesn't require mobility */ + misses = 3; + else if (chunks && is_pool(etmp->ex, etmp->ey)) + misses = 2; /* sitting ducks */ + else + misses = 0; + + if (is_db_wall(etmp->ex, etmp->ey)) + misses -= 3; /* less airspace */ + +#ifdef D_DEBUG + pline("Miss chance = %d (out of 8)", misses); +#endif + + return((boolean)((misses >= rnd(8))? TRUE : FALSE)); +} + +/* + * Can etmp jump from death? + */ + +STATIC_OVL boolean +e_jumps(etmp) +struct entity *etmp; +{ + int tmp = 4; /* out of 10 */ + + if (is_u(etmp)? (Sleeping || Fumbling) : + (!etmp->emon->mcanmove || etmp->emon->msleeping || + !etmp->edata->mmove || etmp->emon->wormno)) + return(FALSE); + + if (is_u(etmp)? Confusion : etmp->emon->mconf) + tmp -= 2; + + if (is_u(etmp)? Stunned : etmp->emon->mstun) + tmp -= 3; + + if (is_db_wall(etmp->ex, etmp->ey)) + tmp -= 2; /* less room to maneuver */ + +#ifdef D_DEBUG + pline("%s to jump (%d chances in 10)", E_phrase(etmp, "try"), tmp); +#endif + return((boolean)((tmp >= rnd(10))? TRUE : FALSE)); +} + +STATIC_OVL void +do_entity(etmp) +struct entity *etmp; +{ + int newx, newy, at_portcullis, oldx, oldy; + boolean must_jump = FALSE, relocates = FALSE, e_inview; + struct rm *crm; + + if (!etmp->edata) + return; + + e_inview = e_canseemon(etmp); + oldx = etmp->ex; + oldy = etmp->ey; + at_portcullis = is_db_wall(oldx, oldy); + crm = &levl[oldx][oldy]; + + if (automiss(etmp) && e_survives_at(etmp, oldx, oldy)) { + if (e_inview && (at_portcullis || IS_DRAWBRIDGE(crm->typ))) + pline_The("%s passes through %s!", + at_portcullis ? "portcullis" : "drawbridge", + e_nam(etmp)); + return; + } + if (e_missed(etmp, FALSE)) { + if (at_portcullis) + pline_The("portcullis misses %s!", + e_nam(etmp)); +#ifdef D_DEBUG + else + pline_The("drawbridge misses %s!", + e_nam(etmp)); +#endif + if (e_survives_at(etmp, oldx, oldy)) + return; + else { +#ifdef D_DEBUG + pline("Mon can't survive here"); +#endif + if (at_portcullis) + must_jump = TRUE; + else + relocates = TRUE; /* just ride drawbridge in */ + } + } else { + if (crm->typ == DRAWBRIDGE_DOWN) { + pline("%s crushed underneath the drawbridge.", + E_phrase(etmp, "are")); /* no jump */ + e_died(etmp, e_inview? 3 : 2, CRUSHING);/* no corpse */ + return; /* Note: Beyond this point, we know we're */ + } /* not at an opened drawbridge, since all */ + must_jump = TRUE; /* *missable* creatures survive on the */ + } /* square, and all the unmissed ones die. */ + if (must_jump) { + if (at_portcullis) { + if (e_jumps(etmp)) { + relocates = TRUE; +#ifdef D_DEBUG + pline("Jump succeeds!"); +#endif + } else { + if (e_inview) + pline("%s crushed by the falling portcullis!", + E_phrase(etmp, "are")); + else if (flags.soundok) + You_hear("a crushing sound."); + e_died(etmp, e_inview? 3 : 2, CRUSHING); + /* no corpse */ + return; + } + } else { /* tries to jump off bridge to original square */ + relocates = !e_jumps(etmp); +#ifdef D_DEBUG + pline("Jump %s!", (relocates)? "fails" : "succeeds"); +#endif + } + } + +/* + * Here's where we try to do relocation. Assumes that etmp is not arriving + * at the portcullis square while the drawbridge is falling, since this square + * would be inaccessible (i.e. etmp started on drawbridge square) or + * unnecessary (i.e. etmp started here) in such a situation. + */ +#ifdef D_DEBUG + pline("Doing relocation."); +#endif + newx = oldx; + newy = oldy; + (void)find_drawbridge(&newx, &newy); + if ((newx == oldx) && (newy == oldy)) + get_wall_for_db(&newx, &newy); +#ifdef D_DEBUG + pline("Checking new square for occupancy."); +#endif + if (relocates && (e_at(newx, newy))) { + +/* + * Standoff problem: one or both entities must die, and/or both switch + * places. Avoid infinite recursion by checking first whether the other + * entity is staying put. Clean up if we happen to move/die in recursion. + */ + struct entity *other; + + other = e_at(newx, newy); +#ifdef D_DEBUG + pline("New square is occupied by %s", e_nam(other)); +#endif + if (e_survives_at(other, newx, newy) && automiss(other)) { + relocates = FALSE; /* "other" won't budge */ +#ifdef D_DEBUG + pline("%s suicide.", E_phrase(etmp, "commit")); +#endif + } else { + +#ifdef D_DEBUG + pline("Handling %s", e_nam(other)); +#endif + while ((e_at(newx, newy) != 0) && + (e_at(newx, newy) != etmp)) + do_entity(other); +#ifdef D_DEBUG + pline("Checking existence of %s", e_nam(etmp)); + wait_synch(); +#endif + if (e_at(oldx, oldy) != etmp) { +#ifdef D_DEBUG + pline("%s moved or died in recursion somewhere", + E_phrase(etmp, "have")); + wait_synch(); +#endif + return; + } + } + } + if (relocates && !e_at(newx, newy)) {/* if e_at() entity = worm tail */ +#ifdef D_DEBUG + pline("Moving %s", e_nam(etmp)); +#endif + if (!is_u(etmp)) { + remove_monster(etmp->ex, etmp->ey); + place_monster(etmp->emon, newx, newy); + update_monster_region(etmp->emon); + } else { + u.ux = newx; + u.uy = newy; + } + etmp->ex = newx; + etmp->ey = newy; + e_inview = e_canseemon(etmp); + } +#ifdef D_DEBUG + pline("Final disposition of %s", e_nam(etmp)); + wait_synch(); +#endif + if (is_db_wall(etmp->ex, etmp->ey)) { +#ifdef D_DEBUG + pline("%s in portcullis chamber", E_phrase(etmp, "are")); + wait_synch(); +#endif + if (e_inview) { + if (is_u(etmp)) { + You("tumble towards the closed portcullis!"); + if (automiss(etmp)) + You("pass through it!"); + else + pline_The("drawbridge closes in..."); + } else + pline("%s behind the drawbridge.", + E_phrase(etmp, "disappear")); + } + if (!e_survives_at(etmp, etmp->ex, etmp->ey)) { + killer_format = KILLED_BY_AN; + killer = "closing drawbridge"; + e_died(etmp, 0, CRUSHING); /* no message */ + return; + } +#ifdef D_DEBUG + pline("%s in here", E_phrase(etmp, "survive")); +#endif + } else { +#ifdef D_DEBUG + pline("%s on drawbridge square", E_phrase(etmp, "are")); +#endif + if (is_pool(etmp->ex, etmp->ey) && !e_inview) + if (flags.soundok) + You_hear("a splash."); + if (e_survives_at(etmp, etmp->ex, etmp->ey)) { + if (e_inview && !is_flyer(etmp->edata) && + !is_floater(etmp->edata)) + pline("%s from the bridge.", + E_phrase(etmp, "fall")); + return; + } +#ifdef D_DEBUG + pline("%s cannot survive on the drawbridge square",Enam(etmp)); +#endif + if (is_pool(etmp->ex, etmp->ey) || is_lava(etmp->ex, etmp->ey)) + if (e_inview && !is_u(etmp)) { + /* drown() will supply msgs if nec. */ + boolean lava = is_lava(etmp->ex, etmp->ey); + + if (Hallucination) + pline("%s the %s and disappears.", + E_phrase(etmp, "drink"), + lava ? "lava" : "moat"); + else + pline("%s into the %s.", + E_phrase(etmp, "fall"), + lava ? "lava" : "moat"); + } + killer_format = NO_KILLER_PREFIX; + killer = "fell from a drawbridge"; + e_died(etmp, e_inview ? 3 : 2, /* CRUSHING is arbitrary */ + (is_pool(etmp->ex, etmp->ey)) ? DROWNING : + (is_lava(etmp->ex, etmp->ey)) ? BURNING : + CRUSHING); /*no corpse*/ + return; + } +} + +/* + * Close the drawbridge located at x,y + */ + +void +close_drawbridge(x,y) +int x,y; +{ + register struct rm *lev1, *lev2; + struct trap *t; + int x2, y2; + + lev1 = &levl[x][y]; + if (lev1->typ != DRAWBRIDGE_DOWN) return; + x2 = x; y2 = y; + get_wall_for_db(&x2,&y2); + if (cansee(x,y) || cansee(x2,y2)) + You("see a drawbridge %s up!", + (((u.ux == x || u.uy == y) && !Underwater) || + distu(x2,y2) < distu(x,y)) ? "coming" : "going"); + lev1->typ = DRAWBRIDGE_UP; + lev2 = &levl[x2][y2]; + lev2->typ = DBWALL; + switch (lev1->drawbridgemask & DB_DIR) { + case DB_NORTH: + case DB_SOUTH: + lev2->horizontal = TRUE; + break; + case DB_WEST: + case DB_EAST: + lev2->horizontal = FALSE; + break; + } + lev2->wall_info = W_NONDIGGABLE; + set_entity(x, y, &(occupants[0])); + set_entity(x2, y2, &(occupants[1])); + do_entity(&(occupants[0])); /* Do set_entity after first */ + set_entity(x2, y2, &(occupants[1])); /* do_entity for worm tail */ + do_entity(&(occupants[1])); + if(OBJ_AT(x,y) && flags.soundok) + You_hear("smashing and crushing."); + (void) revive_nasty(x,y,(char *)0); + (void) revive_nasty(x2,y2,(char *)0); + delallobj(x, y); + delallobj(x2, y2); + if ((t = t_at(x, y)) != 0) deltrap(t); + if ((t = t_at(x2, y2)) != 0) deltrap(t); + newsym(x, y); + newsym(x2, y2); + block_point(x2,y2); /* vision */ +} + +/* + * Open the drawbridge located at x,y + */ + +void +open_drawbridge(x,y) +int x,y; +{ + register struct rm *lev1, *lev2; + struct trap *t; + int x2, y2; + + lev1 = &levl[x][y]; + if (lev1->typ != DRAWBRIDGE_UP) return; + x2 = x; y2 = y; + get_wall_for_db(&x2,&y2); + if (cansee(x,y) || cansee(x2,y2)) + You("see a drawbridge %s down!", + (distu(x2,y2) < distu(x,y)) ? "going" : "coming"); + lev1->typ = DRAWBRIDGE_DOWN; + lev2 = &levl[x2][y2]; + lev2->typ = DOOR; + lev2->doormask = D_NODOOR; + set_entity(x, y, &(occupants[0])); + set_entity(x2, y2, &(occupants[1])); + do_entity(&(occupants[0])); /* do set_entity after first */ + set_entity(x2, y2, &(occupants[1])); /* do_entity for worm tails */ + do_entity(&(occupants[1])); + (void) revive_nasty(x,y,(char *)0); + delallobj(x, y); + if ((t = t_at(x, y)) != 0) deltrap(t); + if ((t = t_at(x2, y2)) != 0) deltrap(t); + newsym(x, y); + newsym(x2, y2); + unblock_point(x2,y2); /* vision */ + if (Is_stronghold(&u.uz)) u.uevent.uopened_dbridge = TRUE; +} + +/* + * Let's destroy the drawbridge located at x,y + */ + +void +destroy_drawbridge(x,y) +int x,y; +{ + register struct rm *lev1, *lev2; + struct trap *t; + int x2, y2; + boolean e_inview; + struct entity *etmp1 = &(occupants[0]), *etmp2 = &(occupants[1]); + + lev1 = &levl[x][y]; + if (!IS_DRAWBRIDGE(lev1->typ)) + return; + x2 = x; y2 = y; + get_wall_for_db(&x2,&y2); + lev2 = &levl[x2][y2]; + if ((lev1->drawbridgemask & DB_UNDER) == DB_MOAT || + (lev1->drawbridgemask & DB_UNDER) == DB_LAVA) { + struct obj *otmp; + boolean lava = (lev1->drawbridgemask & DB_UNDER) == DB_LAVA; + if (lev1->typ == DRAWBRIDGE_UP) { + if (cansee(x2,y2)) + pline_The("portcullis of the drawbridge falls into the %s!", + lava ? "lava" : "moat"); + else if (flags.soundok) + You_hear("a loud *SPLASH*!"); + } else { + if (cansee(x,y)) + pline_The("drawbridge collapses into the %s!", + lava ? "lava" : "moat"); + else if (flags.soundok) + You_hear("a loud *SPLASH*!"); + } + lev1->typ = lava ? LAVAPOOL : MOAT; + lev1->drawbridgemask = 0; + if ((otmp = sobj_at(BOULDER,x,y)) != 0) { + obj_extract_self(otmp); + (void) flooreffects(otmp,x,y,"fall"); + } + } else { + if (cansee(x,y)) + pline_The("drawbridge disintegrates!"); + else + You_hear("a loud *CRASH*!"); + lev1->typ = + ((lev1->drawbridgemask & DB_ICE) ? ICE : ROOM); + lev1->icedpool = + ((lev1->drawbridgemask & DB_ICE) ? ICED_MOAT : 0); + } + wake_nearto(x, y, 500); + lev2->typ = DOOR; + lev2->doormask = D_NODOOR; + if ((t = t_at(x, y)) != 0) deltrap(t); + if ((t = t_at(x2, y2)) != 0) deltrap(t); + newsym(x,y); + newsym(x2,y2); + if (!does_block(x2,y2,lev2)) unblock_point(x2,y2); /* vision */ + if (Is_stronghold(&u.uz)) u.uevent.uopened_dbridge = TRUE; + + set_entity(x2, y2, etmp2); /* currently only automissers can be here */ + if (etmp2->edata) { + e_inview = e_canseemon(etmp2); + if (!automiss(etmp2)) { + if (e_inview) + pline("%s blown apart by flying debris.", + E_phrase(etmp2, "are")); + killer_format = KILLED_BY_AN; + killer = "exploding drawbridge"; + e_died(etmp2, e_inview? 3 : 2, CRUSHING); /*no corpse*/ + } /* nothing which is vulnerable can survive this */ + } + set_entity(x, y, etmp1); + if (etmp1->edata) { + e_inview = e_canseemon(etmp1); + if (e_missed(etmp1, TRUE)) { +#ifdef D_DEBUG + pline("%s spared!", E_phrase(etmp1, "are")); +#endif + } else { + if (e_inview) { + if (!is_u(etmp1) && Hallucination) + pline("%s into some heavy metal!", + E_phrase(etmp1, "get")); + else + pline("%s hit by a huge chunk of metal!", + E_phrase(etmp1, "are")); + } else { + if (flags.soundok && !is_u(etmp1) && !is_pool(x,y)) + You_hear("a crushing sound."); +#ifdef D_DEBUG + else + pline("%s from shrapnel", + E_phrase(etmp1, "die")); +#endif + } + killer_format = KILLED_BY_AN; + killer = "collapsing drawbridge"; + e_died(etmp1, e_inview? 3 : 2, CRUSHING); /*no corpse*/ + if(lev1->typ == MOAT) do_entity(etmp1); + } + } +} + +#endif /* OVLB */ + +/*dbridge.c*/