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; } }
/* So you want music... */ int do_play_instrument(struct obj *instr, const struct nh_cmd_arg *arg) { char c = 'y'; const char *buf; int x, y; boolean ok; if (Underwater) { pline(msgc_cancelled, "You can't play music underwater!"); return 0; } if (Upolyd && !can_blow_instrument(youmonst.data) && (instr->otyp == BUGLE || instr->otyp == WOODEN_FLUTE || instr->otyp == MAGIC_FLUTE || instr->otyp == TOOLED_HORN || instr->otyp == FIRE_HORN || instr->otyp == FROST_HORN)) { pline(msgc_cancelled, "You are incapable of playing %s in your current form!", the(xname(instr))); return 0; } if (instr->otyp != LEATHER_DRUM && instr->otyp != DRUM_OF_EARTHQUAKE) { c = yn("Improvise?"); } if (c == 'n') { if (u.uevent.uheard_tune == 2 && yn("Play the passtune?") == 'y') { buf = msg_from_string(gamestate.castle_tune); } else { /* Note: This is explicitly not getarglin(); we don't want command repeat to repeat the tune. */ buf = getlin("What tune are you playing? [5 notes, A-G]", FALSE); if (*buf == '\033') buf = ""; buf = msgmungspaces(buf); /* convert to uppercase and change any "H" to the expected "B" */ buf = msgcaseconv(buf, highc_htob, highc_htob, highc_htob); } pline(msgc_occstart, "You extract a strange sound from %s!", the(xname(instr))); /* Check if there was the Stronghold drawbridge near and if the tune conforms to what we're waiting for. */ if (Is_stronghold(&u.uz)) { exercise(A_WIS, TRUE); /* just for trying */ if (!strcmp(buf, gamestate.castle_tune)) { /* Search for the drawbridge */ for (y = youmonst.my - 1; y <= youmonst.my + 1; y++) for (x = youmonst.mx - 1; x <= youmonst.mx + 1; x++) if (isok(x, y)) if (find_drawbridge(&x, &y)) { /* tune now fully known */ u.uevent.uheard_tune = 2; if (level->locations[x][y].typ == DRAWBRIDGE_DOWN) close_drawbridge(x, y); else open_drawbridge(x, y); return 1; } } else if (canhear()) { if (u.uevent.uheard_tune < 1) u.uevent.uheard_tune = 1; /* Okay, it wasn't the right tune, but perhaps we can give the player some hints like in the Mastermind game */ ok = FALSE; for (y = youmonst.my - 1; y <= youmonst.my + 1 && !ok; y++) for (x = youmonst.mx - 1; x <= youmonst.mx + 1 && !ok; x++) if (isok(x, y)) if (IS_DRAWBRIDGE(level->locations[x][y].typ) || is_drawbridge_wall(x, y) >= 0) ok = TRUE; if (ok) { /* There is a drawbridge near */ int tumblers, gears; boolean matched[5]; tumblers = gears = 0; for (x = 0; x < 5; x++) matched[x] = FALSE; for (x = 0; x < (int)strlen(buf); x++) if (x < 5) { if (buf[x] == gamestate.castle_tune[x]) { gears++; matched[x] = TRUE; } else for (y = 0; y < 5; y++) if (!matched[y] && buf[x] == gamestate.castle_tune[y] && buf[y] != gamestate.castle_tune[y]) { tumblers++; matched[y] = TRUE; break; } } if (tumblers) if (gears) You_hear(msgc_hint, "%d tumbler%s click and %d gear%s turn.", tumblers, plur(tumblers), gears, plur(gears)); else You_hear(msgc_hint, "%d tumbler%s click.", tumblers, plur(tumblers)); else if (gears) { You_hear(msgc_hint, "%d gear%s turn.", gears, plur(gears)); /* could only get `gears == 5' by playing five correct notes followed by excess; otherwise, tune would have matched above */ if (gears == 5) u.uevent.uheard_tune = 2; } } } } return 1; } else return do_improvisation(instr, arg); }