This is dbridge.c in view mode; [Download] [Up]
/* SCCS Id: @(#)dbridge.c 3.0 88/18/12 /* 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 STRONGHOLD static void FDECL(redosym, (int, int)); static void FDECL(get_wall_for_db, (int *, int *)); static struct entity *FDECL(e_at, (int, int)); static void FDECL(m_to_e, (struct monst *, struct entity *)); static void FDECL(u_to_e, (struct entity *)); static void FDECL(set_entity, (int, int, struct entity *)); static char *FDECL(e_nam, (struct entity *)); /* static char *FDECL(Enam, (struct entity *)); */ /* unused */ static char *FDECL(E_phrase, (struct entity *, const char *)); static boolean FDECL(e_survives_at, (struct entity *, int, int)); static void FDECL(e_died, (struct entity *, int, int)); static boolean FDECL(automiss, (struct entity *)); static boolean FDECL(e_missed, (struct entity *, BOOLEAN_P)); static boolean FDECL(e_jumps, (struct entity *)); static void FDECL(do_entity, (struct entity *)); #endif #ifdef OVL0 boolean is_pool(x,y) int x,y; { if(levl[x][y].typ == POOL || levl[x][y].typ == MOAT) return TRUE; #ifdef STRONGHOLD if(levl[x][y].typ == DRAWBRIDGE_UP && (levl[x][y].drawbridgemask & DB_UNDER) == DB_MOAT) return TRUE; #endif return FALSE; } #endif /* OVL0 */ #ifdef STRONGHOLD #ifdef OVL1 void initsym(x,y) int x,y; { char oldseen; struct rm *crm = &levl[x][y]; oldseen = crm->seen; crm->seen = 1; crm->scrsym = news0(x,y); crm->seen = oldseen; } static void redosym(x,y) int x,y; { if(cansee(x,y)) { levl[x][y].seen = 0; /* force prl */ prl(x, y); } else { initsym(x,y); levl[x][y].seen = 0; } } /* * 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->diggable & W_GATEWAY)) return (-1); switch (lev->typ) { case DOOR: case VWALL: 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 (lev->typ == VWALL) break; case HWALL: 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); } #endif /* OVL1 */ #ifdef OVL1 /* * 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( (levl[x][y].typ == VWALL || levl[x][y].typ == HWALL) && levl[x][y].diggable & W_GATEWAY); } #endif /* OVL1 */ #ifdef OVLB /* * 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; } /* * Find the drawbridge wall associated with a drawbridge. */ static 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; uchar wall; x2 = x; y2 = y; switch(dir) { case DB_NORTH: wall = HWALL; y2--; break; case DB_SOUTH: wall = HWALL; y2++; break; case DB_EAST: wall = VWALL; x2++; break; default: impossible("bad direction in create_drawbridge"); /* fall through */ case DB_WEST: wall = VWALL; 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 = wall; /* Beware, drawbridges are non-diggable. */ levl[x2][y2].diggable = (W_NONDIGGABLE | W_GATEWAY); } levl[x][y].drawbridgemask = dir; /* always have DB_MOAT */ initsym(x,y); initsym(x2,y2); 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 struct entity occupants[ENTITIES]; static 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); fflush(stdout); #endif return((entitycnt == ENTITIES)? (struct entity *)0 : &(occupants[entitycnt])); } static void m_to_e(mtmp, etmp) struct monst *mtmp; struct entity *etmp; { etmp->emon = mtmp; if (mtmp) { etmp->ex = mtmp->mx; etmp->ey = mtmp->my; etmp->edata = mtmp->data; } else etmp->edata = (struct permonst *)0; } static void u_to_e(etmp) struct entity *etmp; { etmp->emon = &youmonst; etmp->ex = u.ux; etmp->ey = u.uy; etmp->edata = uasmon; } static 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), etmp); else etmp->edata = (struct permonst *)0; } #ifdef POLYSELF #define is_u(etmp) (etmp->emon == &youmonst) #else #define is_u(x) FALSE #endif /* * WARNING! THE FOLLOWING IS ONLY USEFUL FOR CANSEEMON, OR OTHER FUNCS WHICH * ALWAYS RETURN TRUE FOR U. */ #define e_boolean(etmp, func) (is_u(etmp)? (boolean)TRUE : func(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 char * e_nam(etmp) struct entity *etmp; { return(is_u(etmp)? "you" : mon_nam(etmp->emon)); } /* * Enam is another unused utility routine: E_phrase is preferable. */ /* static char * Enam(etmp) struct entity *etmp; { return(is_u(etmp)? "You" : Monnam(etmp->emon)); } */ /* * Generates capitalized entity name, makes 2nd -> 3rd person conversion on * verb, where necessary. */ static char * E_phrase(etmp, verb) struct entity *etmp; const char *verb; { char wholebuf[80], verbbuf[30]; if (is_u(etmp)) Strcpy(wholebuf, "You"); else Strcpy(wholebuf, 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 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((is_u(etmp) && (Wwalking || Levitation)) || is_swimmer(etmp->edata) || is_flyer(etmp->edata) || is_floater(etmp->edata)); if (is_db_wall(x, y)) return(passes_walls(etmp->edata)); return(TRUE); } static void e_died(etmp, dest, how) struct entity *etmp; int dest, how; { if (is_u(etmp)) { if (how == DROWNING) drown(); else { coord xy; done(how); /* So, you didn't die */ if (!e_survives_at(etmp, etmp->ex, etmp->ey)) { pline("A %s force teleports you away...", Hallucination ? "normal" : "strange"); enexto(&xy, etmp->ex, etmp->ey, etmp->edata); teleds(xy.x, xy.y); } } } else { xkilled(etmp->emon, dest); etmp->edata = (struct permonst *)0; } } /* * These are never directly affected by a bridge or portcullis. */ static boolean automiss(etmp) struct entity *etmp; { return(passes_walls(etmp->edata) || noncorporeal(etmp->edata)); } /* * Does falling drawbridge or portcullis miss etmp? */ static 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->msleep))) /* 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((misses >= rnd(8))? TRUE : FALSE); } /* * Can etmp jump from death? */ static boolean e_jumps(etmp) struct entity *etmp; { int tmp = 4; /* out of 10 */ if (is_u(etmp)? (Sleeping || Fumbling) : (!etmp->emon->mcanmove || etmp->emon->msleep || !etmp->edata->mmove)) 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((tmp >= rnd(10))? TRUE : FALSE); } static 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_boolean(etmp, canseemon); 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)) { char edifice[20]; if (e_inview) { *edifice = '\0'; if ((crm->typ == DRAWBRIDGE_DOWN) || (crm->typ == DRAWBRIDGE_UP)) Strcpy(edifice, "drawbridge"); else if (at_portcullis) Strcpy(edifice, "portcullis"); if (*edifice) pline("The %s passes through %s!", edifice, 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? 2 : 3, 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? 1 : 0, CRUSHING); /* 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)) && (e_at(newx, newy) != etmp)) do_entity(other); #ifdef D_DEBUG pline("Checking existence of %s", e_nam(etmp)); fflush(stdout); #endif if (e_at(oldx, oldy) != etmp) { #ifdef D_DEBUG pline("%s moved or died in recursion somewhere", E_phrase(etmp, "have")); fflush(stdout); #endif return; } } } if (relocates) { #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); } else { u.ux = newx; u.uy = newy; } etmp->ex = newx; etmp->ey = newy; e_inview = e_boolean(etmp, canseemon); } #ifdef D_DEBUG pline("Final disposition of %s", e_nam(etmp)); fflush(stdout); #endif if (is_db_wall(etmp->ex, etmp->ey)) { #ifdef D_DEBUG pline("%s in portcullis chamber", E_phrase(etmp, "are")); fflush(stdout); #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)) if (e_inview && !is_u(etmp)) /* drown() will supply msgs if nec. */ if (Hallucination) pline("%s the moat and disappears.", E_phrase(etmp, "drink")); else pline("%s into the moat.", E_phrase(etmp, "fall")); killer_format = NO_KILLER_PREFIX; killer = "fell from a drawbridge"; e_died(etmp, e_inview? 1 : 0, /* CRUSHING is arbitrary */ (is_pool(etmp->ex, etmp->ey))? DROWNING : CRUSHING); /* corpse */ return; } } /* * Close the drawbridge located at x,y */ void close_drawbridge(x,y) int x,y; { register struct rm *lev1, *lev2; struct obj *otmp, *otmp2; 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)) /* change msgs if you are a w-walker at portcullis */ You("see a drawbridge %s up!", ((u.ux == x2) && (u.uy == y2))? "coming" : "going"); lev1->typ = DRAWBRIDGE_UP; lev2 = &levl[x2][y2]; switch (lev1->drawbridgemask & DB_DIR) { case DB_NORTH: case DB_SOUTH: lev2->typ = HWALL; break; case DB_WEST: case DB_EAST: lev2->typ = VWALL; break; } lev2->diggable = (W_NONDIGGABLE | W_GATEWAY); set_entity(x, y, &(occupants[0])); set_entity(x2, y2, &(occupants[1])); do_entity(&(occupants[0])); do_entity(&(occupants[1])); redosym(x, y); for (otmp = level.objects[x][y]; otmp; otmp = otmp2) { otmp2 = otmp->nexthere; delobj(otmp); } for (otmp = level.objects[x2][y2]; otmp; otmp = otmp2) { otmp2 = otmp->nexthere; delobj(otmp); } redosym(x2, y2); } /* * Open the drawbridge located at x,y */ void open_drawbridge(x,y) int x,y; { register struct rm *lev1, *lev2; 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)) /* change msgs if you are a w-walker at portcullis */ You("see a drawbridge %s down!", ((x2 == u.ux) && (y2 == u.uy))? "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_entity(&(occupants[1])); redosym(x, y); redosym(x2, y2); } /* * Let's destroy the drawbridge located at x,y */ void destroy_drawbridge(x,y) int x,y; { register struct rm *lev1, *lev2; 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) { if (lev1->typ == DRAWBRIDGE_UP) { if (cansee(x2,y2)) pline("The portcullis of the drawbridge falls into the moat!"); else if (flags.soundok) You("hear a loud *SPLASH*!"); } else { if (cansee(x,y)) pline("The drawbridge collapses into the moat!"); else if (flags.soundok) You("hear a loud *SPLASH*!"); } lev1->typ = MOAT; lev1->drawbridgemask = 0; } else { if (cansee(x,y)) pline("The drawbridge disintegrates!"); else You("hear a loud *CRASH*!"); lev1->typ = ROOM; lev1->icedpool = ((lev1->drawbridgemask & DB_ICE) ? ICED_MOAT : 0); } set_entity(x2, y2, etmp2); /* currently, only automissers can be here */ if (etmp2->edata) { e_inview = e_boolean(etmp2, canseemon); if (!automiss(etmp2)) { /* i.e. no-one yet */ 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? 2 : 3, CRUSHING);/* no corpse */ } /* nothing which is vulnerable can survive this */ } lev2->typ = DOOR; lev2->doormask = D_NODOOR; set_entity(x, y, etmp1); e_inview = e_boolean(etmp1, canseemon); if (etmp1->edata) { 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? 0 : 1, CRUSHING); /* corpse */ } } redosym(x,y); redosym(x2,y2); } #endif /* OVLB */ #endif /* STRONGHOLD /**/
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.