/* * Does falling drawbridge or portcullis miss etmp? */ static boolean e_missed(struct entity *etmp, boolean chunks) { int misses; 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(level, etmp->ex, etmp->ey)) misses = 2; /* sitting ducks */ else misses = 0; if (is_db_wall(etmp->ex, etmp->ey)) misses -= 3; /* less airspace */ return (boolean)((misses >= rnd(8))? TRUE : FALSE); }
/* * Does falling drawbridge or portcullis miss etmp? */ static boolean e_missed(struct entity *etmp, boolean chunks) { int misses; if (automiss(etmp)) return TRUE; if (flying(etmp->emon) && (is_u(etmp) ? !u_helpless(hm_all) : (etmp->emon->mcanmove && !etmp->emon->msleeping))) /* flying requires mobility */ misses = 5; /* out of 8 */ else if (levitates(etmp->emon) || (is_u(etmp) && Levitation)) /* doesn't require mobility */ misses = 3; else if (chunks && is_pool(level, etmp->ex, etmp->ey)) misses = 2; /* sitting ducks */ else misses = 0; if (is_db_wall(etmp->ex, etmp->ey)) misses -= 3; /* less airspace */ enum rng rng = is_u(etmp) ? rng_survive_dbridge : rng_main; return (boolean) ((misses > rn2_on_rng(8, rng)) ? TRUE : FALSE); }
/* * Simple-minded "can it be here?" routine */ static boolean e_survives_at(struct entity *etmp, int x, int y) { if (noncorporeal(etmp->edata)) return TRUE; if (is_pool(level, x, y)) return (boolean) (waterwalks(etmp->emon) || unbreathing(etmp->emon) || swims(etmp->emon) || flying(etmp->emon) || levitates(etmp->emon)); /* must force call to lava_effects in e_died if is_u */ if (is_lava(level, x, y)) return (boolean) (likes_lava(etmp->edata) || flying(etmp->emon) || levitates(etmp->emon)); if (is_db_wall(x, y)) return !!phasing(etmp->emon); return TRUE; }
/* * Simple-minded "can it be here?" routine */ static boolean e_survives_at(struct entity *etmp, int x, int y) { if (noncorporeal(etmp->edata)) return TRUE; if (is_pool(level, 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(level, 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; }
/* * Can etmp jump from death? */ static boolean e_jumps(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 */ return (boolean)((tmp >= rnd(10))? TRUE : FALSE); }
/* * Can etmp jump from death? */ static boolean e_jumps(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 */ enum rng rng = is_u(etmp) ? rng_survive_dbridge : rng_main; return (boolean) ((tmp > rn2_on_rng(10, rng)) ? TRUE : FALSE); }
static void do_entity(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(level, etmp); oldx = etmp->ex; oldy = etmp->ey; at_portcullis = is_db_wall(oldx, oldy); crm = &level->locations[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)); if (is_u(etmp)) spoteffects(FALSE); return; } if (e_missed(etmp, FALSE)) { if (at_portcullis) pline("The portcullis misses %s!", e_nam(etmp)); if (e_survives_at(etmp, oldx, oldy)) return; else { 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; } 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); } } /* * 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. */ newx = oldx; newy = oldy; find_drawbridge(&newx, &newy); if ((newx == oldx) && (newy == oldy)) get_wall_for_db(&newx, &newy); 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); if (e_survives_at(other, newx, newy) && automiss(other)) { relocates = FALSE; /* "other" won't budge */ } else { while ((e_at(newx, newy) != 0) && (e_at(newx, newy) != etmp)) do_entity(other); if (e_at(oldx, oldy) != etmp) { return; } } } if (relocates && !e_at(newx, newy)) {/* if e_at() entity = worm tail */ if (!is_u(etmp)) { remove_monster(level, 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(level, etmp); } if (is_db_wall(etmp->ex, etmp->ey)) { if (e_inview) { if (is_u(etmp)) { pline("You tumble towards the closed portcullis!"); if (automiss(etmp)) pline("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; } } else { if (is_pool(level, 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; } if (is_pool(level, etmp->ex, etmp->ey) || is_lava(level, etmp->ex, etmp->ey)) if (e_inview && !is_u(etmp)) { /* drown() will supply msgs if nec. */ boolean lava = is_lava(level, 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(level, etmp->ex, etmp->ey)) ? DROWNING : (is_lava(level, etmp->ex, etmp->ey)) ? BURNING : CRUSHING); /*no corpse*/ return; } }
/* try to open a door */ int doopen(const struct nh_cmd_arg *arg) { coord cc; struct rm *door; struct monst *mtmp; schar dx, dy, dz; if (nohands(youmonst.data)) { pline(msgc_cancelled, "You can't open, close, or unlock anything " "-- you have no hands!"); return 0; } if (u.utrap && u.utraptype == TT_PIT) { pline(msgc_cancelled, "You can't reach over the edge of the pit."); return 0; } if (!getargdir(arg, NULL, &dx, &dy, &dz)) return 0; cc.x = youmonst.mx + dx; cc.y = youmonst.my + dy; if (!isok(cc.x, cc.y)) return 0; if ((cc.x == youmonst.mx) && (cc.y == youmonst.my)) return 0; if ((mtmp = m_at(level, cc.x, cc.y)) && mtmp->m_ap_type == M_AP_FURNITURE && (mtmp->mappearance == S_hcdoor || mtmp->mappearance == S_vcdoor) && !Protection_from_shape_changers) { stumble_onto_mimic(mtmp, cc.x - youmonst.mx, cc.y - youmonst.my); return 1; } door = &level->locations[cc.x][cc.y]; if (!IS_DOOR(door->typ)) { if (is_db_wall(cc.x, cc.y)) { pline(msgc_cancelled, "There is no obvious way to open the drawbridge."); return 0; } pline(msgc_mispaste, "You %s no door there.", Blind ? "feel" : "see"); return 0; } if (door->doormask == D_ISOPEN) { struct nh_cmd_arg newarg; arg_from_delta(dx, dy, dz, &newarg); return doclose(&newarg); } if (!(door->doormask & D_CLOSED)) { const char *mesg; switch (door->doormask) { case D_BROKEN: mesg = " is broken"; break; case D_NODOOR: mesg = "way has no door"; break; case D_ISOPEN: mesg = " is already open"; break; default: if (last_command_was("open") && door->mem_door_l) { /* With the "open" command given explicitly (rather than implicitly via doorbumping), unlock the door. */ struct obj *bestpick = get_current_unlock_tool(); struct nh_cmd_arg newarg; arg_from_delta(dx, dy, dz, &newarg); if (!bestpick) pline(msgc_cancelled, "You have nothing to unlock that with."); else if (!bestpick->lastused) /* not msgc_controlhelp, or many players would get no message */ pline(msgc_hint, "Use an unlocking tool manually so I know " "which one you want to use."); else return pick_lock(bestpick, &newarg); } door->mem_door_l = 1; map_background(cc.x, cc.y, TRUE); mesg = " is locked"; break; } pline(msgc_cancelled, "This door%s.", mesg); if (Blind) feel_location(cc.x, cc.y); return 0; } if (verysmall(youmonst.data)) { pline(msgc_cancelled, "You're too small to pull the door open."); return 0; } /* door is known to be CLOSED */ if (rnl(20) < (ACURRSTR + ACURR(A_DEX) + ACURR(A_CON)) / 3) { pline(msgc_actionok, "The door opens."); if (door->doormask & D_TRAPPED) { b_trapped("door", FINGER); door->doormask = D_NODOOR; if (*in_rooms(level, cc.x, cc.y, SHOPBASE)) add_damage(cc.x, cc.y, 0L); } else door->doormask = D_ISOPEN; if (Blind) feel_location(cc.x, cc.y); /* the hero knows she opened it */ else newsym(cc.x, cc.y); unblock_point(cc.x, cc.y); /* vision: new see through there */ } else { exercise(A_STR, TRUE); door->mem_door_l = 1; map_background(cc.x, cc.y, TRUE); pline(msgc_failrandom, "The door resists!"); } return 1; }