/* a real safe */
#include <mudlib.h>
inherit CONTAINER;
#include <i_to_str.h>
#define REAL (string)this_player()->query_name(1)
object cracker; /* current user of safe */
object stethoscope; /* to crack the safe */
int *code; /* code numbers */
int *direction; /* 0 right, 1 left */
int *current_position;
int *current_direction;
mixed *safe_crackers;
int safe_difficulty;
status safe_lock;
string current_tumbler_position();
void reset(status arg) {
current_position = ({});
current_direction = ({});
safe_crackers = ({});
if(arg) return;
set_name("safe");
set_short("Safe");
set_long("It is a rather small safe, formed as a cube. On the\n"+
"safe is a numbered tumbler. Next to the tumbler is a\n"+
"handle. Below the handle is a small key hole.\n");
set_key_id("no_key");
set_closed(1);
safe_lock = 1;
/* combination can be made larger */
code = ({ random(9)+1, random(9)+1, random(9)+1, });
direction = ({ random(2), random(2), random(2), });
safe_difficulty = 5;
}
status set_safe_lock(status i) { safe_lock = i; }
int set_safe_difficulty(int i) { safe_difficulty = i; }
status query_safe_lock() { safe_lock; }
int query_safe_difficulty() { safe_difficulty; }
int *query_code() { return code; }
int *query_direction() { return direction; }
int *query_current_position() { return current_position; }
int *query_current_direction() { return current_direction; }
mixed *query_safe_crackers() { return safe_crackers; }
status look_at(string str) {
if(str == "tumbler" || str == "dial" || str == "brass dial") {
write("The tumbler is a small brass dial on the front of the "+
query_name() +".\nThere are numbers from 1 to 9 marked on it.\n"+
"You can turn the tumbler.\n");
write(current_tumbler_position());
return 1;
}
if(str == "handle") {
write("You turn the handle to open the safe.\n");
return 1;
}
if(str == "key hole" || str == "key") {
if(query_key_id() == "no_key") {
write("The key hole is a dummy!\nIt doesn't need a key.\n");
return 1;
}
else {
return ::look_at(query_lock_id());
}
}
return ::look_at(str);
}
void init() {
::init();
add_action("turn", "turn");
add_action("safe_crack", "safecrack");
}
void container_open(string verb) {
if(closed == 1) {
if(safe_lock) {
write("The safe tumbler is still locked.\n");
return;
}
}
::container_open(verb);
}
status turn(string str) {
int number;
status wheel_direction;
object ob;
string safe;
if(cracker && cracker != this_player()) {
if(environment() == cracker || environment() == environment(cracker)) {
write(cracker->query_name() +" is currently using the "+
query_name() +".\n");
return 1;
}
}
cracker = this_player();
if(!str) {
notify_fail("turn what?\n");
return 0;
}
if(sscanf(str, "handle on %s", safe)) {
if(!(ob = present(safe, environment(this_player())))) {
if(!(ob = present(safe, this_player()))) {
write("There is no "+ safe +" here.\n");
return 1;
}
}
if(ob != this_object()) {
notify_fail("You can't do that!\n");
return 0;
}
if(is_locked && query_key_id() != "no_key") {
write("The "+ safe +" is locked. You need a "+ query_key_id() +
" to unlock it.\n");
return 1;
}
if(closed == 1)
container_open("open");
else
container_open("close");
return 1;
}
if(sscanf(str, "handle%s", str)) {
notify_fail("turn handle on what?\n");
return 0;
}
if(sscanf(str,"tumbler left %d on %s", number, safe)) wheel_direction = 1;
sscanf(str,"tumbler right %d on %s", number, safe);
if(!number) {
notify_fail("turn tumbler left|right <number> on <item>?\n");
return 0;
}
if(!(ob = present(safe, environment(this_player())))) {
if(!(ob = present(safe, this_player()))) {
write("There is no "+ safe +" here.\n");
return 1;
}
}
if(ob != this_object()) {
notify_fail("You can't do that!\n");
return 0;
}
current_position += ({ number, });
current_direction += ({ wheel_direction, });
if(sizeof(current_position) == sizeof(code)) {
int i;
for(i = 0; i < sizeof(code); i++) {
if(current_position[i] == code[i]
&& current_direction[i] == direction[i]) continue;
write("The tumbler swings around to the zero position.\n");
current_position = ({});
current_direction = ({});
return 1;
}
write("Click! You hear the "+ query_name() +"'s lock bars slide.\n");
safe_lock = !safe_lock;
if(safe_lock)
write("The safe locks.\n");
else
write("The safe unlocks.\n");
}
else {
write("You turn the tumbler "+ int_to_str(number) +" "+
((wheel_direction) ? "left" : "right") +".\n");
}
return 1;
}
string current_tumbler_position() {
string str;
int i;
str = "The tumbler positon is at ";
if(!sizeof(current_position)) str += "zero";
for(i = 0; i < sizeof(current_position); i++) {
if(i && i != sizeof(current_position)-1)
str += ", ";
else if(i)
str += " and ";
str += int_to_str(current_position[i]);
if(current_direction[i])
str += " left";
else
str += " right";
}
return str +".\n";
}
string query_combination() {
string str;
int i;
str = "The tumbler combination is ";
for(i = 0; i < sizeof(code); i++) {
if(i && i != sizeof(code)-1)
str += ", ";
else if(i)
str += " and ";
str += int_to_str(code[i]);
if(direction[i])
str += " left";
else
str += " right";
}
return str;
}
/***************************************************************************/
status safe_crack(string str) {
int number, i;
string safe;
status wheel_direction;
object ob;
if(cracker && cracker != this_player()) {
if(environment() == cracker || environment() == environment(cracker)) {
write(cracker->query_name() +" is currently using the "+
query_name() +".\n");
return 1;
}
}
cracker = this_player();
if(!this_player()->query_locks()) {
write("You are not skilled at safe cracking.\n");
return 1;
}
if(!stethoscope || !present(stethoscope, this_player())) {
write("You need to apply a stethoscope to the "+ query_name() +"\n");
return 1;
}
if(!str) {
notify_fail("safecrack tumbler left|right <number> on <item>?\n");
return 0;
}
if(sscanf(str,"tumbler left %d on %s", number, safe)) wheel_direction = 1;
sscanf(str,"tumbler right %d on %s", number, safe);
if(!number) {
notify_fail("turn tumbler left|right <number> on <item>?\n");
return 0;
}
if(!(ob = present(safe, environment(this_player())))) {
if(!(ob = present(safe, this_player()))) {
write("There is no "+ safe +" here.\n");
return 1;
}
}
if(ob != this_object()) {
notify_fail("You can't do that!\n");
return 0;
}
if(code[sizeof(current_position)] != number) {
write("You hear a low clicking noise.\n");
return 1;
}
if(direction[sizeof(current_position)] != wheel_direction) {
write("You hear a low clicking noise.\n");
return 1;
}
if((i = member_array(REAL, safe_crackers)) == -1) {
safe_crackers += ({ REAL, ({}), });
i = member_array(REAL, safe_crackers);
}
while(sizeof(current_position) > sizeof(safe_crackers[i+1])) {
safe_crackers[i+1] += ({ 0, });
}
if(sizeof(safe_crackers[i+1]) != sizeof(current_position)) {
if(safe_crackers[i+1][sizeof(current_position)])
write("You hear a small thunk noise again.\n");
else
write("You hear a low clicking noise.\n");
return 1;
}
if(random((int)this_player()->query_locks()+1) >= random(safe_difficulty+1)){
write("You hear a small thunk noise.\n");
safe_crackers[i+1] += ({ 1, }); /* successful */
}
else {
write("You hear a low clicking noise.\n");
safe_crackers[i+1] += ({ 0, }); /* failed */
}
return 1;
}
/* can use stethoscope on this */
status use_stethoscope(object arg) {
stethoscope = arg;
return 1;
}