/** * This is an inheritable for player-owned housing.<p> * It handles the internals for player housing. When creating a house you * should inherit "/std/room/player_housing" rather than this inheritable. * * @author Belle, Gototh, Ceres */ #include <dirs.h> #include <housing.h> #include <login_handler.h> #include <player_handler.h> inherit "/std/room/inherit/placement"; inherit "/std/room/inherit/room_save"; #define STANDARD_EXITS ({ "north", "northeast", "east", "southeast", \ "south", "southwest", "west", "northwest"}) #undef PLAYTESTING #define DEBUG private nosave int _setup_callout; // // The predefines for the functions. // varargs string* query_direc(object thing); int query_relative(string direc); varargs mixed query_door_control(string direc); varargs mixed* query_dest_other(string direc); int modify_exit(string dir, mixed* arr); varargs int add_item(mixed shorts, mixed desc, int no_plural); string the_short(); string* query_exits(); int query_exit(string direc); void setup_doors(); void update_doors(); private void update_short_cache(string direc); int test_occupier(string name); string show_surface(string direc); int allow_list(); int do_deny(string player, string location); int do_allow(object player, string location); int add_allowed(string pname); int remove_allowed(string pname); string query_owner(); /** * This class is used to store information on the decoration of this room (for * walls, floor etc.) */ class decoration { string short; string covering; } /** * This class stores information that we need to save about exits. For example * if the player has changed the lock or added a trap. */ class exit_data { string long; string key; int difficulty; int closed; int locked; int autolock; int transparent; mixed trap_data; } /** * This class is used to cache short names for exits so we don't go loading * rooms all over the place. */ class exit_shorts { string one_short; string a_short; } private class decoration *decorations; private string base_long; private mapping exits; private mapping exit_shorts; private nosave mapping coverings; // here coz of sort_array private nosave string owner; private string *allowed = ({ }); private string *tell_denied = ({ }); private nosave string decoration_desc = ""; private nosave string quit_location; private nosave int done_setup; private nosave int setup_counter; // used to track which surfaces this room actually has. decorations would // include any it used to have too. private nosave string *surfaces; #ifdef DEBUG void debug_log(string fmt, mixed args ...) { debug_printf(fmt + "\n", args ...); if(base_name(this_object()) == "/d/cwc/Bes_Pelargic/Pong_Ping/roads/phoenix_avenue/housing/05entrycorridor") { log_file("HOUSING_DEBUG", "%s (%s): " + fmt + "\n", ctime(time())[4..18], base_name(this_object()), args ...); } } #endif /** @ignore yes */ void create() { room_save::create(); exits = ([ ]); exit_shorts = ([ ]); decorations = ({ }); tell_denied = ({ }); this_object()->add_item("left", (: show_surface("left") :)); this_object()->add_item("right", (: show_surface("right") :)); this_object()->add_item("forward", (: show_surface("forward") :)); this_object()->add_item("backward", (: show_surface("backward") :)); this_object()->add_item("up", (: show_surface("up") :)); this_object()->add_item("down", (: show_surface("down") :)); /* * We try setup_doors() twice. The first time, if it succeeds is * good because it doesn't leave an opportunity for someone to get into * a room before the door locks. However sometimes this is too early * and no exits have been setup yet so the second one catches that case. */ done_setup = 0; setup_doors(); call_out("setup_doors", 0); // call_out("setup_doors", 1); } /* create() */ /** @ignore yes * Called by the add_item to describe a wall, floor or ceiling. */ string show_surface(string direc) { class decoration tmp; int found; string lstr, dstr; mixed tmp2; switch(direc) { case "left": case "right": case "forward": case "backward": // Find relative directions. tmp2 = this_player()->query_facing(); direc = (string)this_player()->find_abs(direc, tmp2[0]); break; case "up": direc = "ceiling"; break; case "down": direc = "floor"; break; } if(!decorations) { decorations = ({ }); } else { // Check it doesn't already exist. foreach(tmp in decorations) { // remove any old surfaces that got saved somehow. if(member_array(tmp->short, surfaces) == -1) decorations -= ({ tmp }); if(tmp->short == direc || tmp->short == direc + " wall") { found = 1; break; } } } if(found) { direc = replace(direc, ({ " wall", "" })); if(member_array(direc, query_direc()) != -1) { lstr = "$C$"; update_short_cache(direc); if(exit_shorts[direc] && exit_shorts[direc]->one_short) { if(query_door_control(direc)) lstr += query_door_control(direc)->one_short() + " leads"; else lstr = "an exit leads"; lstr += " to " + exit_shorts[direc]->one_short; } else { if(query_door_control(direc)) lstr += "is the " + query_door_control(direc)->one_short(); else lstr += "is an exit"; } lstr += " and the "; } else lstr = "The "; dstr = desc_surface(direc); if(dstr != "") dstr = " " + dstr; return lstr + tmp->short + " is " + tmp->covering + "." + dstr; } return ""; } /* show_surface() */ /** @ignore yes * Called by the add_item to describe a wall, floor or ceiling. */ string show_corner(string direc) { string tmp; tmp = desc_surface(direc); if(tmp == "") { tmp = "The " + direc + " corner is empty."; } return tmp; } /* show_corner() */ /** @ignore yes * This function handles producing the long text for the exits. */ string exit_long() { string *direcs, lstr; int i, j; object door; direcs = query_direc(); if(!sizeof(direcs)) return ""; lstr = " $C$"; for(i=0; i<sizeof(direcs); i++) { update_short_cache(direcs[i]); if(query_relative(direcs[i])) { j = member_array(this_player()->find_rel(direcs[i]), REL_DIRS); lstr += REL_DIRS[j+2]; if(exit_shorts[direcs[i]]) { if(query_door_control(direcs[i])) lstr += " " + query_door_control(direcs[i])->a_short() + " leads " "to " + exit_shorts[direcs[i]]->a_short; else lstr += " an exit leads to " + exit_shorts[direcs[i]]->a_short; } else { if(query_door_control(direcs[i])) lstr += " is " + query_door_control(direcs[i])->a_short(); else lstr += " is an exit"; } } else { door = query_door_control(direcs[i]); if(door) { if(member_array("window", door->query_name()) != -1) lstr += door->a_short() + " looks out"; else lstr += door->a_short() + " leads " + direcs[i]; } else lstr += "an exit leads " + direcs[i]; } if(i < sizeof(direcs)-2) lstr += ", "; if(i == sizeof(direcs)-2) lstr += " and "; if(i == sizeof(direcs)-1) lstr += "."; } return lstr; } /* exit_long() */ /** * @ignore yes * This function handles producing the long text for the decorations. */ string decoration_long() { class decoration tmp; string lstr, wstr, tmpstr, *tmparray, *names; int i; #ifdef 0 // Can't use this coz tables etc. have variable shorts and we can't tell // when they change. if(decoration_desc != "") return decoration_desc; #endif lstr = ""; if(sizeof(decorations)) { coverings = ([ ]); names = ({ "walls" }); foreach ( tmp in decorations ) { if(!coverings[tmp->covering]) coverings[tmp->covering] = ({ tmp->short }); else coverings[tmp->covering] += ({ tmp->short }); if(classp(tmp) && strsrch(tmp->short, "wall") == -1) names += ({ tmp->short }); } i = 0; tmparray = sort_array(keys(coverings), (: sizeof(coverings[$1]) - sizeof(coverings[$2]) :)); // Too many surfaces so lets just describe them as multicoloured. if(sizeof(tmparray) > 3) { lstr += " The " + query_multiple_short(names) + " are multicoloured. "; } else if(sizeof(tmparray) == 1) { lstr += " The surfaces are all " + decorations[0]->covering + ". "; } else { foreach(tmpstr in tmparray) { if(i == 0) wstr = " The "; else wstr = "the "; if(i == sizeof(tmparray)-1 && sizeof(coverings[tmpstr]) > 1) wstr += "other surfaces"; else wstr += query_multiple_short(coverings[tmpstr], "", 1); if(sizeof(coverings[tmpstr]) > 1) wstr += " are "; else wstr += " is "; wstr += tmpstr; if(i < sizeof(tmparray) -2) wstr += ", "; else if(i == sizeof(tmparray) -2) wstr += " and "; else if(i == sizeof(tmparray) -1) wstr += ". "; i++; lstr += wstr; } } } decoration_desc = lstr; return decoration_desc; } /** @ignore yes * Internal function to update the dynamic long description of the room. */ string query_long() { string lstr, tmp; if(base_long) lstr = "This is " + base_long + "."; else lstr = ""; tmp = decoration_long(); if(tmp != "") lstr += tmp; else lstr += " "; return (lstr + furniture_long() + exit_long() + "\n"); } /** @ignore yes * Just makes sure room exits are relative inside houses and that doors * are closed. */ int setup_exit(string direc, int relative) { if(member_array(direc, STANDARD_EXITS) != -1 && relative) modify_exit(direc, ({ "relative", 1 })); modify_exit(direc, ({ "closed", 1 })); return 1; } /** * Set the fixed part of the long description. * @param desc The description. (eg. a small and cosy front room) */ void set_base_desc(string desc) { base_long = desc; } /** * Query the fixed part of the description. * @return the description * * @see set_base_desc */ string query_base_desc() { return base_long; } /** * Set the room into which people should be placed if they quit in this room * but are not allowed to control this room. * * @param filename the filename of a room. */ void set_quit_handler(string filename) { quit_location = filename; } /** * This method returns the room that people will be placed in if they logout * while in this room and not allowed to control this room. * @see set_quit_handler * @return string the filename of the room. */ string query_quit_handler() { if(this_player() && !test_occupier(this_player()->query_name()) && quit_location) return quit_location; return base_name(this_object()); } /** * Add a surface (wall, floor etc.) and give the basic covering the surface * has prior to decoration. * * @param short The short name of the surface (eg. north wall) * @param covering The text of the covering (eg. finished pine board) */ void add_surface(string short, string covering ){ class decoration tmp; string dir; if(!surfaces) surfaces = ({ }); surfaces += ({ short }); if(!decorations) decorations = ({ }); else { // Check it doesn't already exist. foreach(tmp in decorations) if(tmp->short == short) { add_item(tmp->short, (: show_surface($(tmp->short)) :)); short = replace(short, ({" wall", ""})); foreach(dir in keys(ABS_FACING)) { if(ABS_FACING[short] && ABS_FACING[dir] == (ABS_FACING[short] % 8 ) + 1) { add_item(dir+" corner", (: show_corner($(dir)) :)); break; } } return; } } tmp = new(class decoration ); tmp->short = short; tmp->covering = covering; decorations += ({ tmp }); add_item(tmp->short, (: show_surface($(tmp->short)) :)); short = replace(short, ({" wall", ""})); foreach(dir in keys(ABS_FACING)) if(ABS_FACING[short] && ABS_FACING[dir] == (ABS_FACING[short] + 1) % 8) { add_item(dir+" corner", (: show_corner($(dir)) :)); break; } decoration_desc = ""; } /** @ignore yes * return a list of surfaces */ string *query_surfaces() { string *stmp; class decoration tmp; stmp = ({ }); foreach(tmp in decorations) { stmp += ({ replace(tmp->short, ({" wall", ""})) }); } return stmp; } /** * Modify a surface (wall, floor etc.) and give it a new covering. * * @param short The short of the surface. * @param covering The new covering decsription. * @return 1 for success, 0 for failure * * @see add_surface */ int decorate_surface(string short, string covering ){ class decoration tmp; foreach(tmp in decorations) if(tmp->short == short) { tmp->covering = covering; event(this_object(), "save"); decoration_desc = ""; return 1; } return 0; } /** @ignore yes */ void query_decorations() { printf("%O\n", decorations); } /** @ignore yes * Determine which items in this room should be saved. */ int test_save(object thing) { return thing->query_furniture() || thing->query_missing_item_receipt(); } /** @ignore yes * Call the test_remove functions in both placement & room_save */ int test_remove(object thing, int flag, mixed dest) { if(!placement::test_remove(thing, flag, dest)) return 0; return room_save::test_remove(thing, flag, dest); } /** @ignore yes * Stops positioned objects from showing. If it has a CURRENT_ROOM_VERB * property it's obviously a placed object and shouldn't been seen. */ object *my_process_non_living(object *int_non_livings, int start) { object *not_positioned; not_positioned = filter(int_non_livings, (: !$1->query_property("current room position") && !$1->query_property("current room hidden") :) ); return not_positioned; } /* process_non_living() */ /** @ignore yes */ void init() { placement::init(); // If this player is an occupier (and really an occupier not just an // authorised player) then give them the ability to allow/deny people // control in this room if(!allowed) { allowed = ({ }); } add_command("allow", "list", (: allow_list() :)); if(test_occupier(this_player()->query_name()) || this_player()->query_creator()) { if(!HOUSING->query_house(base_name(this_object()))) { add_command("allow", "<indirect:player:here>", (: do_allow($1[0], "room") :)); add_command("deny", "<word'player'>", (: do_deny($4[0], "room") :)); } else { add_command("allow", "<indirect:player:here> <word'house|room'>", (: do_allow($1[0], $4[1]) :)); add_command("deny", "<word'player'> <word'house|room'>", (: do_deny($4[0], $4[1]) :)); } } } /** * This method is used to setup things when someone enters the room. * @param ob the object that entered the room */ protected void event_enter_checks(object ob) { if(!tell_denied) { tell_denied = ({ }); } if(ob && interactive(ob) && member_array(ob->query_name(), tell_denied) != -1) { write("%^YELLOW%^%^BOLD%^You are no longer allowed to control this " "room.%^RESET%^\n"); tell_denied -= ({ ob->query_name() }); } } /** @ignore yes */ void event_enter(object ob, string message, object from) { #ifdef PLAYTESTING if(interactive(ob) && (!ob->query_creator() && !"/obj/handlers/playtesters"->query_playtester(ob->query_name())) && ob->query_name() != "gumboot") { tell_object(ob, "Sorry, this area is only open to playtesters.\n"); ob->move(from); } #endif event_enter_checks(ob); } /* event_enter() */ /** @ignore yes * List who can control this room. */ int allow_list() { string *tmp; if(owner && owner != "For Sale" && owner != "Under Offer") { tmp = ({ owner }) + allowed; } else { tmp = allowed; } if (!test_occupier(this_player()->query_name()) && !this_player()->query_creator()) { add_failed_mess("You are not allowed to control this room.\n", ({ })); return 0; } switch(sizeof(tmp)) { case 0: add_succeeded_mess(({"Noone is allowed to control this room.\n", "$N messes with something in " + the_short() + ".\n"})); break; case 1: add_succeeded_mess(({query_multiple_short(tmp) + " is allowed to control this room.\n", "$N messes with something in " + the_short() + ".\n"})); break; default: add_succeeded_mess(({query_multiple_short(tmp) + " are allowed to control this room.\n", "$N messes with something in " + the_short() + ".\n"})); } return 1; } /** @ignore yes * * Allows a player to do things in the room as though they were the * owner/occupier. * This function is called by the 'allow' add_command. */ /* NOTE: This uses object player and requires the owner and the player to be * logged in and present to be allowed. This is a deliberate security * feature to prevent people allowing their alts and so using houses to * multiplay! */ int do_allow(object player, string location) { string pname; string *rooms; string room; int failed; if((query_owner() != this_player()->query_name()) && !this_player()->query_creator()) { add_failed_mess("You are not allowed to control this room.\n", ({ })); return 0; } if(environment(player) != this_object()) { return add_failed_mess("$I is not here.\n", ({ player })); } pname = lower_case(player->query_name()); if(test_occupier(pname)) { add_failed_mess("$I is already able to control this room.\n"); return 0; } if(location == "house") { rooms = HOUSING->query_rooms(base_name(this_object())); failed = 1; foreach(room in rooms) { failed &= room->add_allowed(pname); } } else { failed = add_allowed(pname); } if (failed) { add_failed_mess("Failed to add " + pname + " as being able to control " "the " + location + ".\n"); return 0; } tell_object(player, "You are now permitted to control this " + location + ".\n"); add_succeeded_mess(({ pname + " is allowed to control this " + location + ".\n", ""})); return 1; } /* do_allow() */ /** @ignore yes * * Takes a player off the allowed list. * This function is called by the 'deny' add_command. */ int do_deny(string player, string location) { object ob; string *rooms, room; if(member_array(player, allowed) == -1) { add_failed_mess(player + " is not on the allow list.\n"); return 0; } if (!test_occupier(this_player()->query_name()) && !this_player()->query_creator()) { add_failed_mess("You are not allowed to control this room.\n"); return 0; } if(!test_occupier(player)) { add_failed_mess(player + " is already not allowed to control this room.\n"); return 0; } if(location == "house") { rooms = HOUSING->query_rooms(base_name(this_object())); foreach(room in rooms) room->remove_allowed(player); } else { remove_allowed(player); } ob = find_player(player); if(ob && environment(ob) == this_object()) { tell_object(ob, "%^YELLOW%^%^BOLD%^You are no longer allowed to control " + the_short() + ".\n%^RESET%^"); } else { tell_denied += ({ player }); } add_succeeded_mess(({player + " is no longer allowed to control this " + location + ".\n", "" })); return 1; } /* do_deny() */ /** @ignore yes * * Update our cache of short exit names. */ private void update_short_cache(string direc) { string ostr, astr; if(!exit_shorts) exit_shorts = ([ ]); if(find_object(query_dest_other(direc)[0])) { ostr = query_dest_other(direc)[0]->one_short(); if(this_player()) ostr = this_player()->convert_message(ostr); astr = query_dest_other(direc)[0]->a_short(); if(this_player()) astr = this_player()->convert_message(astr); if(!exit_shorts[direc] || ostr != exit_shorts[direc]->one_short || astr != exit_shorts[direc]->a_short) { exit_shorts[direc] = new(class exit_shorts, one_short : ostr, a_short : astr); } } } /** @ignore yes * Update the door info when the room is dested, prior to saving. */ void update_doors() { string exit; object door_ob; if(!done_setup) setup_doors(); if(!exits) exits = ([ ]); foreach(exit in query_exits()) { door_ob = query_door_control(exit); if(door_ob) { if(door_ob->query_key() == "generic_key") { #ifdef DEBUG if(exits[exit]) debug_log("update_doors", "Door %s has generic_key, exit data " "has %s", exit, exits[exit]->key); else debug_log("update_doors", "Door %s has generic_key, no existing " "exit data.", exit); #endif } exits[exit] = new(class exit_data, long : door_ob->query_long(), key : door_ob->query_key(), difficulty: door_ob->query_difficulty(), closed : door_ob->query_closed(), locked : door_ob->query_locked(), autolock : door_ob->query_autolock(), transparent : door_ob->query_transparent(), trap_data : door_ob->query_trap_data()); #ifdef DEBUG2 debug_log("update_doors: Door %s: key: %s, difficulty: %d, closed: %d, " "locked: %d, owner: %s", exit, exits[exit]->key, exits[exit]->difficulty, exits[exit]->closed, exits[exit]->locked, owner); #endif } } } /** @ignore yes * Initialise the doors when the room is loaded. */ void setup_doors() { string exit; object door; string other, other_id; #ifdef DEBUG2 debug_log("Setup doors called."); #endif if(!this_object()->query_exits()) { this_object()->calc_exits(); } if(!exits || !sizeof(exits)) { if(!this_object()->query_exits()) { #ifdef DEBUG2 debug_log("Leaving setup_doors, No exits to setup."); #endif } else if(query_inventory_loaded()) { #ifdef DEBUG debug_log("Leaving setup_doors, exits not setup %O, autoloading: %d " "inventory loaded: %d.", exits, query_auto_loading(), query_inventory_loaded()); #endif } return; } foreach(exit in keys(exits)) { door = query_door_control(exit); if(!door) { if(!query_exit(exit)) { map_delete(exits, exit); } continue; } if(!owner) catch(owner = HOUSING->query_owner(base_name(this_object()))); //if(!done_setup) { #ifdef DEBUG2 if(exit == "out") debug_log("Setting up %s: key: %s, difficulty: %d, closed: %d, " "locked: %d, owner: %s", exit, exits[exit]->key, exits[exit]->difficulty, exits[exit]->closed, exits[exit]->locked, owner); #endif modify_exit(exit, ({ "key", exits[exit]->key, "difficulty", exits[exit]->difficulty, "closed", exits[exit]->closed, "locked", exits[exit]->locked, "autolock", exits[exit]->autolock, "lock owner", owner, })); if(exits[exit]->trap_data && arrayp(exits[exit]->trap_data)) { door->setup_trap(exits[exit]->trap_data[0], exits[exit]->trap_data[1], exits[exit]->trap_data[2], exits[exit]->trap_data[3], exits[exit]->trap_data[4]); if(exits[exit]->trap_data[5]) door->set_trap_armed(1); } //} // This prevents rooms causing other rooms to load. Hopefully it's ok to // have it commented out. other = door->query_dest(); other_id = door->query_other_id(); // This prevents the room causing other playerhousing rooms to be loaded. // The check for "out" just makes certain we do the front door. #ifdef NO_SLOW_LOAD // I've disabled this for the moment to see if having the cascade of // room loads will be ok with the ramdisk and whether it'll fix any // door issues. if(!find_object(other) && exit != "out") continue; #endif if(!other_id) { door->force_other(); other_id = door->query_other_id(); } // If we have no other_id we need to try again later. if(!other_id) { #ifdef DEBUG2 debug_log("No other_id, adding call_out."); #endif if(!_setup_callout) { _setup_callout = call_out("setup_doors", 0); return; } } other->modify_exit(other_id, ({ "key", exits[exit]->key, "difficulty", exits[exit]->difficulty, "closed", exits[exit]->closed, "locked", exits[exit]->locked, "autolock", exits[exit]->autolock, "lock owner", owner, })); if(exits[exit]->trap_data && arrayp(exits[exit]->trap_data)) { door = other->query_door_control(other_id); if(door && objectp(door)) { door->setup_trap(exits[exit]->trap_data[0], exits[exit]->trap_data[1], exits[exit]->trap_data[2], exits[exit]->trap_data[3], exits[exit]->trap_data[4]); if(exits[exit]->trap_data[5]) door->set_trap_armed(); } } } done_setup = 1; } /** * This method returns the current owner of the house. */ string query_owner() { if(owner) return owner; return HOUSING->query_owner(base_name(this_object())); } /** * This method locally sets the owner of the house. */ string set_owner(string who) { owner = who; } /** * This function adds a name to the allow list of this room. * * @param pname The name of a player. * @return 1 for success, 0 for failure */ int add_allowed(string pname) { if(test_occupier(pname)) return 0; allowed += ({ pname }); event(this_object(), "save"); return 0; } /** * This function removes a name from the allow list of this room. * * @param pname The name of a player. * @return 1 for success, 0 for failure */ int remove_allowed(string pname) { if(!test_occupier(pname)) return 0; allowed -= ({ pname }); event(this_object(), "save"); return 1; } /** * This function determines if a given individual can be considered to be the * occupier of this house. Normally this means the person currently renting or * owning this house. * * For shared ownership houses simply mask this function and do whatever you * have to. :) * * @param name The person to be tested * @return 1 if they are the occupier, 0 if not. */ int test_occupier(string name) { if(!owner) catch(owner = HOUSING->query_owner(base_name(this_object()))); // If there is no owner then anyone in the house is considered the occupier. if(!owner || lower_case(owner) == "for sale" || lower_case(owner) == "under offer") return 1; if(PLAYER_HANDLER->test_creator(name)) return 1; if(!allowed) allowed = ({ }); return (owner == name || (member_array(name, allowed) != -1)); } /** * This function informs the house that its owner has changed, it does not * actually change the owner since that is done in the handler. * * @param old_owner The name of the old owner * @param new_owner The name of the new owner * @return 1 for success, 0 for failure. */ int ownership_change(string old_owner, string new_owner) { if(old_owner != new_owner) { owner = new_owner; allowed = ({ }); return 1; } return 0; } /** * This method returns the current list of people allowed to modify this * room. * @return the allowed people to change this room */ string *query_allowed() { if (allowed) { return allowed; } return ({ }); } /* query_allowed() */ /** * This method tells us if the person is allowed to use this room. * @param name the name of the person */ int is_allowed(string name) { // The owner, those on the allow list and creators are allowed. if((query_owner() && (lower_case(name) == lower_case(query_owner()))) || (member_array(lower_case(name), query_allowed()) != -1) || PLAYER_HANDLER->test_creator(name)) { return 1; } return 0; } /** @ignore yes */ void dest_me() { #ifdef NOT_USED if (base_name(this_object()) + ".c" != __FILE__) update_doors(); #endif ::dest_me(); } /** @ignore yes */ void event_open(object door, object opener) { #ifdef DEBUG2 debug_log("door opened by %s [%d]", opener->query_name(), done_setup); #endif if(done_setup) { ::event_open(door, opener); update_doors(); } } /** @ignore yes */ void event_close(object door, object closer) { #ifdef DEBUG2 debug_log("door closed by %s [%d]", closer->query_name(), done_setup); #endif if(done_setup) { ::event_close(door, closer); update_doors(); } } /** @ignore yes */ void event_unlock(object door, object unlocker) { #ifdef DEBUG2 debug_log("door unlocked by %s [%d]", unlocker?unlocker->query_name():"noone", done_setup); #endif if(done_setup) { ::event_unlock(door, unlocker); update_doors(); } } /** @ignore yes */ void event_lock(object door, object locker) { #ifdef DEBUG2 debug_log("door locked by %s [%d]", locker?locker->query_name():"noone", done_setup); #endif if(done_setup) { ::event_lock(door, locker); update_doors(); } }