static void describe_bg(int x, int y, int bg, char *buf) { if (!bg) return; switch(bg) { case S_altar: if (!In_endgame(&u.uz)) sprintf(buf, "%s altar", align_str(Amask2align(level->locations[x][y].altarmask & ~AM_SHRINE))); else sprintf(buf, "aligned altar"); break; case S_ndoor: if (is_drawbridge_wall(x, y) >= 0) strcpy(buf,"open drawbridge portcullis"); else if ((level->locations[x][y].doormask & ~D_TRAPPED) == D_BROKEN) strcpy(buf,"broken door"); else strcpy(buf,"doorway"); break; case S_cloud: strcpy(buf, Is_airlevel(&u.uz) ? "cloudy area" : "fog/vapor cloud"); break; default: strcpy(buf, defexplain[bg]); break; } }
/* * Return true with x,y pointing to the drawbridge if x,y initially indicate * a drawbridge or drawbridge wall. */ boolean find_drawbridge(int *x, int *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; }
/* pick a lock on a chest or door with a given object */ int pick_lock(struct obj *pick, const struct nh_cmd_arg *arg) { int picktyp, c; coord cc; schar dx, dy, dz; struct rm *door; struct obj *otmp; const char *qbuf; 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; picktyp = pick->otyp; pick->lastused = moves; /* Check whether we're resuming an interrupted previous attempt. For a floor pick, we have u.utracked[tos_lock] as a non-zeroobj and dx and dy as 0. For a door, we have u.utracked_location[tl_lock] specifying the location and u.utracked[tos_lock] as &zeroobj. */ if (u.uoccupation_progress[tos_lock] && ((u.utracked_location[tl_lock].x == cc.x && u.utracked_location[tl_lock].y == cc.y && u.utracked[tos_lock] == &zeroobj) || (dx == 0 && dy == 0 && u.utracked[tos_lock] != &zeroobj))) { static const char no_longer[] = "Unfortunately, you can no longer %s %s."; if (nohands(youmonst.data)) { const char *what = (picktyp == LOCK_PICK) ? "pick" : "key"; if (picktyp == CREDIT_CARD) what = "card"; pline(msgc_interrupted, no_longer, "hold the", what); return reset_pick(); } else if (u.utracked[tos_lock] != &zeroobj && !can_reach_floor()) { pline(msgc_interrupted, no_longer, "reach the", "lock"); return reset_pick(); } else { const char *action = lock_action(); if (turnstate.continue_message) pline(msgc_occstart, "You resume your attempt at %s.", action); one_occupation_turn(picklock, "picking the lock", occ_lock); return 1; } } if (nohands(youmonst.data)) { pline(msgc_cancelled, "You can't hold %s -- you have no hands!", doname(pick)); return 0; } if ((picktyp != LOCK_PICK && picktyp != CREDIT_CARD && picktyp != SKELETON_KEY)) { impossible("picking lock with object %d?", picktyp); return 0; } if (!dx && !dy) { /* pick lock on a container */ const char *verb; boolean it; int count; if (dz < 0) { pline(msgc_cancelled, "There isn't any sort of lock up %s.", Levitation ? "here" : "there"); return 0; } else if (is_lava(level, youmonst.mx, youmonst.my)) { pline(msgc_cancelled, "Doing that would probably melt your %s.", xname(pick)); return 0; } else if (is_pool(level, youmonst.mx, youmonst.my) && !Underwater) { /* better YAFM - AIS */ pline(msgc_cancelled, "Canals might have locks, but this water doesn't."); return 0; } count = 0; c = 'n'; /* in case there are no boxes here */ for (otmp = level->objects[cc.x][cc.y]; otmp; otmp = otmp->nexthere) if (Is_box(otmp)) { ++count; if (!can_reach_floor()) { pline(msgc_cancelled, "You can't reach %s from up here.", the(xname(otmp))); return 0; } it = 0; if (otmp->obroken) verb = "fix"; else if (!otmp->olocked) verb = "lock", it = 1; else if (picktyp != LOCK_PICK) verb = "unlock", it = 1; else verb = "pick"; qbuf = msgprintf( "There is %s here, %s %s?", safe_qbuf("", sizeof ("There is here, unlock its lock?"), doname(otmp), an(simple_typename(otmp->otyp)), "a box"), verb, it ? "it" : "its lock"); c = ynq(qbuf); if (c == 'q') return 0; if (c == 'n') continue; if (otmp->obroken) { pline(msgc_cancelled, "You can't fix its broken lock with %s.", doname(pick)); return 0; } else if (picktyp == CREDIT_CARD && !otmp->olocked) { /* credit cards are only good for unlocking */ pline(msgc_cancelled, "You can't do that with %s.", doname(pick)); return 0; } u.utracked[tos_lock] = otmp; u.uoccupation_progress[tos_lock] = 0; break; } if (c != 'y') { if (!count) pline(msgc_cancelled, "There doesn't seem to be any sort of lock here."); return 0; /* decided against all boxes */ } } else { /* pick the lock in a door */ struct monst *mtmp; if (u.utrap && u.utraptype == TT_PIT) { pline(msgc_cancelled, "You can't reach over the edge of the pit."); return 0; } door = &level->locations[cc.x][cc.y]; if ((mtmp = m_at(level, cc.x, cc.y)) && canseemon(mtmp)) { if (picktyp == CREDIT_CARD && (mx_eshk(mtmp) || mtmp->data == &mons[PM_ORACLE])) verbalize(msgc_npcvoice, "No checks, no credit, no problem."); else pline(msgc_mispaste, "I don't think %s would appreciate that.", mon_nam(mtmp)); return 0; } if (mtmp && (mtmp->m_ap_type == M_AP_FURNITURE) && (mtmp->mappearance == S_hcdoor || mtmp->mappearance == S_vcdoor) && !Protection_from_shape_changers) { stumble_onto_mimic(mtmp, dx, dy); return 1; } if (!IS_DOOR(door->typ)) { if (is_drawbridge_wall(cc.x, cc.y) >= 0) pline(msgc_cancelled, "You %s no lock on the drawbridge.", Blind ? "feel" : "see"); else pline(msgc_mispaste, "You %s no door there.", Blind ? "feel" : "see"); return 0; } switch (door->doormask) { case D_NODOOR: pline(msgc_cancelled, "This doorway has no door."); return 0; case D_ISOPEN: pline(msgc_cancelled, "You cannot lock an open door."); return 0; case D_BROKEN: pline(msgc_cancelled, "This door is broken."); return 0; default: /* credit cards are only good for unlocking */ if (picktyp == CREDIT_CARD && !(door->doormask & D_LOCKED)) { pline(msgc_cancelled, "You can't lock a door with a credit card."); return 0; } /* At this point, the player knows that the door is a door, and whether it's locked, but not whether it's trapped; to do this, we set the mem_door_l flag and call map_background, which will clear it if necessary (i.e. not a door after all). */ level->locations[cc.x][cc.y].mem_door_l = 1; map_background(cc.x, cc.y, TRUE); u.utracked[tos_lock] = &zeroobj; u.utracked_location[tl_lock] = cc; u.uoccupation_progress[tos_lock] = 0; } } one_occupation_turn(picklock, "picking the lock", occ_lock); return 1; }
/* 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); }