/* RCS stuff... -*- LPC -*-
* $Locker: pinkfish $
*
* $Id: basic_room.c,v 1.10 2001/01/14 03:28:59 ceres Exp pinkfish $
*
*/
/**
* The standard room inheritable. This contains all the stuff needed to
* construct a standard room.
* @author Pinkfish
* @see /std/room/outside.c
*/
#include <armoury.h>
#include <dirs.h>
#include <door.h>
#include <move_failures.h>
#include <room.h>
#include <position.h>
#include <situations.h>
#include <twilight.h>
#include <nroff.h>
inherit "/std/basic/cute_look";
inherit "/std/basic/desc";
inherit "/std/basic/extra_look";
inherit "/std/basic/light";
inherit "/std/basic/property";
inherit "/std/basic/export_inventory";
inherit "/std/basic/help_files";
nosave int do_setup;
nosave int *co_ord;
nosave int co_ord_calculated;
nosave string long_exit;
nosave string short_exit;
nosave string theft_handler;
nosave string *aliases;
nosave string *exits;
nosave object item;
nosave object chatter;
nosave object sitchanger;
nosave object effects;
nosave object linker;
nosave object terrain;
nosave object wall;
nosave object *hidden_objects;
private nosave object* _use_internal_objects;
nosave mapping door_control;
nosave mixed *dest_other;
nosave int enchant_time;
nosave int background_enchant;
nosave float dynamic_enchant;
private nosave int last_visited;
// This is the day/night stuff.
nosave string *variablelongs;
nosave mixed variablechats;
nosave mixed variableitems;
nosave int is_day = -1;
varargs int add_item( mixed shorts, mixed desc, int no_plural );
int remove_item( string word );
varargs void room_chat( mixed *args, object chatobj );
void set_not_replaceable(int replace);
int query_not_replaceable();
void set_night_long( string str );
protected void create() {
object *npcs;
object virt_room;
string *inhs;
desc::create();
extra_look::create();
property::create();
export_inventory::create();
light::create();
set_can_export_inventory();
set_ignore_livings_in_inventory( 0 );
aliases = ({ });
exits = ({ });
hidden_objects = ({ });
_use_internal_objects = ({ });
door_control = ([ ]);
dest_other = ({ });
seteuid( (string)"/secure/master"->creator_file( file_name(
this_object() ) ) );
add_property( "location", "inside" );
add_property( "here", "on the floor" );
if ( !do_setup ) {
this_object()->setup();
this_object()->reset();
}
if ( find_object( "/obj/handlers/map" ) ) {
catch( "/obj/handlers/map"->check_map( file_name( this_object() ) ) );
}
if( (virt_room = find_object("/room/virtual") ) ){
npcs = ({}) + all_inventory(virt_room);
catch(npcs->real_room(file_name()));
}
// Check to see if the room is replaceable...
inhs = inherit_list(this_object());
if (sizeof(inhs) == 1) {
if (replaceable(this_object(), ({ "setup" }))) {
// Do it after a delay so that setup() has definately finished.
call_out(function(string fname) {
if (!query_not_replaceable()) {
replace_program(fname);
}
}, 0, inhs[0]);
}
}
} /* create() */
/**
* Returns 1 to indicate that this object is a room.
* @return 1 to indicate that this is a room
*/
int query_is_room()
{
return 1;
}
/**
* Returns the current enchantment level of the room. The enchanment
* level controls things like what happens when you flip coins and
* some special messages which give wizards some idea about magic
* levels.
* @return the current enchantment
* @see set_enchant()
*/
int query_enchant() {
int enchant_level = to_int( floor( 0.5 + dynamic_enchant *
exp( -0.693 *
( time() - enchant_time ) /
ENCHANT_HALF)) +
background_enchant );
if ( enchant_level > 5000 ) {
return 5000;
}
return enchant_level;
} /* query_enchant() */
/**
* Sets the current enchantment level of the room. The enchanment
* level controls things like what happens when you flip coins and
* some special messages which give wizards some idea about magic
* levels.
* When called from the room itself, it sets a background level of
* enchantment that don't decay, when called from another object it
* sets the current enchantment which then decays towards the background
* level.
* @param number the new enchantment level to set
* @see query_enchant()
*/
int set_enchant(int number) {
if (number < 0) {
number = 0;
}
if (previous_object()==this_object()) {
/* setting up the background enchantment */
background_enchant = (float) number;
dynamic_enchant = 0.0;
enchant_time = time();
} else {
/* by design, this can result in negative dynamic enchantment
* it is definitely possible to suck out all magic in an area,
* but it'll seep back in if the background level is higher.
*/
dynamic_enchant = number - background_enchant;
enchant_time=time();
}
return number;
} /* set_enchant() */
/**
* Adds number to the current enchantment level of the room.
* The enchanment level controls things like what happens when you
* flip coins and some special messages which give wizards some
* idea about magic levels.
* @param number the new enchantment level to set
* @see query_enchant()
*/
int add_enchant( int number ) {
dynamic_enchant = dynamic_enchant * exp( -0.693 *
( time() - enchant_time ) /
ENCHANT_HALF ) + number;
enchant_time = time();
return floor( 0.5 + dynamic_enchant ) + background_enchant;
} /* add_enchant() */
/**
* This method sets the background enchantment of the room.
* @return the theft handler of the room
* @see set_enchant()
*/
void set_background_enchant( int number ) {
background_enchant = number;
} /* set_background_enchant() */
/**
* This method sets the current dynamic enchantment of the room.
* @return the theft handler of the room
* @see set_enchant()
*/
void set_dynamic_enchant( float number ) {
dynamic_enchant = number;
enchant_time = time();
} /* set_dynamic_enchant() */
/**
* This method returns the background enchantment of the room.
* @return the theft handler of the room
* @see query_enchant()
*/
int query_background_enchant() {
return background_enchant;
} /* query_background_enchant() */
/**
* This method returns the current dynamic enchantment of the room.
* @return the theft handler of the room
* @see query_enchant()
*/
float query_dynamic_enchant() {
dynamic_enchant = dynamic_enchant * exp( -0.693 *
( time() - enchant_time ) /
ENCHANT_HALF );
enchant_time = time();
return dynamic_enchant;
} /* set_dynamic_enchant() */
/**
* Returns the current co-ordinates of the room. The co-ordinates are
* 3d, ({ x, y, z }). So an array with three elements.
* @return the current co-ordinates
* @see set_co_ord()
*/
int *query_co_ord() {
if ( pointerp( co_ord ) ) {
return copy(co_ord);
}
return 0;
} /* query_co_ord() */
/**
* Sets the current co-ordinates of the room. The co-ordinates are
* 3d, ({ x, y, z }). So an array with three elements.
* @param new_co_ord the new co-ordinates for the room.
* @see query_co_ord()
* @see query_co_ord_calculated()
*/
void set_co_ord( int *new_co_ord ) {
if ( !pointerp( new_co_ord ) ) {
write( "Warning: Co-ordinate must be an array.\n" );
return;
}
if ( sizeof( new_co_ord ) != 3 ) {
write( "Warning: The co-ordinate must have three elements.\n" );
return;
}
co_ord = new_co_ord;
if ( previous_object() == this_object() ) {
co_ord_calculated = 0;
} else {
co_ord_calculated = 1;
}
} /* set_co_ord() */
/**
* This tells us if the co-ordinates were set or if they were calculated.
* If they were set with set_co_ord then the value of this will be 0
* otherwise it will be 1.
* @return 1 if it is calculated, 0 if it is not
* @see query_co_ord()
* @see set_co_ord()
*/
int query_co_ord_calculated() { return co_ord_calculated; }
/**
* This returns the long exit string. This is calculated when it is
* first needed by the calc_long_exit function.
* @return the long exit string
* @see calc_long_exit()
* @see long()
*/
string query_long_exit() { return long_exit; }
/**
* This method creates the long exit description used in the room long
* descriptions.
* @see query_long_exit()
*/
void calc_long_exit() {
int i, add;
string *words;
mixed tmp;
words = ({ });
for ( i = 0; i < sizeof( dest_other ); i += 2 ) {
tmp = dest_other[ i + 1 ][ ROOM_OBV ];
if ( !tmp ) {
continue;
}
if ( intp( tmp ) && tmp ) {
add = 1;
}
if ( stringp( tmp ) ) {
add = (int)call_other( this_object(), tmp, dest_other[ i ] );
}
if ( pointerp( tmp ) ) {
add = (int)call_other( tmp[ 0 ], tmp[ 1 ], dest_other[ i ] );
}
if ( add ) {
if(dest_other[i+1][ROOM_REL]) {
words += ({ "$R$-"+ dest_other[ i ] +"$R$" });
} else {
words += ({ dest_other[i] });
}
}
}
switch ( sizeof( words ) ) {
case 0 :
long_exit = "There are no obvious exits.";
break;
case 1 :
long_exit = "There is one obvious exit: "+ words[ 0 ];
break;
default :
long_exit = "There are "+ query_num( sizeof( words ), 0 ) +
" obvious exits: "+ query_multiple_short( words ) +
".";
}
} /* calc_long_exit() */
/**
* This method returns the current theft handler for the room.
* @return the theft handler of the room
* @see set_theft_handler()
*/
string query_theft_handler() { return theft_handler; }
/**
* This method sets the current theft handler for the room.
* @param word the new theft handler for the room
* @see query_theft_handler()
*/
void set_theft_handler( string word ) { theft_handler = word; }
/**
* This method returns the current exit aliases for the room.
* @return the exit aliases of the room
* @see add_alias()
* @see remove_alias()
*/
string *query_aliases() { return copy( aliases ); }
/**
* This method adds an exit alias to the room.
* Aliases are convenient extra forms that can be attached to certain
* exits. In the above functions, the variable names is either a string
* or an array of strings and is, respectively, the alias or aliases
* for the direction passed in word. Since, sometimes, the same alias
* could be used for more than one exit, remove_alias() requires both
* alias(es) and direction in order to remove the correct alias(es).
* @param names the exit names to alias
* @param word the name to alias them too
* @see query_aliases()
* @see remove_alias()
* @example
* add_exit( "north", PATH +"dining_hall", "corridor" );
* add_alias( ({ "enter", "enter hall", "enter dining hall" }), "north" );
* @example
* add_exit( "board carriage", PATH +"carriage", "door" );
* add_alias( "board", "board carriage" );
*/
void add_alias( mixed names, string word ) {
string name;
if ( !aliases ) {
aliases = ({ });
}
if ( pointerp( names ) ) {
foreach ( name in names ) {
add_alias( name, word );
}
return;
}
aliases += ({ word, names });
if ( find_call_out( "calc_exits" ) == -1 ) {
call_out( "calc_exits", 1 );
}
} /* add_alias() */
/**
* This method removes the exit aliases from the room.
* Aliases are convenient extra forms that can be attached to certain
* exits. In the above functions, the variable names is either a string
* or an array of strings and is, respectively, the alias or aliases
* for the direction passed in word. Since, sometimes, the same alias
* could be used for more than one exit, remove_alias() requires both
* alias(es) and direction in order to remove the correct alias(es).
* @param names the names to remove
* @param word what they were aliased to
* @see add_alias()
* @see query_aliases()
* @example
* remove_exit( "board carriage" );
* remove_alias( "board", "board carriage" );
*/
void remove_alias( mixed names, string word ) {
int i;
string name;
if ( !aliases ) {
return;
}
if ( pointerp( names ) ) {
foreach ( name in names ) {
remove_alias( name, word );
}
return;
}
for ( i = sizeof( aliases ) - 2; i >= -1; i -= 2 ) {
if ( ( aliases[ i ] == word ) && ( aliases[ i + 1 ] == names ) ) {
aliases = delete( aliases, i, 2 );
}
}
} /* remove_alias() */
/**
* This returns the current array of exits.
* @return the exits array
* @see add_exit()
* @see remove_exit()
* @see modify_exit()
*/
string *query_exits() { return copy(exits); }
/**
* This method removes all the current exits in the room.
* @see add_exit()
* @see remove_exit()
* @see modify_exit()
*/
void reset_exits() { exits = ({ }); }
/**
* This method returns the current item object.
* @return the current item object
* @see add_item()
*/
object query_item() { return item; }
/**
* This method returns the current chatter object.
* @return the chatter object
* @see add_room_chat()
*/
object query_chatter() { return chatter; }
/**
* This method returns the current situation changer object.
* @return the situation changer object
* @see add_situation()
* @see automate_situation()
* @see change_situation
*/
object query_situation_changer() { return sitchanger; }
/**
* This method returns the current effects object.
* @return the effects object
* @see add_effect()
*/
object query_effects() { return effects; }
/**
* This method returns the current linker object.
* @return the linker object
*/
object query_linker() { return linker; }
/**
* This method returns the current terrain object.
* @return the terrain object
* @see add_room_chat()
*/
object query_terrain() { return terrain; }
/**
* This method returns the current wall object.
* @return the wall object
* @see add_room_chat()
*/
object query_wall() { return wall; }
/**
* This returns the current array of hidden objects. The hidden objects
* are used to allow things to not actually be in the room description
* but be able to be manipulated by commands.
* @see add_hidden_object()
* @see remove_hidden_object()
* @return the array of hidden objects
*/
object *query_hidden_objects() { return hidden_objects + ({ }); }
/**
* This puts a hidden object into a room. A hidden object is an object that
* exists in the room as far as all the find_match calls go. So, for look at's
* and so on, but does not actually exist in the room so it does not show up
* in the inventory when the player does a look. This is the method used for
* putting signs and doors into rooms, that actually have shorts and you can
* do things to, but do not show up in the inventory. The function init is also
* called on these objects when init is called in the room. The only thing you
* cannot put in your init function is an add_action. You can however define
* up bunches of add_commands...
* <p>
* If this sounds complicated. Think of it as an object that IS in the room, but
* you cannot see it.
* <p>
* A word of warning here, the init() function will *not* be called on all
* the players when the object is added as hidden. This means that the
* commands on it will not be available until the player re-enters the room.
* You could get around this by moving everyone out of the room and
* then back in again.
* @example
* #include <room.h>
* sign = clone_object(PATH + SIGN);
* add_hidden_object(sign);
* @example
* // Add a hidden object that has actions we want players to be able to
* // use.
* add_hidden_object(fluffy_container);
* players = filter(all_inventory(), (: living($1) :));
* players->move(ROOM_VOID);
* // This forces init() to be recalled. (This is realtivatively icky
* // way of doing it, but the driver does not give us many alternatives).
* players->move(this_object());
* @see query_hidden_object()
* @see remove_hidden_object()
* @param thing the hidden object to add
* @return 1 if successful, 0 on a failure
*/
int add_hidden_object( object thing ) {
if ( member_array( thing, hidden_objects ) != -1 ) {
return 0;
}
hidden_objects += ({ thing });
return 1;
} /* add_hidden_object() */
/**
* This method removes a hidden object.
* @param thing the hidden object to remove
* @return 1 on success, 0 on failure
* @see add_hidden_object()
* @see query_hidden_objects()
*/
int remove_hidden_object( object thing ) {
int i;
i = member_array( thing, hidden_objects );
if ( i == -1 ) {
return 0;
}
hidden_objects = hidden_objects[0..i - 1] + hidden_objects[i + 1..];
return 1;
} /* remove_hidden_object() */
/**
* This method adds an object whose interior bits want to be able to export
* commands. You can use this for tables an so on, so that stuff on a table
* can still be used.
* @param thing the thing whose inventory bits are to be exported
*/
void add_use_internal_object(object thing) {
_use_internal_objects |= ({ thing });
} /* add_use_internal_objects() */
/**
* This method removes an object whose interor bits want to export.
* @param thing the object to remove
*/
void remove_use_internal_object(object thing) {
_use_internal_objects -= ({ thing });
} /* remove_use_internal_object() */
/**
* This method returns all the current use internal objects available.
* @return the list of use internal objects here
*/
object* query_use_internal_objects() {
return _use_internal_objects;
} /* query_use_internal_objects() */
/**
* This returns the information about the door in the specified direction.
* @param direc the direction to query the door in
* @return the door control information
* @see modify_exit()
*/
varargs mixed query_door_control( string direc ) {
if ( !stringp( direc ) ) {
return copy( door_control );
}
return door_control[ direc ];
} /* query_door_control() */
/**
* This returns information about the exits in the room. This is the
* information set by modify_exit(). The values from this are probably
* not very useful for normal coding.
* @see modify_exit()
* @see query_dest_dir()
*/
varargs mixed *query_dest_other( string direc ) {
int i;
if ( !stringp( direc ) ) {
return copy( dest_other );
}
i = member_array( direc, dest_other );
if ( i == -1 ) {
return 0;
}
return copy( dest_other[ i + 1 ] );
} /* query_dest_other() */
/**
* Returns an array containing just the destinations and directions used to
* get there. This is useful for monster or whatever that you want to scan a
* room for exits to leave out of. The array is of the format. ({ direction1,
* destination1, direction2, destination2, ... })
* <p>
* The thing passed in is used as the basis for the relative directions
* if it is an object. If it is not an object then this is ignored
* altogether.
* @see query_dest_other()
* @see add_exit()
* @param thing used to get the relative directions according to thing
* @return the array of direction, destination pairs
*/
varargs string *query_dest_dir( object thing ) {
int i;
string *ret;
ret = ({ });
for ( i = sizeof( dest_other ) - 2; i > -1; i -= 2 ) {
if ( !dest_other[ i + 1 ][ ROOM_REL ] || !objectp( thing ) ) {
ret += ({ dest_other[ i ], dest_other[ i + 1 ][ ROOM_DEST ] });
} else {
ret += ({ (string)thing->find_rel( dest_other[ i ] ),
dest_other[ i + 1 ][ ROOM_DEST ] });
}
}
return ret;
} /* query_dest_dir() */
/**
* This method just returns all the directions available to leave from
* the room.
* <p>
* The thing passed in is used as the basis for the relative directions
* if it is an object. If it is not an object then this is ignored
* altogether.
* <p>
* <b>Strawberries</b>
* <p>
* Starting from a above,<br>
* Working slowly down under.<br>
* Sliding up the sides<br>
* Eating a meal, fresh cream and syrup.<br>
* <p>
* Round and round, and round again<br>
* Grining micheviously<br>
* One tongue at play<br>
* Firm and hard, fresh strawberries today.
*
* @see query_dest_other()
* @see add_exit()
* @param thing used to get the relative directions according to thing
* @return the array of directions
*/
varargs string *query_direc( object thing ) {
int i;
string *ret;
ret = ({ });
for ( i = sizeof( dest_other ) - 2; i > -1; i -= 2 ) {
if ( !dest_other[ i + 1 ][ ROOM_REL ] || !objectp( thing ) ) {
ret += ({ dest_other[ i ] });
} else {
ret += ({ (string)thing->find_rel( dest_other[ i ] ) });
}
}
return ret;
} /* query_direc() */
/**
* This method returns the destination room for an exit.
* @param exit the exit name
* @return the path of the destination room, or ROOM_VOID on error
* @see query_dest_dir()
* @example
* #include <room.h>
* string dest;
*
* dest = room->query_destination("south");
* if (dest == ROOM_VOID) {
* do_error();
* } else {
* do_move("south");
* }
*/
string query_destination( string exit ) {
int i;
i = member_array( exit, dest_other );
if ( ( i < 0 ) && objectp( this_player() ) )
i = member_array( (string)this_player()->reorient_rel( exit ),
dest_other );
if ( i < 0 )
return ROOM_VOID;
return dest_other[ i + 1 ][ ROOM_DEST ];
} /* query_destination() */
/** @ignore yes */
int test_add( object thing, int flag ) { return 1; }
/** @ignore yes */
int test_remove( object thing, int flag, mixed dest ) { return 1; }
/** @ignore yes */
int add_weight( int number ) { return 1; }
/** @ignore yes */
int query_no_writing() { return 1; }
/** @ignore yes */
int query_decay() { return 10; }
/**
* This method sets the default attack speed for the room.
* This defaults to 15.
* @return the default attack speed
*/
int attack_speed() { return 15; }
/**
* This is the message to print instead of the room description when the
* room is dark. It defaults to the message "It's dark in here isn't it?".
* @return the dark message
* @see set_dark_mess()
* @see long()
* @see query_bright_mess()
*/
string query_dark_mess() {
mixed dark_mess;
if ( !stringp( dark_mess = query_property( "dark mess" ) ) ) {
return "It's dark here, isn't it?";
}
return dark_mess;
} /* query_dark_mess() */
/**
* This method sets the dark message associated with the room.
* @param word the new dark message
* @see query_dark_mess()
* @see long()
*/
void set_dark_mess( string word ) {
add_property( "dark mess", word );
} /* set_dark_mess() */
/**
* This method returns the message to use when it is too bright to see in
* the room. It defaults to: "It's too bright to see anything!".
* @return the message to print when it is too bright
* @see query_dark_mess()
* @see long()
* @see set_bright_mess()
*/
string query_bright_mess() {
mixed bright_mess;
if ( !stringp( bright_mess = query_property( "bright mess" ) ) ) {
return "It's too bright to see anything!";
}
return bright_mess;
} /* query_bright_mess() */
/**
* This method sets the bright message associated with the room.
* @param word the new bright message
* @see query_bright_mess()
* @see long()
*/
void set_bright_mess( string word ) { add_property( "bright mess", word ); }
/**
* This method queries the size of the room. The default size of a room
* is 10x10x10. A room can be any rectangular size, this method will return
* an array of three elements if the room is a non-cube. If it returns
* a single number then the room is assumed to be cubic.
* <p>
* ({ north-south size, east-west size, up-down size })
* <p>
* The sizes are all radii's so they are half the actual width of the room.
* @return the size of the room
* @see set_room_size()
* @see query_room_size_array()
*/
mixed query_room_size() {
mixed room_size;
room_size = query_property( "room size" );
if ( !room_size ) {
return 10;
}
return room_size;
} /* query_room_size() */
/**
* This method returns the size of the room as a three element array always.
* <p>
* ({ north-south size, east-west size, up-down size })
* <p>
* The sizes are all radii's so they are half the actual width of the room.
* @return the size of the room as a three element array
* @see query_room_size()
* @see set_room_size()
*/
int *query_room_size_array() {
mixed room_size;
room_size = query_room_size();
if ( pointerp( room_size ) ) {
return room_size;
}
return ({ room_size, room_size, room_size });
} /* query_room_size_array() */
/**
* This method sets the rooms principle radii. If the parameter isa single
* number then the room is assumed to be cubic and dimension applies in
* all directions. If the input is a three element array then the elements
* apply to all the directions.<br>
* ({ north-south size, east-west size, up-down size })
* <p>
* The sizes are all radii's so they are half the actual width of the room.
* @param number the new size of the room
* @see query_room_size()
* @see query_room_size_array()
*/
void set_room_size( mixed number ) {
if ( intp( number ) ) {
add_property( "room size", number );
return;
}
if ( pointerp( number ) ) {
if ( sizeof( number ) == 3 ) {
add_property( "room size", number );
return;
}
}
write( "Room size must be an integer or an array of three integers.\n" );
} /* set_room_size() */
/** @ignore yes */
int id( string word ) { return 0; }
string expand_alias( string word ) {
int i;
if ( !aliases || !sizeof( aliases ) ) {
return word;
}
i = member_array( word, aliases );
if ( i == -1 ) {
return word;
}
if ( i % 2 ) {
return aliases[ i - 1 ];
}
return word;
} /* expand_alias() */
/**
* This method returns the exit string used when in brief mode.
* @return the brief exit string
* @see calc_exit_string()
*/
string calc_short_exit_string() {
int i, add;
string *words;
mixed tmp;
words = ({ });
for ( i = 0; i < sizeof( dest_other ); i += 2 ) {
tmp = dest_other[ i + 1 ][ ROOM_OBV ];
if ( !tmp ) {
continue;
}
if ( intp( tmp ) && tmp ) {
add = 1;
}
if ( stringp( tmp ) ) {
add = (int)call_other( this_object(), tmp, dest_other[ i ] );
}
if ( pointerp( tmp ) ) {
add = (int)call_other( tmp[ 0 ], tmp[ 1 ], dest_other[ i ] );
}
if ( add ) {
if ( tmp = SHORTEN[ dest_other[ i ] ] ) { // is there a short form?
if (dest_other[i+1][ROOM_REL]) {// is the exit relative
words += ({ "$r$-"+tmp+"$r$" });
} else {
words += ({ tmp });
}
} else { // no short form
if (dest_other[i+1][ROOM_REL]) { // is the exit relative?
words += ({ "$r$-"+dest_other[ i ]+"$r$" });
} else {
words += ({ dest_other[i] });
}
}
}
}
if(!sizeof(words)) {
return " [none]";
}
return " ["+ implode( words, "," ) +"]";
} /* calc_short_exit_string() */
/**
* This method returns the short exit string. The short exit string is the
* string used in 'brief' mode of a players look.
* @return the short exit string
* @see calc_short_exit_string()
* @see query_exit_string()
*/
string query_short_exit_string() {
string tmp;
if(short_exit) {
return this_player()->colour_event("exits", "%^GREEN%^") +
short_exit + "%^RESET%^";
}
tmp = calc_short_exit_string();
if (!query_property("no exit cache")) {
short_exit = tmp;
}
return this_player()->colour_event("exits", "%^GREEN%^") +
tmp + "%^RESET%^";
} /* query_short_exit_string() */
/**
* @ignore yes
*/
string enchant_string() {
string words;
words = (string)this_object()->query_property( "octarine_mess" );
if ( words ) {
return words +"\n";
}
switch ( query_enchant() ) {
case 0 .. 49 :
return "";
case 50 .. 149 :
return "There is the residual taste of magic in this place.\n";
case 150 .. 299 :
return "This place has seen some use of magic.\n";
case 300 .. 499 :
return "A considerable amount of magic has been used here.\n";
case 500 .. 749 :
return "A very large quantity of magic has been manipulated here.\n";
case 750 .. 1000 :
return "You can feel the Dungeon Dimensions trying to push in.\n";
case 1001 .. 1500 :
return "Little sparks flash in from the Dungeon Dimensions.\n";
case 1501 .. 2000 :
return "Apparations of things with lots of tentacles seem to be "
"on the edge of your vision.\n";
default :
return "So much magic has been expended here that the area is in "+
"danger of dumping itself into the Dungeon Dimensions.\n";
}
} /* enchant_string() */
/** @ignore yes */
string long( string word, int dark ) {
string ret;
if ( !long_exit ) {
calc_long_exit();
}
if ( dark ) {
if ( dark < 0 ) {
ret = this_object()->query_dark_mess() +"\n";
} else {
ret = this_object()->query_bright_mess() +"\n";
}
if ( query_property( "location" ) == "outside" ) {
ret += "$weather$";
}
if ( ( dark == 1 ) || ( dark == -1 ) ) {
ret = "$C$"+ a_short() +". "+ ret +
this_player()->colour_event("exits", "%^GREEN%^") +long_exit
+"%^RESET%^\n";
if ( query_contents( "" ) != "" ) {
ret += this_player()->colour_event("inventory", "") + "Some objects you can't make out are here.%^RESET%^\n";
}
}
} else {
if ( query_property( "location" ) == "outside" ) {
ret = "$long$";
} else {
ret = query_long();
}
if(!ret) {
ret = "Erk, this room seems to be broken.\n";
}
word = calc_extra_look();
if ( stringp( word ) && ( word != "" ) ) {
ret += word;
}
if ( this_player()->query_see_octarine() ) {
ret += enchant_string();
}
if ( query_property( "location" ) == "outside" ) {
ret += "$weather$";
}
ret += this_player()->colour_event("exits", "%^GREEN%^") +
long_exit +"%^RESET%^\n"+ query_contents( "" );
}
if ( query_property( "no exit cache" ) ) {
long_exit = 0;
}
return ret;
} /* long() */
/** @ignore yes */
string pretty_short( object thing ) {
int dark;
if ( thing ) {
dark = (int)thing->check_dark( (int)this_object()->query_light() );
}
return ::short( dark );
} /* pretty_short() */
/**
* This method calculates the co-ordinates of this room. The co-ordinates
* are based on the surrounding rooms co-ordinates, if one of those rooms
* are loaded.
* @see query_co_ord()
* @see modify_exit()
*/
void calc_co_ord() {
int i, j, k, shift, *delta, *other_co_ord;
string other;
for ( i = sizeof( dest_other ) - 2; ( i > -1 ) && !co_ord; i -= 2 ) {
/* if destination isn't loaded, skip it */
other = dest_other[ i + 1 ][ ROOM_DEST ];
if ( !find_object( other ) ) {
continue;
}
/* if destination has no coordinates, skip it */
other_co_ord = (int *)other->query_co_ord();
if ( !other_co_ord ) {
continue;
}
/* if exit had a delta defined, use that */
j = -1;
if ( delta = dest_other[ i + 1 ][ ROOM_DELTA ] ) {
co_ord = copy( other_co_ord );
if (pointerp(delta)) {
k = 3;
while ( k-- ) {
/*
* it's -= to make delta here the offset from this
* room to the destination
*/
co_ord[ k ] -= delta[ k ];
}
continue;
} else {
j = member_array(delta, STD_ORDERS);
}
}
/* if exit isn't a direction, skip it */
if (j == -1) {
j = member_array( dest_other[ i ], STD_ORDERS );
if ( j == -1 ) {
continue;
}
}
co_ord = copy( other_co_ord );
delta = query_room_size_array() + (int *)other->query_room_size_array();
for ( k = 0; k < 3; k++ ) {
co_ord[ k ] += STD_ORDERS[ j + 1 ][ k ] *
( delta[ k ] + delta[ k + 3 ] );
}
if ( ( j < 16 ) && dest_other[ i + 1 ][ ROOM_GRADE ] ) {
/* one of the lateral directions */
switch ( j ) {
case 0 .. 1 :
shift = delta[ 0 ] + delta[ 3 ];
break;
case 2 .. 3 :
shift = delta[ 1 ] + delta[ 4 ];
break;
default :
shift = delta[ 0 ] + delta[ 1 ] + delta[ 3 ] + delta[ 4 ];
}
co_ord[ 2 ] -= ( dest_other[ i + 1 ][ ROOM_GRADE ] * shift ) / 100;
}
co_ord_calculated = 1;
}
} /* calc_co_ord() */
/**
* This method calculates all the exit strings to be used for this room.
*/
void calc_exits() {
int i, j;
string exit, word, *tmp_al;
exits = ({ });
for ( i = sizeof( dest_other ) - 2; i > -1; i -= 2 ) {
exit = dest_other[ i ];
if ( member_array( exit, exits ) == -1 ) {
exits += ({ exit });
word = SHORTEN[ exit ];
if ( stringp( word ) ) {
exits += ({ word });
}
}
tmp_al = aliases;
j = member_array( exit, tmp_al );
while ( j != -1 ) {
if ( j % 2 ) {
j--;
} else {
word = tmp_al[ j + 1 ];
if ( member_array( word, exits ) == -1 ) {
exits += ({ word });
}
}
tmp_al = delete( tmp_al, j, 2 );
j = member_array( exit, tmp_al );
}
}
} /* calc_exits() */
/** @ignore yes */
void init() {
object ob;
int i;
/* This is some experimental XP stuff for exploration.
*
if(this_player() &&
!this_player()->query_queued_commands() &&
((!last_visited && uptime() > 1800 + random(3600)) ||
(random(time() - last_visited) > 900))) {
this_player()->adjust_xp(random(random(500)), 0);
}
*/
// Has the day/night changed and do we care?
if(is_day != -1 && ((WEATHER_HANDLER->query_day() > 0) != is_day)) {
is_day = (1 - is_day);
// Do longs;
if(variablelongs && strlen(variablelongs[is_day]))
set_long(variablelongs[is_day]);
// Do items.
if(variableitems) {
for(i=0; i<sizeof(variableitems[1-is_day]); i += 2)
remove_item(variableitems[1-is_day][i]);
for(i=0; i<sizeof(variableitems[is_day]); i += 2)
add_item(variableitems[is_day][i], variableitems[is_day][i+1]);
}
// Do chats.
if(variablechats)
room_chat(variablechats[is_day]);
}
if(chatter)
chatter->check_chat();
if(sitchanger)
sitchanger->check_situations();
if(!sizeof(exits))
calc_exits();
if(!pointerp(co_ord))
this_object()->calc_co_ord();
foreach (ob in hidden_objects) {
if ( ob && objectp( ob ) ) {
ob->init();
} else {
hidden_objects -= ({ 0, ob });
}
}
if (userp(this_player())) {
foreach (ob in _use_internal_objects) {
if (ob && objectp(ob)) {
_use_internal_objects->find_inv_match("all", this_player())->init();
} else {
_use_internal_objects -= ({ 0, ob });
}
}
}
if(item)
item->init();
}
/**
* This method returns the set of move zones for this room. This is used
* by npcs to see which rooms they are allowed to move into.
* @see add_zone()
* @see /obj/monster->add_move_zone()
*/
string *query_zones() {
string *zones;
zones = query_property( "room zone" );
if ( !zones ) {
return ({ "nowhere" });
}
return zones + ({ });
} /* query_zones() */
/**
* This method adds a move zone into the current list of movement zones
* for the room. The move zones are used by npcs to see which rooms they
* are allowed to move into.
* @see set_zone()
* @see query_zones()
* @see /obj/monster->add_move_zone()
*/
void add_zone(string zone) {
string *zones;
zones = query_property( "room zone" );
if ( !zones ) {
zones = ({ zone });
} else {
zones += ({ zone });
}
add_property( "room zone", zones );
} /* add_zone() */
/**
* This method adds a move zone into the current list of zones.
* This method is depreciated, add_zone should be used instead.
* @see add_zone()
* @see query_zones()
*/
void set_zone( string zone ) {
add_zone(zone);
} /* set_zone() */
/**
* This method determines if there is an exit in the specified direction.
* @param direc the exit to test for
* @return 1 if it exists, 0 if it does now
*/
int query_exit( string direc ) {
return ( member_array( direc, dest_other ) != -1 );
} /* query_exit() */
/**
* This method adds an exit to the room. The direction is the direction in
* which the exit should go. This is something like "north" or "enter
* gate". The destination field is where the player will go when they
* enter the exit. The type is a set type that sets a whole bunch of
* defaults for the room. The destination can be either a strong
* or an object.
* <p>
* The types are controlled by /obj/handlers/room_handler.c and the current
* types and what this all means are:
* <dl>
* <dd>
* <dl>
* <dt>road
* <dd>Wide road.
* <dt>path
* <dd>Narrower path
* <dt>door
* <dd>And exit with a door. Defaults to closed but not locked.
* <dt>secret
* <dd>A secret door. Defaults to closed but not locked.
* <dt>corridor
* <dd>A corridor (bing).
* <dt>hidden
* <dd>A hidden exit without a door
* </dl>
* </dl>
* <p>
* The room aliases are used to expand things for exits. However they
* don't expand the entire exit name. They expand it in bits. For
* instance, if the exit was "enter live eel", you could
* add_alias("eel", "live eel"); and add_alias("bing", "enter"); to
* get both of the bits of the exit. So "bing eel", "enter eel",
* "bing live eel" etc would work.
* @example
* add_exit("north", PATH + "market2", "road");
* add_alias("eel", "live eel");
* add_exit("enter live eel", PATH + "live_eel", "secret");
* @example
* object fluffy_room;
*
* fluffy_room = clone_object(PATH + "fluffy_room");
* add_exit("north", fluffy_room, "road");
* @see modify_exit()
* @see query_dest_dir()
* @see remove_exit()
* @see /obj/handlers/room_handler
*/
int add_exit( string direc, mixed dest, string type ) {
mixed *stuff;
if ( !dest_other ) dest_other = ({ });
if ( member_array( direc, dest_other ) != -1 ) return 0;
if ( objectp( dest ) )
dest = file_name( dest );
if ( dest[ 0 .. 0 ] != "/" )
dest = "/"+ dest;
stuff = ({ dest }) + (mixed *)ROOM_HANDLER->query_exit_type( type, direc );
dest_other += ({ direc, stuff });
if ( ( stuff = (mixed *)ROOM_HANDLER->query_door_type( type, direc,
dest ) ) ) {
door_control[ direc ] = clone_object( DOOR_OBJECT );
door_control[ direc ]->setup_door( direc, this_object(), dest, stuff,
type);
hidden_objects += ({ door_control[ direc ] });
door_control[ dest ] = direc;
}
if ( find_call_out( "calc_exits" ) == -1 )
call_out( "calc_exits", 1 );
long_exit = 0;
short_exit = 0;
return 1;
} /* add_exit() */
/**
* This method modifies the parameters for the exit. See the docs in
* /doc/new/room/modify_exit for more complete information.
*/
int modify_exit( mixed direc, mixed *data ) {
int i, j, k;
if(pointerp(direc)) {
for(k = 0; k < sizeof(direc); k++) {
modify_exit(direc[k], data);
}
return 0;
}
if ( ( i = member_array( direc, dest_other ) ) == -1 ) {
return 0;
}
for ( j = 0; j < sizeof( data ); j+= 2 ) {
switch ( lower_case( data[ j ] ) ) {
case "message" :
dest_other[ i + 1 ][ ROOM_EXIT ] = data[ j + 1 ];
break;
case "exit mess" :
case "exit_mess" :
dest_other[ i + 1 ][ ROOM_EXIT ] = data[ j + 1 ];
break;
case "move mess" :
dest_other[ i + 1 ][ ROOM_MESS ] = data[ j + 1 ];
break;
case "linker mess" :
dest_other[ i + 1 ][ ROOM_LINK_MESS ] = data[ j + 1 ];
break;
case "obvious" :
dest_other[ i + 1 ][ ROOM_OBV ] = data[ j + 1 ];
if ( !intp( data[ j + 1 ] ) )
add_property( "no exit cache", 1 );
long_exit = 0;
short_exit = 0;
break;
case "function" :
dest_other[ i + 1 ][ ROOM_FUNC ] = data[ j + 1 ];
break;
case "size" :
dest_other[ i + 1 ][ ROOM_SIZE ] = data[ j + 1 ];
break;
case "upgrade" :
dest_other[ i + 1 ][ ROOM_GRADE ] = data[ j + 1 ];
break;
case "downgrade" :
dest_other[ i + 1 ][ ROOM_GRADE ] = -data[ j + 1 ];
break;
case "enter" :
dest_other[ i + 1 ][ ROOM_ENTER ] = data[ j + 1 ];
break;
case "enter mess" :
case "enter_mess" :
if ( sizeof( dest_other[ i + 1 ][ ROOM_ENTER ] ) == 2 )
dest_other[ i + 1 ][ ROOM_ENTER ] = replace( data[ j + 1 ],
"$F", dest_other[ i + 1 ][ ROOM_ENTER ][ 1 ] );
else
dest_other[ i + 1 ][ ROOM_ENTER ] = data[ j + 1 ];
break;
case "dest" :
dest_other[ i + 1 ][ ROOM_DEST ] = data[ j + 1 ];
if ( objectp( door_control[ direc ] ) ) {
door_control[ direc ]->set_dest( data[ j + 1 ] );
door_control[ data[ j + 1 ] ] = direc;
}
break;
case "relative" :
dest_other[ i + 1 ][ ROOM_REL ] = data[ j + 1 ];
break;
case "look" :
dest_other[ i + 1][ ROOM_LOOK ] = data[ j + 1 ];
break;
case "look func" :
dest_other[ i + 1][ ROOM_LOOK_FUNC ] = data[ j + 1 ];
break;
case "no map" :
dest_other[ i + 1][ ROOM_NO_MAP ] = data[ j + 1 ];
break;
case "delta" :
dest_other[ i + 1][ ROOM_DELTA ] = data[ j + 1 ];
break;
case "closed" :
if ( objectp( door_control[ direc ] ) ) {
data[j+1] ? door_control[ direc ]->set_closed() :
door_control[ direc ]->set_open();
}
break;
case "open" :
if ( objectp( door_control[ direc ] ) ) {
data[j+1] ? door_control[ direc ]->set_open() :
door_control[ direc ]->set_closed();
}
break;
case "transparent" :
if ( objectp( door_control[ direc ] ) ) {
data[j+1] ? door_control[ direc ]->set_transparent() :
door_control[ direc ]->reset_transparent();
}
break;
case "stuck" :
if ( objectp( door_control[ direc ] ) ) {
door_control[ direc ]->set_stuck( data[ j + 1 ] );
}
break;
case "locked" :
if ( objectp( door_control[ direc ] ) ) {
if(data[j+1]) {
door_control[ direc ]->set_closed();
door_control[ direc ]->set_locked();
} else
door_control[ direc ]->set_unlocked();
}
break;
case "unlocked" :
if ( objectp( door_control[ direc ] ) ) {
data[j+1] ? door_control[ direc ]->set_unlocked() :
door_control[ direc ]->set_locked();
}
break;
case "autolock":
if ( objectp( door_control [direc ] ) ) {
door_control[ direc ]->set_autolock( data[ j + 1 ] );
}
break;
case "key" :
if ( objectp( door_control[ direc ] ) ) {
door_control[ direc ]->set_key( data[ j + 1 ] );
}
break;
case "other" :
if ( objectp( door_control[ direc ] ) ) {
door_control[ direc ]->set_other_id( data[ j + 1 ] );
}
break;
case "difficulty" :
if ( objectp( door_control[ direc ] ) ) {
door_control[ direc ]->set_difficulty( data[ j + 1 ] );
}
break;
case "door long" :
if ( objectp( door_control[ direc ] ) ) {
door_control[ direc ]->set_long( data[ j + 1 ] );
}
break;
case "open/close func" :
if ( objectp( door_control[ direc ] ) ) {
door_control[direc]->set_open_trap(data[j+1][0], data[j+1][1]);
}
break;
case "lock/unlock func" :
if ( objectp( door_control[ direc ] ) ) {
door_control[direc]->set_lock_trap(data[j+1][0], data[j+1][1]);
}
break;
case "door short" :
if ( objectp( door_control[ direc ] ) ) {
door_control[ direc ]->set_short( data[ j + 1 ] );
}
break;
case "double doors" :
if ( objectp( door_control[ direc ] ) ) {
door_control[ direc ]->set_how_many( data[ j + 1 ] );
}
break;
case "one way" :
if ( objectp( door_control[ direc ] ) ) {
door_control[ direc ]->set_one_way( data[ j + 1 ] );
}
break;
case "secret" :
if ( objectp( door_control[ direc ] ) ) {
door_control[ direc ]->remove_hide_invis( "secret" );
if ( data[ j + 1 ] > 0 ) {
door_control[ direc ]->add_hide_invis( "secret", 0,
data[ j + 1 ], 0 );
}
}
break;
case "undoor" :
if ( objectp( door_control[ direc ] ) ) {
door_control[ direc ]->go_away();
hidden_objects -= ({ door_control[ direc ] });
door_control = m_delete( door_control, direc );
door_control = m_delete( door_control,
dest_other[ i + 1 ][ ROOM_DEST ] );
/* Hope that something takes care of the other side. */
}
break;
}
}
return 1;
} /* modify_exit() */
/**
* This method removes the specified exit from the room.
* @see add_exit()
* @see modify_exit()
*/
int remove_exit( string direc ) {
int i;
if ( !dest_other ) {
dest_other = ({ });
return 0;
}
i = member_array( direc, dest_other );
if ( i == -1 )
return 0;
if ( door_control[ direc ] ) {
door_control[ direc ]->dest_me();
hidden_objects -= ({ door_control[ direc ] });
door_control = m_delete( door_control, direc );
door_control = m_delete( door_control, dest_other[ i + 1 ][ ROOM_DEST ] );
}
dest_other = delete( dest_other, i, 2 );
if ( find_call_out( "calc_exits" ) == -1 )
call_out( "calc_exits", 1 );
long_exit = 0;
short_exit = 0;
return 1;
} /* remove_exit() */
/**
* This method checks to see if the door is open.
* @param direc the direction of the door
* @return -1 on an error, 0 for closed, 1 for open
* @see modify_exit()
*/
int query_door_open( string direc ) {
if ( !objectp( door_control[ direc ] ) ) {
return -1;
}
return (int)door_control[ direc ]->query_open();
} /* query_door_open() */
/**
* This method checks to see if the exit is a relative one.
* @param direc the direction to check
* @see modify_exit()
*/
int query_relative( string direc ) {
int i;
i = member_array( direc, dest_other );
if ( i == -1 ) {
return 0;
}
return dest_other[ i + 1 ][ ROOM_REL ];
} /* query_relative() */
string query_look( string direc ) {
int i;
i = member_array( direc, dest_other );
if ( i == -1 )
return 0;
if ( !dest_other[ i + 1 ] )
return 0;
return (string)evaluate(dest_other[ i + 1 ][ ROOM_LOOK ]);
} /* query_look() */
mixed *query_look_func( string direc ) {
int i;
if ( ( i = member_array( direc, dest_other ) ) == -1 ) return 0;
if( !dest_other[ i + 1 ] ) return 0;
return dest_other[ i + 1 ][ ROOM_LOOK_FUNC ];
}
/**
* This method returns the size of the exit. This is used to check to make
* sure that people can enter it.
* @param direc the direction of the exit to check
* @return the size of the exit
* @see modify_exit()
*/
int query_size( string direc ) {
int i;
if ( ( i = member_array( direc, dest_other ) ) == -1 ) return 0;
if ( stringp( dest_other[ i + 1 ][ ROOM_SIZE ] ) )
return (int)call_other( this_object(), dest_other[ i + 1 ][ ROOM_SIZE ] );
if ( pointerp( dest_other[ i + 1 ][ ROOM_SIZE ] ) )
return (int)call_other( dest_other[ i + 1 ][ ROOM_SIZE ][ 0 ],
dest_other[ i + 1 ][ ROOM_SIZE ][ 1 ] );
return dest_other[ i + 1 ][ ROOM_SIZE ];
} /* query_size() */
/** @ignore yes */
void event_magic( object channel, int amount, object caster ) {
add_enchant( amount / 5 );
} /* event_magic() */
/** @ignore yes */
void event_theft( object command_ob, object thief, object victim,
object *stolen ) {
log_file( "THEFT", "%s: %s stole %s from %s in %s\n",
ctime( time() ), (string)thief->query_short(),
implode( (string *)stolen->query_short(), ", " ),
(string)victim->query_short(), file_name() );
if ( stringp( theft_handler )) {
if( theft_handler != "none" )
theft_handler->handle_theft( this_object(), command_ob, thief,
victim, stolen );
} else
"/obj/handlers/theft_handler"->handle_theft( this_object(), command_ob,
thief, victim, stolen );
} /* event_theft() */
/** @ignore yes */
void event_exit(object ob, string message, object to) {
if(interactive(ob))
last_visited = time();
} /* event_exit() */
/** @ignore yes */
int query_last_visited() { return last_visited; }
/**
* This method adds an item description to a room. This allows you to
* set up objects which do not as such exist, but can be looked at for
* instance. There should be a lot of these in rooms. The name of
* the item can be multiple word, and the plural for it is
* automagicaly added, unless the no_plural flag is set. If the name
* is an array all of the elements in the array respond to the
* description.
* <p>
* If the desc is set to an array, you can use this for handling
* things like read messages and so on too. Every second element in
* the array is the description/text to be printed and the other
* element is the command upon which the text should be printed. The
* special command 'long' is used to set the long description.
* <p>
* The special type 'position' is used to allow people to use that
* item to do positions on, like stand, sit, lie etc.
* <p>
* This method also allows you to setup add_command patterns. If the
* name after the verb is just a string, then the string will be printed
* when that verb is used. If it is just a function pointer then the
* function will be evaluated and the return result printed. If it
* is an array, then the first element must be a function pointer and
* the second optional element is the pattern to use for that method.
* Multiple patterns and functions may be specified.
* @param shorts the short description of the item
* @param desc the description of the item
* @param no_plural do not automaticaly add a plural for the item
* @return 1 if successfully added, 0 if not
* @example
* add_item("green pot plant", "It is a nasty green pot plant lurking by "
* "the door.\n");
* @example
* add_item(({ "telephone", "red phone" }),
* "Sitting in the corner is the red phone, it is staring unhappily "
* "into space thinking of cupcakes and better times.\n");
* @example
* add_item("small book", ({ "long", "A small red book with dots on the
* "cover.\n",
* "read", "It says 'Rabbit!' in big letters.\n" }) );
* @example
* add_item("green leather couch",
* ({ "long", "The green leather couch is wonderful , so comfy! "
* " So... Comfy!\n"m
* "position", "the green leather couch" }) );
* @example
* add_item("rotating hologram", (: query_current_hologram_string() :));
* @example
* add_item("glue stick",
* ({ "long", "The glue stick looks sticky, like you could slime "
* "something with it.\n",
* "slime", ({ (: do_slime :),
* "<indirect:living> with <direct:object>" }) }) );
* @see query_item()
* @see remove_item()
* @see modify_item()
*/
varargs int add_item( mixed shorts, mixed desc, int no_plural ) {
if (!desc) {
// A 0 description is a definate no no.
printf("Error! In %O add_item(%O, 0), not added.\n", file_name(),
shorts);
return 0;
}
if ( !item ) {
item = clone_object( ITEM_OBJECT );
}
item->setup_item( shorts, desc, no_plural );
return 1;
} /* add_item() */
/**
* This method will attempt to remove the item defined by the given string.
* This will remove everything associated with that item, verbs, patterns,
* everything.
* @param word the name of the item to remove
* @return 1 if successful, 0 on a failure
* @example
* add_item("frog", "Cute, green and sitting on a lilly pad. Yes!\n");
* ...
* remove_item("frog");
* @example
* add_item(({ "big bad chicken", "clucker" }),
* "The big bad chicken sits and stares at you.\n");
* ...
* remove_item("big bad chicken");
* @see add_item()
* @see query_item()
*/
int remove_item( string word ) {
if ( !item ) {
return 1;
}
return (int)item->remove_item( word );
} /* remove_item() */
/**
* This method will modify certain bits of the specified item. This will
* change only the bits of the pattern that are specified. If you wish to
* remove elements a better method would be to remove the item and
* then readd it. The format of the new_desc array is the same as in the
* add_item code.
* @param word the name of the item to change
* @param new_desc the bits of the item to change
* @see remove_item()
* @see add_item()
*/
int modify_item( string word, mixed new_desc ) {
if ( !item ) {
return 0;
}
return (int)item->modify_item( word, new_desc );
} /* modify_item() */
/** @ignore yes */
void add_effect( string eff, mixed arg ) {
effects = clone_object( "/std/shadows/misc/effects" );
effects->setup_shadow( this_object() );
effects->add_effect( eff, arg );
} /* add_effect() */
/**
* This method sets up a linkage between the current room and othert
* rooms. The linkage broadcasts things like says and enter/exit
* messages between the rooms.
* <P>
* The the dynamic preposition is used when someone enters/exits
* the room, the static preposition is used when someone says something
* in the room. The dynamic proposition defaults to "into" and the
* static preposition defaults to "in".
* @param rooms the rooms to link together
* @param d_prep the dynamic preposition
* @param s_prep the static preposition
* @param r_name the name of the room/area
* @example
* set_linker( ({ PATH + "room1", PATH + "room2", }),
* "into", "in", "fluffy square");
*/
varargs int set_linker( string *rooms, string d_prep, string s_prep,
string r_name ) {
if ( linker ) {
return 0;
}
linker = clone_object( LINKER_OBJECT );
linker->setup_shadow( this_object(), rooms, d_prep, s_prep, r_name );
return 1;
} /* set_linker() */
int set_terrain( string terrain_name ) {
if ( terrain ) {
return 0;
}
terrain = clone_object( TERRAIN_OBJECT );
terrain->setup_shadow( this_object(), terrain_name );
set_not_replaceable(1);
return 1;
} /* set_terrain() */
void set_wall( mixed *args ) {
if ( !wall ) {
wall = clone_object( WALL_OBJECT );
wall->setup_shadow( this_object() );
}
wall->set_wall( args );
} /* set_wall() */
/**
* This method sets the default position for the room. Se the set
* default position in the living code for a more complete
* example of this.
* @param pos the default position
* @see /ostd/living/living->set_default_position()
*/
void set_default_position(mixed stuff) {
add_property(DEFAULT_POSITION_PROPERTY, stuff);
} /* set_default_position() */
/**
* This method returns the current default position asigned to this
* room.
* @return the current default position
*/
mixed query_default_position() {
return query_property(DEFAULT_POSITION_PROPERTY);
} /* query_default_position() */
/**
* This method tells us if the passed i nposition is
* allowed in this type of room.
* @param poss the position to check
*/
int is_allowed_position(string poss) {
switch (poss) {
case SITTING :
case STANDING :
case KNEELING :
case LYING :
case MEDITATING :
case CROUCHING :
return 1;
default :
return 0;
}
} /* is_allowed_position() */
/** @ignore yes */
void dest_me() {
int in_armoury, in_void;
object thing, *things;
if ( file_name( this_object() ) == ARMOURY )
in_armoury = 1;
if ( file_name( this_object() ) == ROOM_VOID )
in_void = 1;
/* If this is not the void, remove contents to /room/rubbish for
recycling. Move players to the void */
if ( !in_void ) {
things = all_inventory( this_object() );
foreach( thing in things ) {
if ( userp( thing ) ) {
thing->move_with_look( ROOM_VOID, "$N fall$s into the void." );
continue;
}
if(thing->cleaning_room())
continue;
thing->move("/room/rubbish");
}
}
if ( chatter )
chatter->dest_me();
if ( sitchanger )
sitchanger->dest_me();
if ( effects )
effects->destruct_shadow( effects );
if ( linker )
linker->destruct_shadow( linker );
if ( terrain )
terrain->destruct_shadow( terrain );
if(wall)
wall->destruct_shadow(wall);
if ( item )
item->dest_me();
if(door_control)
foreach(thing in keys(door_control))
if(objectp(thing))
catch(thing->dest_me());
if ( sizeof( hidden_objects ) )
foreach( thing in hidden_objects ) {
// Don't dest thing if it's hidden in multiple rooms
if ( objectp( thing ) && ( thing->multiple_hidden() == 0 ) )
catch( thing->dest_me() );
}
destruct( this_object() );
} /* dest_me() */
/**
* This method sets the flag that enables or disables the room being
* cleaned up. If they flag is set to 1, then room is never cleaned up.
* @param flag the room being cleaned up flag
* @see query_keep_room_loaded()
*/
void set_keep_room_loaded(int flag) {
add_property(ROOM_KEEP_PROP, flag);
} /* set_keep_room_loaded() */
/**
* This method returns the status of the keep room loaded flag. If they
* flag is non-0 then the room with not be unloaded.
* @return the status of the keep room loaded flag
*/
int query_keep_room_loaded() {
return query_property(ROOM_KEEP_PROP);
} /* query_keep_room_loaded() */
/** @ignore yes */
int clean_up( int parent ) {
if (parent) {
return 0;
}
if (query_keep_room_loaded()) {
return 0;
}
call_out("real_clean", 30 + random(120));
return 1;
} /* clean_up() */
/** @ignore yes */
int real_clean() {
object thing;
/*
* Don't clean up the room if:
* there's a player in it, there's a unique npc in it and it's been visited
* by a player in the last hour, it's a slave room or there's a corpse in
* it.
*/
foreach ( thing in all_inventory( this_object() ) ) {
if ( thing->query_property( "player" ) ||
(thing->query_property( "unique" ) &&
last_visited > time() - 3600) ||
thing->query_slave() ||
thing->query_name() == "corpse") {
return 0;
}
}
dest_me();
return 1;
} /* real_clean() */
/**
* This method returns all the matchable objects in the room. This is used
* by find_match to determine the group of objects to select from.
* @param words the words to match on
* @param looker the person doing the pmacthing
* @return the array of objects to match on
*/
object *find_inv_match( string words, object looker ) {
object *things;
things = all_inventory( this_object() );
if ( pointerp( hidden_objects ) ) {
things += hidden_objects;
}
//
// Only do these extra checks for players.
//
if (looker && userp(looker)) {
/* Chekced to this filter thing to speed it up. */
things = filter(things, (: $1 && $1->short(0) &&
(!$2 || $1->query_visible($2)) :), looker);
}
if ( item ) {
things += ({ item });
}
return things;
} /* find_inv_match() */
/**
* This method adds a sign into the room. Any of these elements can
* be set to 0, except the long description.
* @param sign_long the long description of the sign
* @param sign_read_mess the readable message on the sign
* @param sign_short the short description of the sign
* @param sign_name the name of the sign
* @param sign_language the language the sign is written in
* @return the object for the sign
*/
varargs object add_sign( string sign_long, mixed sign_read_mess, string
sign_short, mixed sign_name, string sign_language ) {
object sign;
string* bits;
sign = clone_object( "/std/object" );
if ( !sign_name ) {
sign_name = "sign";
}
if (pointerp(sign_name)) {
bits = explode(sign_name[0], " ");
sign->set_name( bits[<1] );
sign->add_adjective( bits[0..<2]);
sign->add_alias(sign_name[1..]);
} else {
bits = explode(sign_name, " ");
sign->set_name( bits[<1] );
sign->add_adjective( bits[0..<2]);
}
sign->set_long( sign_long );
if ( !sign_language ) sign_language = "common";
sign->set_read_mess( sign_read_mess, sign_language );
sign->reset_get();
if ( sign_short && ( sign_short != "" ) ) {
sign->set_short( sign_short );
sign->set_main_plural( pluralize( sign_short ) );
sign->move( this_object() );
sign->add_property("there", "here");
} else hidden_objects += ({ sign });
return sign;
} /* add_sign() */
/** @ignore yes */
void tell_door( string direc, string message, object thing ) {
if ( objectp( door_control[ direc ] ) )
door_control[ direc ]->tell_door( message, thing );
} /* tell_door() */
/** @ignore yes */
varargs mixed call_door( string direc, string func, mixed arg1, mixed arg2,
mixed arg3 ) {
if ( objectp( door_control[ direc ] ) )
return (mixed)call_other( door_control[ direc ], func, arg1, arg2,
arg3 );
} /* call_door() */
/**
* This method determines if the specified exit is a door or not.
* @param dest the destination to check for being a door
* @see query_exit()
* @see add_exit()
*/
string query_door( mixed dest ) {
int i;
string direc;
mixed bing;
if ( objectp( dest ) ) {
dest = file_name( dest );
}
if ( !stringp( dest ) ) {
return 0;
}
if ( bing = door_control[ dest ] ) {
if ( !objectp( bing ) ) {
direc = bing;
}
}
if ( !direc ) {
return 0;
}
bing = door_control[ direc ];
if ( objectp( bing ) ) {
return direc;
}
bing = clone_object( DOOR_OBJECT );
i = member_array( direc, dest_other );
bing->setup_door( direc, this_object(), dest, dest_other[ i + 1 ] );
hidden_objects += ({ bing });
door_control[ direc ] = bing;
return direc;
} /* query_door() */
/**
* This method stops all the room chats for the room. It also removes
* all the room chats, so if you want to have any more you must
* add them again.
* @see room_chat()
*/
void stop_room_chats() {
if ( chatter ) {
chatter->dest_me();
}
} /* stop_room_chats() */
/**
* Allows the chat interval to be changed.
* @param min minimum interval between chats (seconds)
* @param max maximum interval between chats (seconds)
*/
void set_chat_min_max( int min,int max ) {
if (chatter) chatter->set_chat_min_max(min,max);
}
/**
* Adds more chats to the existing set of room chats
* managed by this chatter object.
* @param new_chats an array of new chat strings
* @see remove_room_chats
* @see query_room_chats
* @see /std/room/basic/chatter
*/
void add_room_chats( string *new_chats ) {
if (chatter) chatter->add_room_chats( new_chats );
} /* add_room_chats() */
/**
* Removes chats from the set of room chats
* managed by this chatter object. If there are no chats
* left the chatter is destructed.
* @param dead_chats an array of chat strings to remove
* @see add_room_chats
* @see query_room_chats
* @see /std/room/basic/chatter
*/
void remove_room_chats( string *dead_chats ) {
if (chatter) chatter->remove_room_chats( dead_chats );
} /* remove_room_chats() */
/**
* Returns the set of room chats
* managed by the chatter object.
* @return pointer to the mixed array of chat args
* @example
* ({ 120, 240, ({ "A frog gimbles the curtains.",
* "A truly revolting smell drifts insidiously "
* "from the rug." }) })
* @see add_room_chats
* @see remove_room_chats
* @see room_chat
* @see /std/room/basic/chatter
*/
mixed *query_room_chats() {
if (chatter) return chatter->query_room_chats();
return 0;
} /* query_room_chats() */
/**
* This method sets up the room chats.
* Room chats are strings which are printed at (semi) random intervals
* in rooms. They are used to add atmosphere to a room. A chat will
* be picked at random from the array of chats with a frequency
* controlled by the times min and max. ie. one will be picked every n
* seconds where is varies between min and max seconds. Please don't
* make the values for min and max too small or the messages just
* become annoying!
* <p>
* The argument to the room_chat method is an array of the format:<br>
* ({ int min, int max, ({ string *chats }) }). In place of a chat
* string you may use "#function_name" where function_name is a
* function that exists on the room object.
* <p>
* Repeated calls to this function overwrite the chats for
* the default chatter.
* @param args the room chat arguments
* @param chatobj chatter object in case the default offends you.
* This argument may be omitted in which case you get
* /std/room/basic/chatter.c
* @example
* room_chat(({ 120, 240, ({ "A string frog wanders past.",
* "#make_soggy_bread",
* "A trully revolting smell drifts insidiously "
* "from the bakery." }) }) );
* @see stop_room_chat()
* @see add_room_chats()
* @see remove_room_chats()
* @see set_chat_min_max()
*/
varargs void room_chat( mixed *args, object chatobj ) {
if ( !pointerp( args[ 2 ] ) ) {
write( "Error: second argument of room_chat args is not an array.\n" );
return;
}
if ( chatter ) {
chatter->setup_chatter( this_object(), args );
return;
}
if (objectp(chatobj)) chatter = chatobj;
else chatter = clone_object( CHATTER_OBJECT );
chatter->setup_chatter( this_object(), args );
} /* room_chat() */
/**
* Set a situation changer (in place of the default).
* If there is no argument you get the default:
* /std/room/basic/situation_changer.
* You call this before any other situation related functions.
* If you create your own changer it should inherit one of
* /std/room/basic/situation_changer or
* /std/room/basic/multiroom_situation_changer or
* otherwsie provide the functionality of those objects.
* @param changer optional parameter specifying either a path for
* the changer object or an existing object to use.
* @see add_situation
* @see start_situation
* @see automate_situation
* @see change_situation
* @example
* If you have a special changer object used for more than one room
* then in setup for those rooms you should have:
* set_situation_changer(load_object("/w/me/mychanger"));
* Where /w/me/mychanger inherits
* /std/room/basic/multiroom_situation_changer
*/
varargs object set_situation_changer( mixed changer ) {
if (stringp(changer)) {
sitchanger = clone_object( changer );
} else if (objectp(changer)) {
sitchanger = changer;
} else {
sitchanger = clone_object( SITUATION_CHANGER_OBJECT );
}
return (sitchanger = sitchanger->set_room( this_object() ));
} /* set_situation_changer() */
/**
* Adds a situation to the room. These situations can be
* invoked manually with start_situation or automatically via
* automate_situation.
* @param label string or number labelling the situation
* @param sit a structure (class) containing all the bits
* of the situation you want to add. It should be a
* variable of class situation. You should include
* situations.h where this class is defined.
* Every part is optional.
* eg.
* start_func function to be called at start of situation
* that might be used to load NPC's or anything
* beyond a message.
*
* The start function is passed the label,
* a do_start_mess flag and the room object.
* If the flag is 1 the situation is starting
* rather than being reloaded. Thus if
* do_start_mess is 0 then you should avoid
* any obvious start messages and make it look
* like the situation is already underway.
*
* end_func function to be called an the end of a situation.
* The end function is only
* passed the label and the room object.
*
* start_mess message told to the room at start of situation
*
* end_mess message told to the room at end of situation
*
* extra_look extra look string appended to rooms long
* during the situation
* chat_rate an array of 2 numbers giving the minimum and
* maximum delay between chats. If this is set
* then the chats are not merged with the
* existing chats but added independently with
* their own chat rates as given.
*
* chats an array of chat strings to be active
* during the situation
*
* add_items a mixed array of ({ item, item description })
* pairs to be active during the situation
*
* random_words sets of words to insert into text to replace
* the special character #n where n is a number.
* The form of the array is ({ #1array, #2array, ... })
* where #1array = ({ "#1word1","#1word2",... }) etc.
* For the duration of the situation one of the strings
* in #1array is used to replace all instances of #1
* in the extra_look, start_mess, end_mess, chats
* and key and the long description part of the add_items.
* In a situation compounded of many situations
* the same random seed is used for choosing all #1's
* for each individual situation for the duration,
* and a different seed for all #2's etc.
*
* @see start_situation
* @see end_situation
* @see automate_situation
* @see change_situation
* @see add_item
* @see room_chat
* @see add_extra_look
* @see set_situation_changer
* @see make_situation_seed
* @see /include/situation.h
* @see goto learning search situation for working examples
* @example
* #include <situations.h>
*
* class situation frogs;
* frogs = new(class situation,
* start_mess: "Water seeps out of the ground to form puddles.",
* extra_look: "There are large puddles on the ground here.",
* chat_rate: ({ 120,180 }),
* chats: ({"A hidden frog croaks quietly.",
* "There is a blooping sound." }),
* add_items:({ ({"puddle", "The puddles are dark and murky. "
* "They will probably dry up given time." }) }) );
* add_situation( "frogs", frogs );
*
* @example
* add_situation( "ship", new(class situation,
* start_mess: "A #1 ship hoves into view.",
* extra_look: "There is a #1 ship forging up the river.",
* chats: ({"The #1 ship's sails flap in the breeze.",
* "Shouts of sailors carry over to you from the #1 ship." }),
* add_items: ({ ({"ship", "The #1 ship, the \"#2\" is a small "
* "sailing vessel that transports cargo up and "
* "down the river."}) }),
* random_words: ({ ({ "old","waterlogged","heavily laden" }),
* ({ "Jemima", "Old Sea Dog", "Randy Mermaid" }) })
* ) );
* When the situation is started a random choice (eg. "old") replaces #1
* and a name (eg. "Jemima") replaces #2 in the text strings for
* the duration.
*
* @example
* add_situation( "frogs", new(class situation,
* start_mess: "Water seeps out of the ground to form puddles.",
* extra_look: "There are large puddles on the ground here.",
* chats: ({"A hidden frog croaks quietly.",
* "There is a blooping sound." }),
* add_items: ({ ({"puddle", "The puddles are dark and murky. "
* "They will probably dry up given time." }) }) ));
* @example
* This is an empty situation, useful for making pauses in the action.
* add_situation( "pause", new(class situation) );
*/
void add_situation( mixed label, class situation sit ) {
if (!sitchanger) {
sitchanger = clone_object( SITUATION_CHANGER_OBJECT );
sitchanger->set_room( this_object() );
}
sitchanger->add_situation( label, sit );
} /* add_situation() */
/**
* Makes a seed value for the random part of when
* situations turn on and off. The two ints should be
* constant for a given room -- eg. the coordinates.
* If this function is not called the seed is
* generated from the file_name of the object.
* For this reason, rooms that are clones will produce
* erratic results unless this function is called.
* @param xval integer to use to make a seed (eg. x coordinate)
* @param yval integer to use to make a seed (eg. y coordinate)
*/
void make_situation_seed(int xval, int yval) {
if (sitchanger) sitchanger->set_seed(xval,yval);
} /* make_situation_seed() */
/**
* Starts a situation previously added to the room. These situations can be
* invoked manually with start_situation or automatically via
* automate_situation. The call is passed to the situation
* changer object. If there isn't one nothing happens.
* @param label label for the situation as passed to add_situation
* @param do_start_mess 0 to supress the start_mess string
* This is to fake it that a situation has been
* going for a while when really you just loaded it.
* @see add_situation
* @see end_situation
* @see automate_situation
* @see change_situation
*/
void start_situation(int label, int do_start_mess) {
if (sitchanger)
sitchanger->start_situation( label, do_start_mess );
} /* start_situation */
/**
* Ends a situation previously added and started on the room.
* These situations can be invoked manually with start_situation
* or automatically via automate_situation.
* @param label label for the situation
* @see add_situation
* @see start_situation
* @see automate_situation
* @see change_situation
*/
void end_situation(mixed label) {
if (sitchanger) sitchanger->end_situation( label );
} /* end_situation() */
/**
* Starts one or more situations that will end after a
* specified duration. You can use an array and make
* further situations commence when others end.
* @param label (mixed) label of the situation to start up.
* If you pass an array such as ({ "frog1", "frog2" }) for the
* label then that set of situations are started one at
* a time and the total duration is split evenly between them.
* Label is usually an integer or a string or an array of
* integers and/or strings.
* If the string is a list of labels
* separated by , then multiple situations
* are started using those labels.
* @param duration (int) total time (seconds) the overall situation
* should last. You can put an array of durations -- one for each
* situation if the label lists more than one situation and then
* the overall time is the sum of the numbers.
* -1 is a special duration. It means that the labelled situation goes on
* forever (and thus nothing after it in the array will ever go).
* @param words is a list of replacements for #n in the text OR
* a random number seed to use to choose words from random_words.
* eg. ({ "#1", "frog", "#2", "honey" }) or 22
*
* @return handle useful for halting the changes later. It is typically
* a small positive integer.
* @see shutdown_situation
* @see add_situation
* @see automate_situation
* @see goto learning search situation for working examples
* @example
* handle=change_situation( ({ "sew1","sew2" }), ({ 60,60 }) );
*
* if (over) shutdown_situation( handle, ({ "sew1","sew2" }) );
* @example
* change_situation( ({ "background,sew1","background,sew2" }), 120 );
* @example
* change_situation( ({ "building_falling","rubble" }), ({ 120, -1 }) );
* @example
* add_situation( "boat1",
* new(class situation,
* start_mess: "A little fishing boat comes up to the pier. "
* "It has the name \"#1\" painted on the side.\n"
* "A fisherman jumps off the boat and ties its painter to a post.",
* chats: ({"The little boat bobs up and down next to the pier.",
* "A fisherman hops off the little boat." }),
* chat_rate: ({ 20, 30 }),
* extra_look: "There is a little boat here.",
* add_items: ({ ({ ({ "#1","little boat" }),
* "There is little fishing boat tied up at the pier here. "
* "The name \"#1\" is painted on the side." }) }),
* end_mess: "The fishermen get back on board and "
* "the little boat moves on.",
* random_words: ({ ({ "Honey Toad", "Piker", "Bing" }) })
* )
* );
*
* change_situation( "boat1", 120, ({ "#1","Floating Egg" }) );
*/
varargs mixed change_situation( mixed label, mixed duration, mixed words ) {
if (sitchanger)
return sitchanger->change_situation(label,duration,words,0);
return 0;
} /* change_situation() */
/**
* Automate starting and ending of a situation.
* These situations can be invoked manually with start_situation.
* The automated starting and ending is unaffected by the room
* unloading. When the room reloads the situation will be
* restarted unless its duration is up.
* You must include the file situations.h for the definitions
* of the when masks. The random seed needs to be set
* consistently for the situations. This is probably ok
* unless your rooms are clones (see make_situation_seed).
* @param label (mixed) label of the situation to start
* up. If you pass an array such as ({ "frog1", "frog2" }) for the
* label then that set of situations are started one at
* a time and the total duration is split evenly between them.
* Label is usually an integer or a string or an array of
* integers and/or strings.
* If the string is a list of labels
* separated by , then multiple situations
* are started using those labels.
* @param duration (int) total time (seconds) the overall situation
* should last. You can put an array of durations -- one for each
* situation if the label lists more than one situation and then
* the overall time is the sum of the numbers.
* -1 is a special duration. It means that the situaton given that
* duration is not part of the set but a special background or
* default situation that occurs all the time except when
* this automated situation is going.
* @param when (int) a time of the day mask. This limits when
* the situation is allowed to occur. The mask is composed of
* the allowed hours in AM time ( 24 hours clock, (1<<hour) and
* combined with | (OR) ). You can just use these
* predefined masks and ignore how it works:
* WHEN_WEE_HOURS, WHEN_EARLY_MORNING, WHEN_LATE_MORNING, WHEN_AFTERNOON
* WHEN_EVENING, WHEN_LATENIGHT, WHEN_MIDDAY, WHEN_MORNING,
* WHEN_EARLY_MORNING, WHEN_LATE_MORNING, WHEN_NIGHT, WHEN_DAY
* WHEN_ANY_TIME
* The masks are defined in /include/situations.h.
* @param chance (int) chance in 1000 of starting the situation
* This is tested every duration seconds.
* @param category (optional) if you specify a cateory for the situation
* then no situations with the same category will overlap.
* category would usually be a string eg. "boats".
* @see add_situation
* @see start_situation
* @see end_situation
* @see situations.h
* @see set_situation_changer
* @see make_situation_seed
* @see goto learning search situation for working examples
* @example
* #include <situations.h>
*
* automate_situation( "frog", 300, WHEN_ANY_TIME, 200 );
*
* This will automatically start the situation labelled "frog"
* at a random time that is any time of the day with a 200/1000
* chance of it starting per 300 seconds. It will last for
* 300 seconds (5 minutes).
* @example
* automate_situation( ({"frog1,pond","frog2,pond"}), 240,
* WHEN_EVENING|WHEN_NIGHT, 300 );
*
* This will automatically start a situation that is a combination
* of "frog1" and "pond" followed by "frog2" and "pond". They start
* at a random time but only in the evening or at night.
* There will be a 300/1000 chance of
* it starting per 240 seconds. Both the "frog1" and "frog2"
* situations will get half the total time (as there are two),
* 120 seconds each, for a total duration of 240 seconds (4 minutes).
* @example
* automate_situation( ({"frog1,pond","frog2,pond"}), ({ 80,160 }),
* WHEN_EVENING|WHEN_NIGHT, 300 );
* Same as previous example except the durations of the individual
* parts are set: "frog1,pond" for 80 seconds followed by "frog2,pond"
* for 160 seconds. The total time is 80+160.
*/
varargs void automate_situation( mixed label, mixed duration, mixed when,
mixed chance, mixed category ) {
if (sitchanger)
sitchanger->automate_situation(label,duration,when,chance,category);
} /* automate_situation() */
/**
* Shuts down all current and pending situations. It also turns off the
* automated situation manager so no more are added. It does not
* destruct this object so all the add_situations are still loaded
* and make be recommenced with automate_situation. dest_me is
* the appropriate call to permanently remove all situations. The
* call is passed to the situation changer object. If none exists
* then nothing happens. The situation changer is created when
* an add_situation call is performed.
* @see add_situation
* @see automate_situation
* @see change_situation
*/
void shutdown_all_situations() {
if (sitchanger) sitchanger->shutdown_all_situations();
} /* shutdown_all_situations() */
/**
* Shuts down a situation or set of situations initiated with
* change_situation based on the call_out handle
* returned by the call to change_situation.
* @param callout call_out handle. If 0 then the last
* known handle is used.
* @param label label or array of labels of situations to clean
* up with end_situation
* @param the_room the room
* @see automate_situation
* @see change_situation
*/
void shutdown_situation(int call, mixed label) {
if (sitchanger) sitchanger->shutdown_situation(call,label);
} /* shutdown_situation() */
/**
* This method checks to see if the program is replaceable.
* @return 1 if the program is not replaceable
* @see set_not_replaceable()
*/
int query_not_replaceable() {
return query_property(ROOM_NOT_REPLACE_PROGRAM_PROP);
} /* query_not_replaceable() */
/**
* This method sets a property to make the program replaceable. A program
* will only be replaced if there is only a setup() function in the room.
* A reset() or a create() will stop the room from being replace and in
* fact any other function existing in there will stop it from being
* replaced as well.
* @pram replace 1 to make the room not replacable
* @see query_not_replaceable()
*/
void set_not_replaceable(int replace) {
add_property(ROOM_NOT_REPLACE_PROGRAM_PROP, replace);
} /* set_not_replaceable() */
/** @ignore yes */
mixed stats() {
int i;
mixed *stuff;
stuff = ({ });
for ( i = sizeof( dest_other ) - 2; i > -1; i -= 2 )
stuff += ({
({ dest_other[ i ], dest_other[ i + 1 ][ ROOM_DEST ] })
});
if ( co_ord )
stuff += ({
({ "co-ord x", co_ord[ 0 ] }),
({ "co-ord y", co_ord[ 1 ] }),
({ "co-ord z", co_ord[ 2 ] })
});
return light::stats() + property::stats() + stuff + ({
({ "short", short( 0 ) }),
({ "enchantment", query_enchant() }),
({ "background enchantment", background_enchant }),
({ "dynamic enchantment", dynamic_enchant }),
({ "enchantment time", enchant_time }),
({ "theft handler", theft_handler }),
});
} /* stats() */
/**
* This method sets the long description to display during the day time.
* @param str the new day long description
* @see query_day_long()
* @see set_night_long()
*/
void set_day_long( string str ) {
if(!variablelongs)
variablelongs = allocate(2);
variablelongs[DAY] = str;
if(is_day == -1)
is_day = (WEATHER_HANDLER->query_day() > 0);
if(is_day == DAY)
set_long(str);
}
/**
* This method sets up the night long for the room. This will be the
* long description displayed at night in the room.
* @param str the new night long description
* @see set_day_long()
* @see query_night_long()
*/
void set_night_long( string str ) {
if(!variablelongs)
variablelongs = allocate(2);
variablelongs[NIGHT] = str;
if(is_day == -1)
is_day = (WEATHER_HANDLER->query_day() > 0);
if(is_day == NIGHT)
set_long(str);
}
/**
* This method returns the long description of the room at night. This is
* used to print out different strings for rooms during the day and during
* the night.
* @return the night long for the room.
*/
string query_night_long() {
if(variablelongs && strlen(variablelongs[NIGHT]))
return variablelongs[NIGHT];
return this_object()->query_long();
} /* query_night_long() */
/* Returns the long part of the description. */
private string return_long(mixed desc) {
int ma;
if(!pointerp(desc))
return (string)desc;
ma = member_array("long", desc);
if(ma < 0)
return "Error: No long found.";
return (string)desc[ma+1];
}
/**
* This method sets up an item which will only be displayed during the
* day. All of the standard add_item things are available with this
* method.
* @see /std/room->add_item()
* @see add_night_item()
*/
varargs int add_day_item(mixed shorts, mixed desc, mixed no_plural) {
string the_item;
if(pointerp(shorts))
the_item = shorts[0];
else
the_item = shorts;
if(!variableitems)
variableitems = ({ ({ }), ({ }) });
variableitems[DAY] += ({ the_item, return_long(desc) });
if(is_day == -1)
is_day = (WEATHER_HANDLER->query_day() > 0);
if(is_day == DAY)
return add_item(shorts, desc, no_plural);
return 1;
}
/**
* This method sets up an item which will only be displayed during the
* night. All of the standard add_item things are available with this
* method.
* @see /std/room->add_item()
* @see add_day_item()
*/
varargs int add_night_item(mixed shorts, mixed desc, mixed no_plural) {
string the_item;
if(pointerp(shorts))
the_item = shorts[0];
else
the_item = shorts;
if(!variableitems)
variableitems = ({ ({ }), ({ }) });
variableitems[NIGHT] += ({ the_item, return_long( desc ) });
if(is_day == -1)
is_day = (WEATHER_HANDLER->query_day() > 0);
if(is_day == NIGHT)
return add_item(shorts, desc, no_plural);
return 1;
}
/**
* This method sets up chats for when the room is in the day cycle.
* @param args the chatter arguements
* @see room_night_chat()
* @see /std/room->room_chat()
*/
void room_day_chat(mixed *args) {
if(!variablechats)
variablechats = allocate(2);
variablechats[DAY] = args;
if(is_day == -1)
is_day = (WEATHER_HANDLER->query_day() > 0);
if(is_day == DAY)
room_chat(args);
}
/**
* This method sets up chats for when the room is in the night cycle.
* @param args the chatter arguements
* @see room_day_chat()
* @see /std/room->room_chat()
*/
void room_night_chat( mixed *args ) {
if(!variablechats)
variablechats = allocate(2);
variablechats[NIGHT] = args;
if(is_day == -1)
is_day = (WEATHER_HANDLER->query_day() > 0);
if(is_day == NIGHT)
room_chat(args);
}
/** @ignore yes */
string query_help_file_directory() {
return ROOM_HELP_FILE_DIR;
} /* query_help_file_directory() */