// /std/basic/close_lock /* -*- LPC -*- */ /* * $Locker: $ * $Id: close_lock.c,v 1.39 2003/03/06 11:04:14 runtime Exp $ * */ /** * This class keeps track of whether things are open or closed, * locked or unlocked. Trap functions that are triggered when * an object is opened or unlocked are also handled. When * you need an object that is locked you should remember to * use set_key() and set_difficulty(). When cloning an object * and moving other objects inside it you should remember to * make sure the object is open before moving things into it, * and then close it if neccessary. * * @author Gototh */ #include <move_failures.h> #include <tasks.h> #include <clothing.h> #define MAX_INVENT 40 #define C_CLOSED 1 #define C_TRANS 2 #define C_OPAQUE 1 #define LOCKED 1 #define CLOSED 2 #define CLOSED_LOCKED 3 #define SKILL "covert.manipulation.sleight-of-hand" #define STEALTH_NONE 0 #define STEALTH_FAILED 1 #define STEALTH_SUCCEEDED 2 #define TP this_player() #define TO this_object() int do_open(); int do_close(); int do_lock(object *keys); int do_unlock(object *keys); private int _locked; private int _closed; private string _pick_skill; private nosave int _stuck; private nosave int _trans; private nosave int _difficulty; private nosave int _autolock; private nosave string _trap_open_func; private nosave string _trap_lock_func; private nosave object _trap_open_ob; private nosave object _trap_lock_ob; private nosave mixed _key; /** * @ignore yes */ void create() { _difficulty = 5; _pick_skill = "covert.lockpick.doors"; } /* create() */ /** * This sets the object as being open. Note that to find out * if an object is open we use query_closed() which will return * 0 if the object is open. * * @see set_closed() * @see query_closed() */ void set_open() { if (!_closed) return; _closed = 0; _locked = 0; if (environment()) { event( ({ environment(), TO, }), "open", TP); } else if (TO->query_my_room()) { event( ({ TO->query_my_room(), TO, }), "open", TP); } } /* set_open() */ /** * This sets the object as being closed. If you are moving * objects into the object, make sure that you do not set * the object as closed and then try to move the objects in. * Move the objects in and then close the object. * * @see set_open() * @see query_closed() */ void set_closed() { if (_closed) return; _closed = 1; if (environment()) { event( ({ environment(), TO, }), "close", TP); } else if (TO->query_my_room()) { event( ({ TO->query_my_room(), TO, }), "close", TP); } } /* set_closed() */ /** * This function tells you whether an object is open or closed. * It returns 1 if the object is closed, and 0 if the object is open. * * @return Return 1 if closed, 0 if open. * @see set_open() * @see set_closed() */ int query_closed() { return _closed; } /* query_closed() */ /** * This function tells you whether an object is open or closed. * It returns 1 if the object is open and 0 if the object is closed. * * @return Return 1 if open, 0 if closed. * @see set_open() * @see set_closed() */ int query_open() { return !_closed; } /** * This sets the object as being locked. Remember to set the * object as being closed before using this. In most cases you * should probably use set_key() to set the key which opens the * lock and use set_difficulty() to set the skill level required * to pick the lock. * * @see set_open() * @see set_closed() * @see set_key() * @see set_difficulty() */ void set_locked() { if (!_closed) return; if (_locked) return; _locked = 1; if (environment()) { event( ({ environment(), TO, }), "lock", TP); } else if (TO->query_my_room()) { event( ({ TO->query_my_room(), TO, }), "lock", TP); } } /* set_locked() */ /** * This sets the object as being unlocked. * * @see set_locked() */ void set_unlocked() { if (!_locked) return; _locked = 0; if (environment()) { event( ({ environment(), TO, }), "unlock", TP); } else if (TO->query_my_room()) { event( ({ TO->query_my_room(), TO, }), "unlock", TP); } } /* set_unlocked() */ /** * This tells us whether an object is locked. It returns 1 if * the object is locked and 0 is the object is unlocked. * * @see set_locked() * @see set_unlocked() * @return Return 1 if locked and 0 if unlocked. */ int query_locked() { return _locked; } /* query_locked() */ /** * This tells us whether an object is unlocked. It returns 1 if * the object is unlocked and 0 is the object is locked. * * @see set_locked() * @see set_unlocked() * @return Return 1 if unlocked and 0 if locked. */ int query_unlocked() { return !_locked; } /* query_unlocked() */ /** * This sets the object so that it will lock automatically when closed. * * @param number 1 for autolock, 0 for no autolock. */ void set_autolock(int number) { _autolock = number; } /** * This tells us whether an object will lock automatically when closed. * * @see set_autolock() * @return 1 for true 0 for false; */ int query_autolock() { return _autolock; } /** * This sets the property of the key that will unlock the object. This can * be a string, or a function pointer which will evaluate to a string. * * @see set_locked() * @param val The property the key must have to unlock the object. */ void set_key(mixed val) { if (!stringp(val) && !functionp(val)) return; _key = val; } /* set_key() */ /** * This returns the property of the key that will unlock the * object. * * @see set_key() */ string query_key() { if (stringp(_key)) return _key; if (functionp(_key)) return evaluate(_key); return 0; } /* query_key() */ /** * This sets the skill that will be used when attempting to * pick the lock. By default it is set to "covert.lockpick.safes" * so it need only be changed if you want to check a different * skill. * * @see query_pick_skill() * @see set_difficulty() * @param str The skill to use when picking the lock. */ void set_pick_skill(string str) { _pick_skill = str; } /* set_pick_skill() */ /** * This returns the name of the skill which is used when * determining if the object can be unlocked. * * @see set_pick_skill() */ string query_pick_skill() { return _pick_skill; } /* query_pick_skill() */ /** * This sets how difficult the lock on an object is to pick. * By default it is set to 200. * * @see query_difficulty() * @param i The difficulty of the lock to pick. */ void set_difficulty(int i) { _difficulty = i; } /* set_difficulty() */ /** * This returns the difficulty picking the lock on the object. * By default it is set to 200 unless it has been changed with * set_difficulty(). * * @see set_difficulty() */ int query_difficulty() { return _difficulty; } /* query_difficulty() */ /** * This can set an object as being stuck, or make it unstuck. * Stuck objects cannot be opened, closed, locked or unlocked. * Using set_stuck(1) will make the object stuck, and set_stuck(0) * will unstick it. * * @see query_stuck() * @param i Use 1 to make the object stuck, and 0 to unstick it. */ void set_stuck(int i) { _stuck = i; } /* set_stuck() */ /** * This tells you whether an object is stuck or not. It will * return 1 if the object is stuck, and 0 if the object is not * stuck. * * @see set_stuck() * @return Return 1 if the object is stuck, and 0 if it is not. */ int query_stuck() { return _stuck; } /* query_stuck() */ /** * This sets the object as being transparent. * A transparent object is one in which you can see the * inventory even if it is closed. * * @see reset_transparent() * @see query_transparent() */ void set_transparent() { _trans = C_TRANS; } /* set_transparent() */ /** * This method removes the current objects transparent * status. * A transparent object is one in which you can see the * inventory even if it is closed. This is also * used for light propogration. * * @see set_transparent() * @see query_transparent() */ void reset_transparent() { _trans = 0; } /* reset_transparent() */ /** * This method returns the current transparent * value of the object. * A transparent object is one in which you can see the * inventory even if it is closed. This is also * used for light propogration. * * @see reset_transparent() * @see query_transparent() * @return 1 if the object is transparent */ int query_transparent() { return _trans; } /* query_transparent() */ /** * This sets the function to be called when someone attempts to * open the object. The function does not have to be in the * object's file, and can be specified using the 'ob' paramater. * In most cases the function will be stored in the same file, * so use this_object() in these cases. * * The function will be called with a single string parameter of "open" or * "close" * * @param ob The object in which the function is stored. * @param func The function to be called. * @see query_open_trap_func() * @see query_open_trap_ob() */ void set_open_trap(object ob, string func) { _trap_open_func = func; _trap_open_ob = ob; } /* set_open_trap() */ /** * This returns the name of the function to be called when * someone tries to open the object. * * @return The function to be called when the object is opened. * @see set_open_trap() * @see query_open_trap_ob() */ string query_open_trap_func() { return _trap_open_func; } /* query_open_trap_func() */ /** * This returns the object on which the trap function is stored. * The trap function is called when someone tries to open the * the object. * * @return The function to be called when the object is opened. * @see set_open_trap() * @see query_open_trap_func() */ object query_open_trap_ob() { return _trap_open_ob; } /* query_open_trap_ob() */ /** * This sets the function to be called when someone attempts to * unlock the object. The function does not have to be in the * object's file, and can be specified using the 'ob' paramater. * In most cases the function will be stored in the same file, * so use this_object() in these cases. * * The function will be called with a single string parameter of "lock", * "unlock" or "pick" * * @param ob The object in which the function is stored. * @param func The function to be called. * @see query_lock_trap_func() * @see query_lock_trap_ob() */ void set_lock_trap(object ob, string func) { _trap_lock_func = func; _trap_lock_ob = ob; } /* set_unlock_trap() */ /** * This returns the name of the function to be called when * some attempts to unlock the object. * * @return The function to be called when someone attempts * to unlock the object. * @see set_open_trap() * @see query_open_trap_ob() */ string query_lock_trap_func() { return _trap_lock_func; } /* query_lock_trap_func() */ /** * This returns the object on which the trap function is stored. * The trap function is called when someone tries to unlock the * the object. * * @return The function to be called when the object is opened. * @see set_open_trap() * @see query_open_trap_func() */ object query_lock_trap_ob() { return _trap_lock_ob; } /* query_lock_trap_ob() */ /** * This method returns the closed and locked status. The status is * 0 if open and unlocked, 1 if locked, 2 if closed and 3 if closed * and locked. * @return closed and locked status */ int query_closed_locked_status() { if (_closed && _locked) { return CLOSED_LOCKED; } if (_closed) { return CLOSED; } if (_locked) { return LOCKED; } return 0; } /* query_closed_locked_status() */ /** * This method adds the lock commands to the specified player. * It's been abstracted out of init() to make it easier for some * odd situations (like the t-shop). */ void add_close_lock_commands(object player) { if(!player) return; player->add_command("open", TO, "<direct:object:me-here>", (: do_open() :) ); player->add_command("close", TO, "<direct:object:me-here>", (: do_close() :) ); if (TO->query_key()) { player->add_command("lock", TO, "<direct:object:me-here>", (: do_lock(0) :) ); player->add_command("lock", TO, "<direct:object:me-here> with <indirect:object:me>", (: do_lock($1) :) ); player->add_command("unlock", TO, "<direct:object:me-here>", (: do_unlock(0) :) ); player->add_command("unlock", TO, "<direct:object:me-here> with <indirect:object:me>", (: do_unlock($1) :) ); } } /* add_close_lock_commands() */ /** * @ignore yes */ void init() { add_close_lock_commands(this_player()); } /* init() */ /** * @ignore yes */ string short_status() { return ""; } /* short_status() */ /** * @ignore yes */ string long_status() { // If it is stuck in one state don't bother telling us the state. if (TO->query_stuck()) return ""; switch (query_closed_locked_status()) { case CLOSED_LOCKED: return "It is closed and locked.\n"; case CLOSED: return "It is closed.\n"; default: return "It is open.\n"; } } /* long_status() */ /** * @ignore yes */ int check_stealth() { mapping hide_invis; int hiding, sneaking, difficulty, light, my_light, items_blocking; hide_invis = (mapping) TP->query_hide_invis(); hiding = undefinedp(hide_invis["hiding"]) ? 0 : 1; sneaking = TP->query_sneak_level() ? 1 : 0; if (!hiding && !sneaking) return STEALTH_NONE; my_light = TP->query_light(); light = environment(TP)->query_light(); difficulty = light + (4 * my_light) / (light + 1); items_blocking = sizeof(CLOTHING_HANDLER->query_items_blocking(TO, TP)); if (items_blocking) { difficulty += 50 * items_blocking; } debug_printf( "Difficulty = %d.\n Skill = %s\n Bonus = %d\n", difficulty, SKILL, TP->query_skill_bonus( SKILL ) ); switch (TASKER->perform_task(TP, SKILL, difficulty, TM_FREE)) { case AWARD: write("%^YELLOW%^" + ({ "You discover something that lets your fingers move more nimbly.", "You find yourself capable of deceiving the eye with greater ease " "than before.", "You realise how to deceive the eye more effectively." })[ random(3) ] + "%^RESET%^\n" ); case SUCCEED : return STEALTH_SUCCEEDED; default : return STEALTH_FAILED; } } /* check_stealth() */ /** * @ignore yes */ int do_unlock(object *keys) { object *gkeys = ({ }); string key = TO->query_key(); int inv = 0; if (!_locked) { TP->add_failed_mess(TO, "$D $V$0=isn't,aren't$V$ locked.\n", ({ }) ); return 0; } if (_stuck) { TP->add_failed_mess(TO, "$D $V$0=is,are$V$ stuck.\n", ({ }) ); return 0; } if (!_closed) { TP->add_failed_mess(TO, "$D $V$0=isn't,aren't$V$ closed.\n", ({ }) ); return 0; } if (!key) { TP->add_failed_mess(TO, "$D $V$0=doesn't,don't$V$ have a lock.\n", ({ }) ); return 0; } if (key != "by hand") { if (!keys || !sizeof(keys)) { keys = all_inventory(TP); inv = 1; } gkeys = filter(keys, (: $1->query_property( $(key) ) :) ); if (!sizeof(gkeys)) { if (!inv) { TP->add_failed_mess(TO, "You cannot $V $D with $I.\n", keys); return 0; } TP->add_failed_mess(TO, "You cannot $V $D.\n", ({ }) ); return 0; } gkeys[0]->use_key(TO); } if (_trap_lock_func && _trap_lock_ob) { if (!call_other(_trap_lock_ob, _trap_lock_func, "unlock", TO, TP)) { return 0; } } set_unlocked(); switch (check_stealth()) { case STEALTH_SUCCEEDED: if (sizeof (gkeys)) { TP->add_succeeded_mess(TO, ({ "$N $V $D with $I, managing to stay unnoticed.\n", "", }), ({ ( gkeys[0] ) }) ); } else { TP->add_succeeded_mess(TO, ({ "$N $V $D, managing to stay unnoticed.\n", "", }), ({ }) ); } break; case STEALTH_FAILED: TP->add_succeeded_mess(TO, "$N unsuccessfully tr$y to " + query_verb() + " $D while " "staying unnoticed.\n", ({ }) ); break; default: if (sizeof (gkeys)) { TP->add_succeeded_mess(TO, "$N $V $D with $I.\n", ({ gkeys[0] }) ); } else { TP->add_succeeded_mess (TO, "$N $V $D.\n", ({ })); } break; } return 1; } /* do_unlock() */ /** * @ignore yes */ int do_lock(object *keys) { object *gkeys = ({ }); string key = TO->query_key(); int inv = 0; if (_locked) { TP->add_failed_mess(TO, "$D $V$0=is,are$V$ already locked.\n", ({ }) ); return 0; } if (_stuck) { TP->add_failed_mess(TO, "$D $V$0=is,are$V$ stuck.\n", ({ }) ); return 0; } if (!_closed && !do_close()) { TP->add_failed_mess(TO, "$D $V$0=isn't,aren't$V$ closed.\n", ({ }) ); return 0; } if (!key) { TP->add_failed_mess(TO, "$D $V$0=doesn't,don't$V$ have a lock.\n", ({ }) ); return 0; } if (key != "by hand") { if (!keys || !sizeof(keys)) { keys = all_inventory(TP); inv = 1; } gkeys = filter(keys, (: $1->query_property( $(key) ) :) ); if (!sizeof(gkeys)) { if (!inv) { TP->add_failed_mess(TO, "You cannot lock $D with $I.\n", keys); return 0; } TP->add_failed_mess(TO, "You cannot lock $D.\n", ({ }) ); return 0; } gkeys[0]->use_key(TO); } if (_trap_lock_func && _trap_lock_ob) { if (!call_other(_trap_lock_ob, _trap_lock_func, "lock", TO, TP)) { return 0; } } set_locked(); switch (check_stealth()) { case STEALTH_SUCCEEDED: if (sizeof (gkeys)) { TP->add_succeeded_mess(TO, ({ "$N lock$s $D with $I, managing to stay unnoticed.\n", "", }), ({ ( gkeys[0] ) }) ); } else { TP->add_succeeded_mess(TO, ({ "$N lock$s $D, managing to stay unnoticed.\n", "", }), ({ }) ); } break; case STEALTH_FAILED: TP->add_succeeded_mess(TO, "$N unsuccessfully tr$y to " + "lock $D while staying unnoticed.\n", ({ }) ); break; default: if (sizeof (gkeys)) { TP->add_succeeded_mess(TO, "$N lock$s $D with $I.\n", ({ gkeys[0] }) ); } else { TP->add_succeeded_mess (TO, "$N lock$s $D.\n", ({ })); } break; } return 1; } /* do_lock() */ /** * @ignore yes */ int pick_lock(object player) { if (_trap_lock_ob && !call_other(_trap_lock_ob, _trap_lock_func, "pick", TO, player)) { return 0; } set_locked(); return 1; } /* pick_lock() */ /** * This is called when someone successfully unlocks the object. * If there is a trap, and the trap function returns true, the * object will not be opened. If the trap function returns * false, the object will still be opened even if the trap is * sprung. * * @param player The player who is unlocking the object. * * @return Returns 1 if the object was unlocked, and 0 otherwise. */ int pick_unlock(object player) { if (_trap_lock_ob && !call_other(_trap_lock_ob, _trap_lock_func, "pick", TO, player)) { return 0; } set_unlocked(); return 1; } /* pick_unlock() */ /** * @ignore yes */ int do_open() { if (!_closed) { TP->add_failed_mess(TO, "$D $V$0=is,are$V$ already open.\n", ({ }) ); return 0; } if (_stuck) { TP->add_failed_mess(TO, "$D $V$0=is,are$V$ stuck.\n", ({ }) ); return 0; } if (_locked && !do_unlock(0)) { TP->add_failed_mess(TO, "$D $V$0=is,are$V$ locked.\n", ({ }) ); return 0; } if (_trap_open_func && _trap_open_ob) { if (!call_other(_trap_open_ob, _trap_open_func, "open", TO, TP)) { return 0; } } set_open(); if (!TO->query_door_name()) { switch (check_stealth()) { case STEALTH_SUCCEEDED: TP->add_succeeded_mess(TO, ({ "$N $V $D, managing to stay unnoticed.\n", "", }), ({ }) ); break; case STEALTH_FAILED: TP->add_succeeded_mess(TO, "$N unsuccessfully tr$y to " + query_verb() + " $D while " "staying unnoticed.\n", ({ }) ); break; default: TP->add_succeeded_mess(TO, "$N $V $D.\n", ({ }) ); break; } } return 1; } /* do_open() */ /** * @ignore yes */ int do_close() { if (_closed) { TP->add_failed_mess(TO, "$D $V$0=is,are$V$ already closed.\n", ({ }) ); return 0; } if (_stuck) { TP->add_failed_mess(TO, "$D $V$0=is,are$V$ stuck.\n", ({ }) ); return 0; } if (_trap_open_func && _trap_open_ob) { if (!call_other(_trap_open_ob, _trap_open_func, "close", TO,TP)) { return 0; } } set_closed(); if (_autolock) set_locked(); if (!TO->query_door_name()) { switch (check_stealth()) { case STEALTH_SUCCEEDED: TP->add_succeeded_mess(TO, ({ "$N $V $D, managing to stay unnoticed.\n", "", }), ({ }) ); break; case STEALTH_FAILED: TP->add_succeeded_mess(TO, "$N unsuccessfully tr$y to " + query_verb() + " $D while " "staying unnoticed.\n", ({ }) ); break; default: TP->add_succeeded_mess(TO, "$N $V $D.\n", ({ }) ); break; } } return 1; } /* do_close() */ /** * @ignore yes */ string *parse_command_adjectiv_id_list() { switch( query_closed_locked_status() ) { case CLOSED_LOCKED: return ({ "closed", "locked" }); case CLOSED: return ({ "closed", "unlocked" }); case LOCKED: return ({ "open", "locked" }); default: return ({ "open", "unlocked" }); } } /* parse_command_adjectiv_id_list() */ /** * @ignore yes */ mixed *stats() { return ({ ({"closed", query_closed()}), ({"transparent", query_transparent()}), ({"key", query_key()}), ({"difficulty", query_difficulty()}), ({"locked", query_locked()}), ({"stuck", query_stuck()}), ({"open trapped", _trap_open_func != 0}), ({"lock trapped", _trap_lock_func != 0}), ({"pick skill", query_pick_skill()}),}); } /* stats() */ /* EOF */