/* If you have moved since initially setting some occupations, they * now shouldn't be able to restart. * * The basic rule is that if you are carrying it, you can continue * since it is with you. If you are acting on something at a distance, * your orientation to it must have changed when you moved. * * The exception to this is taking off items, since they can be taken * off in a number of ways in the intervening time, screwing up ordering. * * Currently: Take off all armor. * Picking Locks / Forcing Chests. */ void reset_occupations() { reset_remarm(); reset_pick(); }
/* Called every turn during chest-forcing. The caller must set u.utracked[tos_lock] to the chest in question. */ static int forcelock(void) { struct monst *shkp; boolean costly; struct obj *otmp; struct obj *box = u.utracked[tos_lock]; if (!obj_with_u(box)) return reset_pick(); if (!uwep_can_force()) /* prints the messages; ensures uwep != NULL */ return reset_pick(); if (u.uoccupation_progress[tos_lock]++ >= 50 || nohands(youmonst.data)) { pline(msgc_failrandom, "You give up your attempt to force the lock."); if (!nohands(youmonst.data)) exercise(is_blade(uwep) ? A_DEX : A_STR, TRUE); return reset_pick(); } if (is_blade(uwep)) { if (rn2(1000 - (int)uwep->spe) > (992 - greatest_erosion(uwep) * 10) && !uwep->cursed && !obj_resists(uwep, 0, 99)) { /* for a +0 weapon, probability that it survives an unsuccessful attempt to force the lock is (.992)^50 = .67 */ pline(msgc_substitute, "%sour %s broke!", (uwep->quan > 1L) ? "One of y" : "Y", xname(uwep)); useup(uwep); pline_implied(msgc_failcurse, "You can't exactly force that lock now."); exercise(A_DEX, TRUE); return reset_pick(); } } else /* blunt */ wake_nearby(FALSE); /* due to hammering on the container */ if (rn2(100) >= objects[uwep->otyp].oc_wldam * 2) return 1; /* still busy */ pline(msgc_actionok, "You succeed in forcing the lock."); box->olocked = 0; box->obroken = 1; costly = (*u.ushops && costly_spot(youmonst.mx, youmonst.my)); shkp = costly ? shop_keeper(level, *u.ushops) : 0; if (!is_blade(uwep) && !rn2(3)) { long loss = 0L; pline(msgc_substitute, "In fact, you've totally destroyed %s.", the(xname(box))); /* Put the contents on ground at the hero's feet. */ while ((otmp = box->cobj) != 0) { obj_extract_self(otmp); if (!rn2(3) || otmp->oclass == POTION_CLASS) { chest_shatter_msg(otmp); if (costly) loss += stolen_value(otmp, youmonst.mx, youmonst.my, (boolean) shkp->mpeaceful, TRUE); if (otmp->quan == 1L) { obfree(otmp, NULL); continue; } useup(otmp); } if (box->otyp == ICE_BOX && otmp->otyp == CORPSE) { otmp->age = moves - otmp->age; /* actual age */ start_corpse_timeout(otmp); } place_object(otmp, level, youmonst.mx, youmonst.my); stackobj(otmp); } if (costly) loss += stolen_value(box, youmonst.mx, youmonst.my, (boolean) shkp->mpeaceful, TRUE); if (loss) pline(msgc_unpaid, "You owe %ld %s for objects destroyed.", loss, currency(loss)); delobj(box); } else { if (costly) { struct obj *cobjbak = box->cobj; box->cobj = (struct obj *)0; verbalize(msgc_unpaid, "You damage it, you bought it!"); bill_dummy_object(box); box->cobj = cobjbak; } } exercise(is_blade(uwep) ? A_DEX : A_STR, TRUE); return reset_pick(); }
/* 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; }
/* Called every turn during lock-picking. The caller must set u.utracked[tos_lock] appropriately: &zeroobj for a door, an object for a box. For a door, u.utracked_location[tl_lock] must also be set. */ static int picklock(void) { int chance = get_unlock_chance(); int x = u.utracked_location[tl_lock].x; int y = u.utracked_location[tl_lock].y; struct rm *door = NULL; if (u.utracked[tos_lock] != &zeroobj) { if (!obj_with_u(u.utracked[tos_lock])) return reset_pick(); } else { /* door */ door = &(level->locations[x][y]); switch (door->doormask) { case D_NODOOR: pline(msgc_cancelled, "This doorway has no door."); return reset_pick(); case D_ISOPEN: pline(msgc_cancelled, "You cannot lock an open door."); return reset_pick(); case D_BROKEN: pline(msgc_cancelled, "This door is broken."); return reset_pick(); } } if (!chance) { pline(msgc_interrupted, "You seem to have lost your unlocking tools."); return reset_pick(); } if (u.uoccupation_progress[tos_lock]++ >= 50 || nohands(youmonst.data)) { pline(msgc_failrandom, "You give up your attempt at %s.", lock_action()); if (!nohands(youmonst.data)) exercise(A_DEX, TRUE); /* even if you don't succeed */ return reset_pick(); } if (rn2(100) >= chance) return 1; /* still busy */ pline(msgc_actionok, "You succeed in %s.", lock_action()); if (door) { if (door->doormask & D_TRAPPED) { b_trapped("door", FINGER); door->doormask = D_NODOOR; unblock_point(x, y); if (*in_rooms(level, x, y, SHOPBASE)) add_damage(x, y, 0L); } else if (door->doormask & D_LOCKED) door->doormask = D_CLOSED; else door->doormask = D_LOCKED; /* player now knows the door's open/closed status, and its locked/unlocked status, and also that it isn't trapped (it would have exploded otherwise); thus, we can safely fully spoil the door's stats (the door is the background of the door's location) */ magic_map_background(x, y, TRUE); } else { u.utracked[tos_lock]->olocked = !u.utracked[tos_lock]->olocked; if (u.utracked[tos_lock]->otrapped) chest_trap(&youmonst, u.utracked[tos_lock], FINGER, FALSE); } exercise(A_DEX, TRUE); return reset_pick(); }