/* Do not remove the headers from this file! see /USAGE for more info. */
/*
* Design:
*
* BLOCKEXITS, OPENABLE, and LOCKABLE handle keeping our state for us
* and keeping people from moving through the exit. We do, however,
* overload set_locked() and set_closed() so that we can inform our
* other end. We find our other end by finding the room we are in,
* finding the destination in that direction, and searching that room
* for a door that has the same 'identifier' as us. We also cache
* the object so we don't have to look that often under normal circumstances.
* We also establish a move hook, so that when we are initially moved
* to our room, we can check if our sibling already exists, and if so,
* we copy it's state. This way, as long as both objects are loaded,
* they remain consistent, and if neither object is loaded, the first
* one to load keeps it's default state, and the second one to load
* copies the first.
*
* Note: in order for this to work, you have to call setup_door AFTER
* set_locked and set_closed, otherwise it will happily update the sibling,
* overriding the values we got from it.
*/
#include <lpscript.h>
inherit COMPLEX_EXIT_OBJ;
inherit M_LOCKABLE;
inherit M_OPENABLE;
inherit M_KNOCKABLE;
inherit M_SIBLING;
/* Check function used by M_COMPLEX_EXIT */
mixed door_check(string dir, object who) {
if (query_closed()) {
this_body()->other_action( "$N $vtry to go $o, but the $o1 is closed.",
dir,
this_object());
return sprintf("You try to go %s, but the %s is closed.\n",
query_obvious_description(),
the_short());
}
if(!load_object(query_method_destination("go")))
return "Error, the destination does not exist, please mail the creator of this room";
return 1;
}
//:FUNCTION update_state
// Internal use function.
// Update the state of both this door and its sibling.
void update_state(object ob) {
m_openable::set_closed(ob->query_closed());
::set_locked(ob->query_locked(), ob->query_key_type());
}
//:FUNCTION update_sibling
// After a door is opened or closed, this is called, and in turn calls
// do_on_open/close() in the door and its sibling.
// See also m_sibling::update_sibling()
void update_sibling()
{
object sibling = get_sibling();
// Needed for initial load ...\
if( !sibling )
return;
// Magic ...
if( is_open())
sibling->do_on_open();
else sibling->do_on_close();
::update_sibling();
}
//:FUNCTION do_on_close
// Called when the door is closed.
void do_on_close() {
if (!query_closed())
object_event("The $o closes.\n");
#ifndef CLOSED_DOORS_ARE_OBVIOUS_EXITS
set_hidden(1);
#endif
}
//FUNCTION: do_on_open
// Called when the door is opened.
void do_on_open() {
if (query_closed())
object_event("The $o opens.\n");
#ifndef CLOSED_DOORS_ARE_OBVIOUS_EXITS
set_hidden(0);
#endif
}
varargs void on_clone(string dir, mixed rest...)
{
object sib;
if(!dir)
dir=query_direction();
if(!sizeof(rest))
rest+=({query_method_destination("go")});
complex_exit_obj::on_clone(dir,rest...);
sib=get_sibling();
if( sib ) update_state( sib );
}
//:FUNCTION set_door_direction
// Set the direction name the door uses. This is also the name of the
// resulting exit.
void set_door_direction(string direction)
{
/* Start by removing the old stuff, if it exists */
string olddir=query_obvious_description();
if(olddir)
{
TBUG(olddir);
remove_adj(olddir);
remove_id(olddir);
}
set_obvious_description(direction);
add_adj(direction);
add_id_no_plural(direction);
}
//:FUNCTION set_door_destination
// Set the destination of the exit that the door covers. The destination can be
// anything M_COMPLEX_EXIT::set_method()'s destination argument will accept
void set_door_destination(mixed dest)
{
/* This is to make the pathname relative to the room that the door is in
* rather than the door itself */
if(stringp(dest)) {
dest=(:absolute_path($(dest),environment()):);
}
set_method("go",dest,(:door_check:),
({sprintf("$N $vleave through the $o.")}),
({sprintf("$N $venter through the $o.")}));
set_sibling_room(dest);
}
//:FUNCTION query_door_destination
// Return the destination of the door
mixed query_door_destination()
{
return query_method_destination("go");
}
//:FUNCTION setup_exits
// Set the name of the direction and the location to which the door's exit
// will lead.
// DEPRICATED! -- Use set_door_destination() and set_door_description()
// instead.
void setup_exits(string dir,string room) {
add_method("go", room,(:door_check:));
set_door_direction(dir);
}
//:FUNCTION setup_door
// Set up the key parts of the door.
// The first argument is the string to which the door responds and should
// match its sibling, the second argument is the direction of the exit the
// door covers, and the third argument is the object to which the exit leads.
// This should be called only once, from setup().
// DEPRECATED! -- Use set_door_destination, set_door_direction(), and
// set_sibling_ident() instead.
void setup_door(string ident, string dir, string room) {
set_sibling_ident(ident);
set_door_direction(dir);
set_door_destination(room);
}
varargs void set_locked(string x, string y) {
m_lockable::set_locked(x, y);
update_sibling();
}
void set_closed(int x) {
::set_closed(x);
update_sibling();
}
void do_knock()
{
object sibling;
if(is_open())
{
write("There is no need, the door is already open.\n");
return;
}
sibling = get_sibling();
if(sibling)
{
tell_environment( sibling, "There is a knock at the door.\n" );
}
::do_knock();
}
mixed direct_get_obj( object obj )
{
return "#Opening it would be easier.\n";
}
mapping lpscript_attributes()
{
return ([ "ident" : ({ LPSCRIPT_STRING, "setup", "set_sibling_ident" }),
"direction" : ({ LPSCRIPT_STRING, "setup", "set_door_direction" }),
"destination" : ({ LPSCRIPT_STRING, "setup", "set_door_destination" }) ]);
}