/**
* This file contains all the bits needed to handle rooms, plus the code to
* handle following when moving.
* @author Pinkfish
* @changed Deutha Who knows
* Severely changed, move the follow code in here from the living object
* @changed Sandoz - 09/09/02
* Added recycling for chatters, doors and items.
* @changed Zrutu - 13/10/04
* Added support for functions in exit / enter mess
* @see /std/room/basic_room.c
* @see recycle_chatter()
* @see recycle_door()
* @see recycle_item()
* @see get_chatter()
* @see get_door()
* @see get_item()
*/
#include <climate.h>
#include <player.h>
#include <room.h>
#define CHATSIZE 80
#define DOORSIZE 60
private mapping exit_types, door_types, opposite;
private mixed chatters, doors;
private nosave object *recycled_chatters, *recycled_doors, *recycled_items;
private nosave object *queued_doors, *queued_chatters;
private nosave int chatter_hits, door_hits, item_hits;
private nosave int chatter_misses, door_misses, item_misses;
private nosave int chatter_id, door_id;
private void housekeeping();
private void clean_chatters();
private void clean_doors();
void create() {
seteuid( master()->creator_file( file_name( TO ) ) );
// ({ mess, obv, size, func })
exit_types = ([
"standard" :({ 0, 1, 400, 0 }),
"corridor" :({ 0, 1, 250, 0 }),
"plain" :({ 0, 1, 10000, 0 }), /* very large */
"door" :({ 0, 1, 300, 0 }),
"stair" :({ 0, 1, 300, 0 }), /* going up? */
"hidden" :({ 0, 0, 300, 0 }), /* hidden non door exit */
"secret" :({ 0, 0, 300, 0 }), /* secret door */
"gate" :({ 0, 1, 450, 0 }),
"road" :({ 0, 1, 1300, 0 }),
"path" :({ 0, 1, 800, 0 }),
"window" :({ "$N climb$s through a window.\n", 0, 75, 0 }),
]);
door_types = ([
"door" : ({ 0, 0, "generic_key", 2, 0, 0 }),
"secret" : ({ 0, 0, "generic_key", 3, 1, 0 }),
"gate" : ({ 1, 0, "generic_key", 1, 0, 1 }),
"window" : ({ 1, 0, "generic_key", 1, 0, 1 }),
]);
opposite = ([
"north" : ({ 0, "$R$[the ]+south$R$" }),
"south" : ({ 0, "$R$[the ]+north$R$" }),
"east" : ({ 0, "$R$[the ]+west$R$" }),
"west" : ({ 0, "$R$[the ]+east$R$" }),
"northeast" : ({ 0, "$R$[the ]+southwest$R$" }),
"southwest" : ({ 0, "$R$[the ]+northeast$R$" }),
"southeast" : ({ 0, "$R$[the ]+northwest$R$" }),
"northwest" : ({ 0, "$R$[the ]+southeast$R$" }),
"up": ({ 0, "below"}), "down" : ({ 0, "above"}),
"out": ({ 0, "inside"}), "in" : ({ 0, "outside"}),
"exit": ({ 0, "inside"}), "enter" : ({ 0, "outside"}),
"hubward": ({ 0, "rimward"}), "rimward" : ({ 0, "hubward"}),
"turnwise": ({ 0, "widdershins"}),
"widdershins": ({ 0, "turnwise"}),
]);
chatters = allocate( CHATSIZE );
doors = allocate( DOORSIZE );
queued_doors = ({ 0 });
queued_chatters = ({ 0 });
recycled_doors = recycled_chatters = recycled_items = ({ });
call_out( (: housekeeping :), 4 );
} /* create() */
/**
* This method returns the current list of rooms that are enabled for
* chatting.
* @return the current chatters
*/
mixed query_chatters() { return chatters; }
/**
* This method returns the current list of doors handled by the room
* handler.
* @return the current array of doors
*/
mixed query_doors() { return doors; }
/**
* This method adds an exit type to the current list of available exit types.
* This is used when the room handler is setup to add all the used exit
* types.
* @param type the name of the exit type
* @param message the message to display when going through the exit
* @param obvious if the exit is obvious or not
* @param size the size of the exit (used for heigh restrictions)
* @param func the function to call when using the exit
* @return 1 if successfuly added, 0 if not
* @see remove_exit_type()
*/
int add_exit_type( string type, mixed message, mixed obvious,
int size, mixed func ) {
if( exit_types[type] )
return 0;
exit_types[type] = ({ message, obvious, size, func });
return 1;
} /* add_exit_type() */
/**
* This method remove the named exit from the type list.
* @param type the name of the exit type to remove
* @return always returns 1
* @see add_exit_type()
*/
int remove_exit_type( string type ) {
map_delete( exit_types, type );
return 1;
} /* remove_exit_type() */
/** @ignore yes */
void add_door( object thing ) {
int number;
number = random( DOORSIZE );
if( !pointerp( doors[ number ] ) )
doors[ number ] = ({ thing });
else
doors[ number ] += ({ thing });
} /* add_door() */
/**
* This method returns information about the door type, the door has
* extra information associated with it than the standard exit type.
* This function does a double job of trying to find the corresponding
* door on the other side of the room.
* @param type the type of the door
* @param direc the direction the door points
* @param dest the destination of the door
* @return the door type array of information
*/
mixed query_door_type( string type, string direc, mixed dest ) {
if( !door_types[type] )
return 0;
if( functionp(dest) )
error("Cannot use function pointer destinations with doors.\n");
// If there isn't a door on the other side. We don't join.
call_out("check_door", 1, ({ PO, direc }) );
return door_types[type] + ({ 0 });
} /* query_door_type() */
/**
* This method checks to see if the door exists or not.
* It is passed in the room we are going from and the direction the
* exit faces in the array.<br>
* <pre>({ room_from, direction })</pre><br>
* This is the function which generates those door xx not found messages.
* @param args the arguements passed into the function
* @see query_door_type()
*/
void check_door( mixed args ) {
int ignore;
string direc, dest;
if( !args[ 0 ] )
return;
args[ 0 ]->set_destination( args[ 1 ] );
dest = args[ 0 ]->query_destination( args[ 1 ] );
if( !dest ) {
tell_room( args[ 0 ], "Error: "+args[ 1 ]+" is no longer an exit.\n");
return;
}
if( !find_object( dest ) ) {
ignore = 1;
if( catch( call_other( dest, "??" ) ) ) {
tell_room( args[ 0 ], "Error: "+dest+" does not exist or does "
"not load.\n" );
args[ 0 ]->modify_exit( args[ 1 ], ({ "undoor", 0 }) );
return;
}
}
direc = dest->query_door( args[ 0 ] );
if( !direc && !args[ 0 ]->call_door( args[ 1 ], "query_one_way") ) {
tell_room( args[ 0 ], "Error: "+dest+" does not have a door coming "
"back here.\n" );
args[ 0 ]->modify_exit( args[ 1 ], ({ "undoor", 0 }) );
return;
}
args[ 0 ]->modify_exit( args[ 1 ], ({ "other", direc }) );
// If the other side wasn't loaded anyway, don't bother.
if( ignore )
return;
// This makes sure that whatever the states of the two sides,
// they'll both end up the same. Think about it...
args[ 0 ]->modify_exit( args[ 1 ], ({
"closed", dest->call_door( direc, "query_closed"),
"locked", dest->call_door( direc, "query_locked"),
"closed", dest->call_door( direc, "query_closed") }) );
if( !args[ 0 ]->call_door( args[ 1 ], "query_closed") &&
args[ 0 ]->query_property("location") == "outside" )
add_door( args[ 0 ]->query_door_control( args[ 1 ] ) );
} /* check_door() */
/**
* This method returns the information associated with the exit type.
* @param type the exit type to query
* @param dir the direction the type information is for
* @return a huge amount of info as specified above
*/
mixed query_exit_type( string type, string dir ) {
mixed s;
if( !s = opposite[dir] )
s = ({ 0, "elsewhere" });
if( !exit_types[type] )
return exit_types["standard"] + ({ s, 0, 0, 0, 0, 0, 0, 0, 0, 0 });
return exit_types[ type ] + ({ s, 0, 0, 0, 0, 0, 0, 0, 0, 0 });
} /* query_exit_type() */
/**
* This is the code that actually moves the thing around the place.
* It handles all the weirdness involved with dragging things and other
* such stuff.
* @param thing what is being moved
* @param dir the direction we are going
* @param dest the destination room
* @param exit the exit name
* @param enter the enter name
* @param move the string to tell the object when it moves
* @return 1 on success, 0 on failure
*/
int move_thing( object thing, string dir, mixed dest, mixed exit,
mixed enter, string move ) {
int ret;
string arrive, leave;
object dragging;
dragging = thing->query_dragging();
if( dragging && ENV(dragging) != ENV(thing)) {
thing->reset_dragging();
dragging = 0;
}
if( exit != "none" || objectp( dragging ) ) {
if( stringp( enter ) )
enter = ({ 1, enter });
else if ( !pointerp( enter ) )
enter = ({ 0, "somewhere"});
switch ( enter[ 0 ] ) {
case 0 :
arrive = replace( thing->query_msgin(), "$F", enter[1] );
break;
default :
arrive = enter[ 1 ];
}
if( stringp( exit) )
leave = exit;
else if( pointerp( exit ) )
leave = exit[ 0 ];
else
leave = thing->query_msgout();
leave = replace( leave, "$T", "$R$-"+ dir +"$R$" );
}
// Check position...
if( thing->query_position() != "crouching")
thing->return_to_default_position(1);
if( arrive || objectp( dragging ) ) {
thing->remove_hide_invis("hiding");
if( stringp( arrive ) && objectp( dragging ) ) {
arrive += "\n$C$"+thing->query_pronoun()+" drags "+
dragging->a_short()+" in behind "+thing->HIM+".";
}
if( stringp( leave ) && objectp( dragging ) ) {
leave += "\n$C$"+thing->HE+" drags "+
dragging->the_short()+" away behind "+thing->HIM+".";
}
if( stringp( move ) )
tell_object( thing, move );
ret = thing->move( dest, arrive, leave );
if( !ret && objectp( dragging ) ) {
if( !dragging->move(ENV(thing)) ) {
tell_object( thing, "You drag "+
dragging->the_short() +" behind you.\n");
thing->adjust_time_left( -DEFAULT_TIME );
} else {
tell_object( thing, "You fail to drag "+
dragging->the_short()+" behind you.\n");
}
}
} else {
if( stringp(move) )
tell_object( thing, move );
ret = thing->move( dest );
}
return !ret;
} /* move_thing() */
/**
* @ignore yes
* This function performs the door checks for exit_move(). It is called
* for the object moving and each of its followers. It returns 1 if
* the player can move or 0 if not, 2 if the player was dead
*/
int exit_move_door_checks( object thing, mixed closed ) {
closed->force_other();
if( closed->query_open() )
return 1;
if( thing->query_property("demon") || thing->query_property("dead") )
return 2;
if( thing->no_use_doors() ) {
tell_object( thing, "You cannot go through closed doors.\n");
return notify_fail("");
}
if( closed->query_locked() && !closed->moving_unlock(thing) ) {
// It is locked and invisible...
if( !closed->query_visible(thing) )
return 0;
tell_object( thing, closed->the_short()+" "+
({"is", "are"})[closed->query_how_many()] +" locked.\n");
return notify_fail("");
}
if( !closed->moving_open(thing) )
return 0;
return 1;
} /* exit_move_door_checks() */
/**
* @ignore yes
* This function performs the function checks for exit_move().
* It returns 1 if the player can move or 0 if not.
*/
int exit_move_func_checks( string verb, string special, object thing,
mixed func, object place ) {
if( stringp(func) )
return call_other( place, func, verb, thing, special );
if( functionp(func) )
return evaluate( func, verb, thing, special );
if( pointerp(func) )
return call_other( func[0], func[1], verb, thing, special );
return 1;
} /* exit_move_func_checks() */
/**
* This is the main code for moving someone. The move_thing code above
* should not be called directly. This code handlers all the followers
* and any other things that need to be handled.
* @param verb the movement verb
* @param extra extra information
* @param special special information
* @param thing the thing to move
* @return 1 on success, 0 on failure
*/
int exit_move( string verb, string extra, string special, object thing ) {
int open, flag;
string leave;
object place, follower;
object *okay, *all_followers, *tmp_followers, *more_followers;
mixed closed, func, dest_other, destination;
// Find the exit infomation.
place = ENV( thing );
verb = place->expand_alias( verb );
place->set_destination( verb );
// This checks that the destination is correctly set.
dest_other = (mixed)place->query_dest_other( verb );
if( !pointerp(dest_other) )
return 0;
if( thing->cannot_walk() )
return notify_fail("");
// This checks that the door exists if there should be one.
if( closed = place->query_door_control( verb ) )
open = closed->query_open();
if( objectp(closed) && !open &&
!( flag = exit_move_door_checks( thing, closed ) ) )
return 0;
// Check exit functions.
func = dest_other[ROOM_FUNC];
if( func && !thing->query_property("demon") &&
!exit_move_func_checks( verb, special, thing, func, place ) )
return 0;
if( place->query_relative( verb ) )
leave = thing->find_rel( verb, 0 );
else
leave = verb;
// Check height.
if( thing->query_height() > dest_other[ ROOM_SIZE ] &&
!( thing->query_position() == "crouching" &&
thing->query_height() / 3 <= dest_other[ ROOM_SIZE ] ) ) {
tell_object( thing, "You are too tall to go that way.\n");
return notify_fail("");
}
// Now actually move.
if(functionp( dest_other[ ROOM_EXIT ]))
special = evaluate(dest_other[ ROOM_EXIT ]);
else
special = dest_other[ ROOM_EXIT ];
destination = dest_other[ ROOM_DEST ];
if( functionp(destination) )
destination = evaluate( destination, thing );
if(functionp(dest_other[ROOM_ENTER])){
if( !move_thing( thing, verb, destination, special,
evaluate(dest_other[ROOM_ENTER]), dest_other[ ROOM_MESS ] ) )
return 0;
}else{
if( !move_thing( thing, verb, destination, special,
dest_other[ROOM_ENTER], dest_other[ ROOM_MESS ] ) )
return 0;
}
// exit_move_door_checks() retuned 2 - the player is dead.
if( flag == 2 )
tell_object( thing, "You ghost through "+closed->the_short()+".\n");
thing->return_to_default_position(1);
okay = ({ });
if( place && !dest_other[ ROOM_NO_FOLLOW ] ) {
// Get all the followers of the followers.
all_followers = thing->query_followers();
more_followers = all_followers;
do {
tmp_followers = ({ });
foreach( follower in more_followers ) {
// We only follow people that are actually here.
// Also make sure we don't end up with duplicates.
if( follower && ENV( follower ) == place )
tmp_followers |= follower->query_followers();
}
// Make sure we do not get repeated followers.
more_followers = tmp_followers - all_followers;
all_followers |= tmp_followers;
} while( sizeof(more_followers) );
// Move all those people following us too!
foreach ( follower in all_followers ) {
if( !objectp( follower ) ) {
thing->remove_follower( follower );
continue;
}
// Make sure they are in the start room and if they are a user
// they are interactive and the person they are following is
// visible and they aren't passed out and can walk.
if( ENV(follower) != place ||
( userp(follower) && !interactive(follower) ) ||
( !thing->query_visible(follower) || special == "none") ||
follower->query_property(PASSED_OUT) ||
follower->cannot_walk() )
continue;
// Do the door checks.
if( objectp(closed) && !open &&
!exit_move_door_checks( follower, closed ) )
continue;
// Do the function checks.
if( func && !follower->query_property("demon") &&
!exit_move_func_checks( verb, special, follower, func, place ) )
continue;
if( place->query_relative( verb ) ) {
leave = follower->find_rel( verb, 0 );
follower->reorient_rel( leave );
} else {
leave = verb;
}
if( follower->query_height() > dest_other[ ROOM_SIZE ] &&
!( follower->query_position() == "crouching" &&
follower->query_height() / 3 <= dest_other[ ROOM_SIZE ] ) ) {
tell_object( follower, "You are too tall to follow "+
thing->the_short()+" "+leave+".\n" );
continue;
}
if( function_exists("check_doing_follow", follower ) &&
!follower->check_doing_follow( thing, verb, special ) )
continue;
if(functionp(dest_other[ROOM_ENTER])){
if( move_thing( follower, verb, destination, special,
evaluate(dest_other[ROOM_ENTER]), dest_other[ ROOM_MESS ] ) &&
living(follower) ){
tell_object( follower, "You follow "+
thing->the_short()+" "+leave+".\n");
if( follower->query_visible(thing) )
okay += ({ follower });
follower->return_to_default_position(1);
follower->adjust_time_left( -DEFAULT_TIME );
} else {
tell_object( follower, "You fail to follow "+
thing->the_short()+" "+leave+".\n");
}
}else{
if( move_thing( follower, verb, destination, special,
dest_other[ROOM_ENTER], dest_other[ ROOM_MESS ] ) &&
living(follower) ) {
tell_object( follower, "You follow "+
thing->the_short()+" "+leave+".\n");
if( follower->query_visible(thing) )
okay += ({ follower });
follower->return_to_default_position(1);
follower->adjust_time_left( -DEFAULT_TIME );
} else {
tell_object( follower, "You fail to follow "+
thing->the_short()+" "+leave+".\n");
}
}
}
}
// Move everyone then do the look. Fix up problems with followers
// that have light, like the fireflies and blue lights.
thing->room_look();
if( sizeof(okay) ) {
okay->room_look();
tell_object( thing, query_multiple_short(okay)+" $V$0=follows,"
"follow$V$ you.\n");
}
if( objectp(closed) && !open )
closed->moving_close(thing);
return 1;
} /* exit_move() */
/** @ignore yes */
void add_chatter( object thing, int number ) {
number /= 4;
if( number > CHATSIZE - 1 )
number = CHATSIZE - 1;
if( !pointerp( chatters[ number ] ) )
chatters[ number ] = ({ thing });
else
chatters[ number ] += ({ thing });
} /* add_chatter() */
/** @ignore yes */
void check_chatters() {
object *things;
things = chatters[ 0 ];
chatters[0..<2] = chatters[1..<1];
chatters[<1] = 0;
if( pointerp( things ) ) {
things -= queued_chatters;
map( things, (: $1->make_chat() :) );
}
} /* check_chatters() */
/** @ignore yes */
void check_doors() {
int wind;
string dest, other, mess;
object mine, thing, *things;
things = doors[ 0 ];
doors[0..<2] = doors[1..<1];
doors[<1] = 0;
if( pointerp( things ) ) {
things -= queued_doors;
foreach( thing in things ) {
if( thing->query_closed() || thing->query_stuck() )
continue;
dest = thing->query_dest();
other = thing->query_other_id();
mine = thing->query_my_room();
wind = WEATHER_H->calc_actual( mine, WINDSP );
if( random( 25 ) > wind ) {
add_door( thing );
return;
}
switch( wind ) {
case -1000 .. 20 :
mess = "blow$s shut in the breeze.\n";
break;
case 21 .. 40 :
mess = "blow$s shut in the wind.\n";
break;
default :
mess = "slam$s shut in the wind.\n";
}
if( find_object( dest ) ) {
dest->modify_exit( other, ({"closed", 1 }) );
dest->tell_door( other, "The $D "+mess, 0 );
}
thing->set_closed( 1 );
thing->tell_door("The $D "+mess, 0 );
}
}
} /* check_doors() */
/** @ignore yes */
private void housekeeping() {
call_out( (: check_doors :), 2 );
call_out( (: housekeeping :), 4 );
check_chatters();
} /* housekeeping() */
/** @ignore yes */
private object *clean_array( object *obs, object *minus ) {
if( sizeof(obs) )
obs -= minus;
return obs;
} /* clean_array() */
/** @ignore yes */
private void clean_doors() {
door_id = 0;
doors = map( doors, (: clean_array( $1, $2 ) :), queued_doors );
queued_doors = ({ 0 });
} /* clean_doors() */
/** @ignore yes */
private void clean_chatters() {
chatter_id = 0;
chatters = map( chatters, (: clean_array( $1, $2 ) :), queued_chatters );
queued_chatters = ({ 0 });
} /* clean_chatters() */
/**
* This method recycles a chatter object and is used by rooms.
* It calls reload_object() on the chatter, removes it from the
* chatters array and adds it to the recycled_chatters array.
* It can then be re-used by using get_chatter().
* Only objects whose base_name is CHATTER_OBJECT can be recycled.
* @param ob the chatter object to recycle
* @return 1 if successfully recycled, 0 if not
* @see get_chatter()
*/
int recycle_chatter( object ob ) {
if( base_name(ob) == CHATTER_OBJECT ) {
if( !chatter_id )
chatter_id = call_out( (: clean_chatters :), 15 );
reload_object(ob);
queued_chatters += ({ ob });
recycled_chatters += ({ ob });
return 1;
}
return 0;
} /* recycle_chatter() */
/**
* This method gets a recycled chatter from the handler,
* or clones a new one, if needed. This is used by rooms.
* @return a chatter object
* @see recycle_chatter()
*/
object get_chatter() {
object ret;
if( sizeof( recycled_chatters -= ({ 0 }) ) ) {
ret = recycled_chatters[0];
recycled_chatters = recycled_chatters[1..];
chatter_hits++;
if( chatter_id ) {
remove_call_out(chatter_id);
clean_chatters();
}
} else {
ret = clone_object(CHATTER_OBJECT);
chatter_misses++;
}
return ret;
} /* get_chatter() */
/**
* This method recycles a door object and is used by rooms.
* It calls reload_object() on the door, removes it from the
* doors array and adds it to the recycled_doors array.
* It can then be re-used by using get_door().
* Only objects whose base_name is DOOR_OBJECT can be recycled.
* @param ob the door object to recycle
* @return 1 if successfully recycled, 0 if not
* @see get_door()
*/
int recycle_door( object ob ) {
if( base_name(ob) == DOOR_OBJECT ) {
if( !door_id )
door_id = call_out( (: clean_doors :), 15 );
reload_object(ob);
queued_doors += ({ ob });
recycled_doors += ({ ob });
return 1;
}
return 0;
} /* recycle_door() */
/**
* This method gets a recycled door from the handler,
* or clones a new one, if needed. This is used by rooms.
* @return a door object
* @see recycle_door()
*/
object get_door() {
object ret;
if( sizeof( recycled_doors -= ({ 0 }) ) ) {
ret = recycled_doors[0];
recycled_doors = recycled_doors[1..];
door_hits++;
if( door_id ) {
remove_call_out(door_id);
clean_doors();
}
} else {
ret = clone_object(DOOR_OBJECT);
door_misses++;
}
return ret;
} /* get_door() */
/**
* This method recycles an item object and is used by rooms.
* It calls reload_object() on the item and puts it in the
* recycled_items array.
* It can then be re-used by using get_item().
* Only objects whose base_name is ITEM_OBJECT can be recycled.
* @param ob the item object to recycle
* @return 1 if successfully recycled, 0 if not
* @see get_item()
*/
int recycle_item( object ob ) {
if( base_name(ob) == ITEM_OBJECT ) {
reload_object(ob);
recycled_items += ({ ob });
return 1;
}
return 0;
} /* recycle_item() */
/**
* This method gets a recycled item object from the handler,
* or clones a new one, if needed. This is used by rooms.
* @return an item object
* @see recycle_item()
*/
object get_item() {
object ret;
if( sizeof( recycled_items -= ({ 0 }) ) ) {
ret = recycled_items[0];
recycled_items = recycled_items[1..];
item_hits++;
} else {
ret = clone_object(ITEM_OBJECT);
item_misses++;
}
ret->set_my_room(PO);
return ret;
} /* get_item() */
/** @ignore yes */
object *query_recycled_chatters() { return recycled_chatters; }
/** @ignore yes */
object *query_recycled_doors() { return recycled_doors; }
/** @ignore yes */
object *query_recycled_items() { return recycled_items; }
/** @ignore yes */
void dest_me() {
if( sizeof( recycled_chatters -= ({ 0 }) ) )
recycled_chatters->dest_me();
if( sizeof( recycled_doors -= ({ 0 }) ) )
recycled_doors->dest_me();
if( sizeof( recycled_items -= ({ 0 }) ) )
recycled_items->dest_me();
destruct(TO);
} /* dest_me() */
/**
* @ignore yes
* Survive the updates.
*/
mapping query_dynamic_auto_load() {
return ([
"exit_types" : exit_types,
"door_types" : door_types,
"opposite" : opposite,
"chatters" : chatters,
"doors" : doors,
]);
} /* query_dynamic_auto_load() */
/**
* @ignore yes
* Survive the updates.
*/
void init_dynamic_arg( mapping maps ) {
if( maps["exit_types"] )
exit_types = maps["exit_types"];
if( maps["door_types"] )
door_types = maps["door_types"];
if( maps["opposite"] )
opposite = maps["opposite"];
if( maps["chatters"] )
chatters = maps["chatters"];
if( maps["doors"] )
doors = maps["doors"];
} /* init_dynamic_arg() */
/** @ignore yes */
mixed stats() {
int door_count, chatter_count;
int doors_missing, chatters_missing, items_missing;
mixed temp;
foreach( temp in chatters )
chatter_count += sizeof( temp );
foreach( temp in doors )
door_count += sizeof( temp );
doors_missing -= sizeof( children(DOOR_OBJECT) -
({ find_object(DOOR_OBJECT) }) ) - door_misses;
chatters_missing -= sizeof( children(CHATTER_OBJECT) -
({ find_object(CHATTER_OBJECT) }) ) - chatter_misses;
items_missing -= sizeof( children(ITEM_OBJECT) -
({ find_object(ITEM_OBJECT) }) ) - item_misses;
return ({
({"exit types", sizeof( exit_types ) }),
({"door types", sizeof( door_types ) }),
({"opposites", sizeof( opposite ) }),
({"chatters", chatter_count }),
({"doors", door_count }),
({"recycled chatters", sizeof(recycled_chatters) }),
({"recycled doors", sizeof(recycled_doors) }),
({"recycled items", sizeof(recycled_items) }),
({"chatter hits", chatter_hits }),
({"door hits", door_hits }),
({"item hits", item_hits }),
({"chatter misses", chatter_misses }),
({"door misses", door_misses }),
({"item misses", item_misses }),
({"missing chatters", chatters_missing > 0 ? chatters_missing : 0 }),
({"missing doors", doors_missing > 0 ? doors_missing : 0 }),
({"missing items", items_missing > 0 ? items_missing : 0 }),
({"total hits", chatter_hits + door_hits + item_hits }),
({"total misses", chatter_misses + door_misses + item_misses }),
});
} /* stats() */