1818 lines (1640 with data), 45.4 kB
/*
* Shattered World's Standard Room Template.
*
* This should be used by inheriting from the appropriate room.
* ie. inherit "room/room";
*
* See /doc/example/simple_room.c for an example of how to use this file.
*
* Modified from the original proposal at Genesis by
* rendell@bruce.cs.monash.oz.au
* geoff@bruce.cs.monash.oz.au
* mmcg@bruce.cs.monash.oz.au
*/
/*
* History:
* 950219 Hunter Cleaned up code and comments
* 950221 Hunter Added support for multiple id's for items()
* 950225 Hunter Added OLDCODE trap for 'dest_dir = .. '
* 951121 Hunter shifted 'medium' code to RO/base/medium.c
* 960901 Belgarion Added "static" to several global variables that
* don't really need to save; changed show_stats()
* to _not_ generate a massive array through addition.
* 981211 Jenna Added short() to long desc, at player request
* Also added hilite capability to short().
* 000429 Dredd Added in item persistence.
* 000514 Jenna Overhauled doors
* 010822 Jenna Persistent rooms (eg shops) now save on player exit()
*/
inherit "RO/base_object";
inherit "RO/base/medium";
#include <Objects.h>
//#define PERSISTENT
/* weather stuff */
const CLOUD=0;
const PRECIPITATION=1;
const HUMIDITY=2;
const WIND=3;
const TEMPERATURE=4;
const NIGHT=5;
string short_desc; /* Short description of the room */
string long_desc; /* Long description of the room */
/*
* Special items in the room. "table", "A nice table", "window", "A window"
*/
array items;
/*
* An array with destinations and directions: "room/church", "north" ...
*
* Made static by Jenna May 2000
*/
static array dest_dir;
/*
* Doorways: added by Jenna May 2000
* A paired array of directions and door objects (or direction and 0
* to indicate an empty doorway).
*/
static array doorways;
#define JEN(x) catch tell_room(load_object("players/jenna/door/bug_room"), "[room:"+file_name(this_object())+"] "+x+"\n");
#define DOOR "obj/door"
static int light; /* light value of this location */
/*
* outdoorness defines how 'outdoors' a location is for weather purposes:
* 0: no effect from weather
* 1: indoors, can hear weather effects on the roof etc
* 2: indoors, but can see outdoors
* 3: outdoors, under cover
* 4: completely exposed
*/
int outdoorness;
const w_none = 0;
const w_inside = 1;
const w_enclosed = 2;
const w_covered = 3;
const w_outdoors = 4;
/*
* int set_dest_dir_flag is a hack to find all the code that
* directly assigns to dest_dir : Hunter
*
* Static because most saved rooms reset their dest_dir on more than
* one occasion - frequently after restoring from the save file.
* - Belgarion.
*/
static int set_dest_dir_flag;
int last_night;
static object weather_obj;
/*
* This is the area controller, if any. It should be set before the
* dest_dir is set; then, when dest dir is set, this object won't add itself
* to the mapknower (it expects the room controller to do all that for it).
*/
static (int|string|object) area_controller;
static int last_init_time = time();
/*
* call_out_functions is an array of function names that are called
* periodically, with 2 args: a state arg and the player. The function
* can return 0 (to halt the call_out sequence) or an array, the first
* element specifying the time value of the next call_out, the second
* being the new state variable.
*/
static array call_out_functions;
/*
* foot prints varibles see footprints doc and end of file for a description
* of the variables.
*/
static int prints_status;
static int prints;
static string prints_desc_long;
static string prints_desc_short;
static int prints_time;
/* debug info (added by Hunter of Shattered Worlds) */
array
show_stats()
{
int count;
array ret, tmp;
int pos;
/* Force these global variables into sane states. */
if (!pointerp(dest_dir)) dest_dir = ({ });
if (!pointerp(items)) items = ({ });
if (!pointerp(call_out_functions)) call_out_functions = ({ });
if (!arrayp(doorways)) doorways = [];
ret = "RO/base/medium"::show_stats();
tmp = "RO/base_object"::show_stats();
if (tmp) ret += tmp;
ret += allocate(sizeof(dest_dir) / 2 + sizeof(items) / 2 + 8 +
sizeof(call_out_functions) + sizeof(doorways) / 2);
pos = index(ret, 0);
ret[pos++] = "short desc: "+short_desc;
ret[pos++] = "dest_dir: "+(sizeof(dest_dir)?"":"<none>");
if (sizeof(dest_dir))
for (count = 0; count < sizeof(dest_dir); count += 2)
{
if (pointerp(dest_dir[count]))
ret[pos++] = "x,y,z: "+dest_dir[count][0]+","
+dest_dir[count][1] +","+dest_dir[count][2]+", "
+dest_dir[count+1];
else if (objectp(dest_dir[count]))
ret[pos++] = " ob: " +dest_dir[count]->short()+ ", "
+dest_dir[count+1];
else
ret[pos++] = " "+dest_dir[count]+", "+dest_dir[count+1];
}
ret[pos++] = "doorways: "+(sizeof(doorways)?"":"<none>");
if (sizeof(doorways))
for (count = 0; count < sizeof(doorways); count += 2)
{
ret[pos] = " "+doorways[count]+": ";
if (arrayp(doorways[count+1]))
ret[pos] += doorways[count+1][1][0];
else if (objectp(doorways[count+1]))
ret[pos] += doorways[count+1]->query_short();
else if (!doorways[count+1])
ret[pos] += "<empty>";
pos++;
}
if (sizeof(items))
{
ret[pos++] = "items:";
for (count = 0; count < sizeof(items); count += 2)
ret[pos++] = " \""+(stringp(items[count])?items[count]
:(implode(items[count], "\" \"")))
+"\", \""+items[count+1]+"\"";
}
ret[pos++] = "outdoorness: "+outdoorness+"\t"
+"light: "+query_light()+"\t"
/* write("in game light: "+query_glight()+"\t"); */
+"last_night: "+last_night;
ret[pos++] = "weather_obj: "+(objectp(weather_obj)?file_name(weather_obj)
:"<none>");
ret[pos++] = "area controller: "+
(objectp(area_controller)?file_name(area_controller):
stringp(area_controller)?area_controller:
MAPKNOWER->query_room_known(file_name(this_object()))? "<MapKnower>" :
"<none>");
if (sizeof(call_out_functions))
{
ret[pos++] = "call_out_functions:";
for (count = 0; count < sizeof(call_out_functions); count++)
ret[pos++] = " "+call_out_functions[count];
}
#if 0
write("Map info: ");
MAPKNOWER->show_room(file_name(this_object()));
#endif
/* compact the array */
ret = ret[..(pos - 1)];
return ret;
}
init()
{
int i,sz;
object item;
object wet;
array DestDir = this_object()->query_dest_dir();
last_init_time = time(); /* auto-decay on room boxes */
/* ported from ~rhys/room/sea_box - hunter 931210 */
if (this_object()->query_medium("water"))
{
if (wet = present("wet_obj", this_player()))
wet->reset_wet();
else
{
wet = clone_object(WET_OBJ);
if (transfer(wet, this_player()))
destruct(wet);
}
}
add_light(this_player()->query_light());
this_player()->set_room(this_object());
this_player()->test_breath();
if (weather_obj)
{
int chance;
if (outdoorness >= 4)
add_action("snow_make", "make");
update_weather(-1);
chance = 0;
if (outdoorness == 4 && weather_obj->query_precipitation() >= 2)
chance = 10 - weather_obj->query_precipitation()*2;
if (chance)
if (check_wet(chance, this_player()))
{
call_out("test_func", chance*5,
({"check_wet", chance, this_player()}));
}
}
if (call_out_functions)
{
for (i = 0; i < sizeof(call_out_functions); i++)
call_out("test_func", 1, ({call_out_functions[i], 0, this_player()}));
}
// Jenna 14-Feb-01 - put back this since non-cardinal exits
// stopped working - temp fix while waiting for Dredd
#if 1
/* no dest_dir set */
if (!DestDir) return;
/* check if this_player is present*/
if (environment(this_player()) == this_object())
{
i = 1;
sz = sizeof(DestDir);
while(i < sz)
{
string dirn;
dirn = explode(DestDir[i], "#")[0];
if (!DIRECTION->is_standard(dirn))
{
add_action("redirect_move", dirn);
// LOG->log("DIRECTIONS", file_name(this_object()) + ": " + dirn);
}
i += 2;
}
}
#endif
}
/*
* should propogate upward - adding light to objects it's within
* Hence we can have dark object which don't light the area
* around them - must remember to remove light whenever a
* transfer() is called.
*/
add_light(x)
{
if (!intp(x)) return light;
if (!intp(light)) light = 0;
light = light + x;
if (environment())
environment()->add_light(x);
return light;
}
query_light() { return light; }
query_total_light() { return light; }
set_light(int x)
{
light += x;
return light;
}
id(str)
{
if (query_item_index(str) > -1) return 1;
return ::id(str);
}
query_room() { return 1; }
query_look(Exception)
{
return query_room_look() +
LOOK->query_contents_look(this_object(), Exception);
}
query_room_look()
{
/* this is a backward compatible hack to catch any code using this dirrectly */
LOG->log("OLDCODE", "query_room_look() (room/room) in " + file_name(this_object()) + "\n");
return this_player()->query_soul("generic/perception")->query_room_look();
}
long(str)
{
/* this is a hack to make the code backwards compatible
this should be logged in the future */
LOG->log("OLDCODE", "long() (room/room) in " + file_name(this_object()) + "\n");
return this_player()->query_soul("generic/perception")->room_long(str);
}
describe_exits()
{
int myint, i, x;
object door;
array Exits, EmptyDoorways;
string ret;
int hilite = 0;
if (!doorways)
doorways = [];
if (this_player())
{
myint = this_player()->query_int();
hilite = ANSI->hilite(this_player(), "exits");
}
else
myint = 0;
if (!dest_dir || sizeof(dest_dir) == 0)
ret = "There are no obvious exits.\n";
else
{
if (sizeof(dest_dir) == 2)
ret = "The one obvious exit is ";
else
ret = "There are " +
NUMBERS_SERV->int_to_english(sizeof(dest_dir)/2, myint, 5) +
" obvious exits:\n ";
for (i = 1, Exits = [], EmptyDoorways = []; i < sizeof(dest_dir); i += 2)
{
x = index(doorways, dest_dir[i]);
if (x == -1)
Exits += [ dest_dir[i] ];
else if (!doorways[x+1])
EmptyDoorways += [ dest_dir[i] ];
else if (arrayp(doorways[x+1]))
Exits += [ doorways[x+1][1][0] + " leading " +
dest_dir[i] ];
else if (objectp(doorways[x+1]))
{
string tmp = doorways[x+1]->is_open() ? " (open)" : "";
Exits += [ "a " + doorways[x+1]->describe_strength() +
" door" + tmp + " leading " + dest_dir[i] ];
}
}
if (sizeof(EmptyDoorways))
{
string tmp = "a doorway leading ";
if (sizeof(EmptyDoorways) > 1)
tmp = "doorways leading ";
Exits += [ tmp + STRINGS->array2string(EmptyDoorways) ];
}
ret += STRINGS->array2string(Exits) + ".\n";
}
#ifdef NORTH_MUDLIB
ret = STRINGS->indent( ret, 4, 6, 72 ) + "\n";
#endif
ret = ANSI->scolor(hilite,ret);
return ret;
}
redirect_move(str)
{
object ms = this_player()->query_soul("generic/movement");
string qv = query_verb();
if (!ms) return 0;
if (str)
qv += " " + str;
return ms->move(qv);
}
/*
* Called by obj/souls/movement to see if we can really move. Returns
* the destination if movement is allowed, else sets a notify_fail()
* and returns 0.
*
* Jenna May 2000
*/
move(string direction, (int|string) str, object pob)
{
int i;
string verb, door_name, dest;
object door;
if (!pob)
{
throw("Type error in room/room::move(dir, str, pob) - not sent correct args\n");
pob = this_player();
LOG->log("jenna.move", file_name(this_object()));
}
dest = query_exit(direction);
if (!dest)
{
pob->notify_fail("There is no exit to the "+direction+".\n");
return 0;
}
door = query_door(direction);
if (door && !door->is_open() && pob->query_wiz() < 20)
{
tell_object(pob, "The door is closed!\n");
return 1;
}
return dest;
}
/* this should query perception soul on weather a player can see rather
than the light level */
/* Modified by Jenna for hilites Dec 98 */
short()
{
int hilite = 0;
string ret;
if (this_player())
hilite = ANSI->hilite(this_player(), "roomnames");
if (this_object()->query_light() > 0)
ret = short_desc;
else
ret = "Dark area";
return ANSI->scolor(hilite, ret);
}
search(str)
{
/* Hidden object stuff by Hunter */
array stuff;
int count, found;
if (stuff = contents(this_object()) )
{
found = 0;
for (count = 0; count < sizeof(stuff); count ++)
if (stuff[count]->query_hidden())
found += stuff[count]->set_hidden(0);
if (found)
{
write("You find "+found+" object"+(found ==1?"":"s")+".\n");
return 1;
}
}
notify_fail("You search around for a while, but don't find anything interesting.\n");
return 0;
}
/*
* outdoorness {none, indoors, indoors(windows), under cover, exposed}
*
* cloud cover {clear, light, heavy, overcast}
* precipitation {none, spitting, light, storm, thunder storm}
* humidity {none, lots, fog, mist}
* wind {still, light, breezy, windy, strong wind, gale force}
* temperature {cold, chilly, temperate, warm, hot}
* night {not night, night}
*/
reset(arg)
{
"RO/base_object"::reset(arg);
/* Footprints/tracking stuff */
if (query_prints()) clean_prints();
if (!is_cloned(this_object())) set_prints(1);
weather_obj = load_object("obj/weather");
last_night = 1;
update_weather(-1);
#ifdef PERSISTENT
/* Persistent stuff */
if (!arg)
{
catch W_DATABASE->restore_items(file_name(this_object()), this_object());;
}
#endif
}
query_weather()
{
return weather_obj->query_sky_weather(outdoorness);
}
sky_look(arg)
{
array DestDir = this_object()->query_dest_dir();
if (arg == "footprints" || arg == "prints" || arg == "floor")
{
if (query_prints())
{
write(query_prints_long());
return 1;
}
else
{
write("Your feet don't seem to leave any marks here.\n");
}
}
if (weather_obj && outdoorness > 1)
return weather_obj->sky_query_long(arg, outdoorness);
if (index(DestDir, arg) > -1)
return this_player()->peer(arg);
return 0;
}
set_outdoors(n) { outdoorness = n; }
query_outdoors() { return outdoorness; }
set_weather_obj(n) { weather_obj = n; }
query_weather_obj() { return weather_obj; }
sky_change_weather(weather_type, value)
{
int current;
string thing;
if (outdoorness == 0)
return;
if (weather_type == CLOUD && outdoorness > 1)
{
if (value >= 100)
{
while (value >= 100)
value -= 100;
return appropriate(value, weather_obj->query_cloud(),
"The sky is starting to cloud over.\n",
"The sky is getting more cloudy.\n",
"The sky is starting to clear.\n",
3);
}
else
{
return appropriate(value, weather_obj->query_cloud(),
"The sky is now cloudy.\n",
"The cloud cover gets heavier.\n",
"The sky clears.\n",
"The cloud cover decreases.\n");
}
}
if (weather_type == PRECIPITATION)
{
current = weather_obj->query_temperature();
if (current == 0)
thing = " snow";
else if (current == 1)
thing = " hail";
else
thing = " rain";
if (value >= 100)
{
while (value >= 100)
value -= 100;
if (outdoorness > 1)
{
ud_prints(); /* to see if prints are washed away */
return appropriate(value, weather_obj->query_precipitation(),
"It looks like"+thing+" is on the way.\n",
"The"+thing+" seems to be getting heavier.\n",
"The"+thing+" seems to be getting lighter.\n",
3);
}
}
else
{
if (outdoorness == 1)
{
if (weather_obj->query_temperature() > 0)
return appropriate(value, weather_obj->query_precipitation(),
"A noise starts above your head.\n",
"The noise above your head gets louder.\n",
"The noise above your head stops.\n",
"The noise above your head becomes quieter.\n");
}
else
{
ud_prints(); /* to see if prints are washed away */
return appropriate(value, weather_obj->query_precipitation(),
"It starts to"+thing+".\n",
"The"+thing+" gets heavier.\n",
"The"+thing+" stops.\n",
"The"+thing+" eases up.\n");
}
}
}
if (weather_type == HUMIDITY && outdoorness > 1)
{
current = weather_obj->query_humidity();
if (current == 1 || (current == 0 && value == 1))
thing = " mist ";
else if (current == 2 || (current == 0 && value == 2))
thing = " fog ";
else if (thing == 0) thing = " fog ";
if (value >= 100)
{
while (value >= 100)
value -= 100;
return appropriate(value, weather_obj->query_humidity(),
"A"+thing+"starts to rise.\n",
"The"+thing+"seems to be getting thicker.\n",
"The"+thing+"seems to be getting lighter.\n",
3);
}
else
{
return appropriate(value, weather_obj->query_humidity(),
"A"+thing+"settles on the land.\n",
"The"+thing+"gets thicker.\n",
"The"+thing+"clears.\n",
"The"+thing+"thins somewhat.\n");
}
}
if (weather_type == WIND && outdoorness > 1)
{
if (value >= 100)
{
while (value >= 100)
value -= 100;
ud_prints(); /* to see if prints are washed away */
return appropriate(value, weather_obj->query_wind(),
"The air is starting to move.\n",
"The wind seems to be picking up.\n",
"The wind seems to be easing off.\n",
3);
}
else
{
ud_prints(); /* to see if prints are washed away */
return appropriate(value, weather_obj->query_wind(),
"A wind starts to blow.\n",
"The wind gets stronger.\n",
"The wind stills.\n",
"The wind eases somewhat.\n");
}
}
if (weather_type == TEMPERATURE)
{
if (value >= 100)
{
while (value >= 100)
value -= 100;
if (outdoorness > 1)
return appropriate(value, weather_obj->query_temperature(),
"The air seems to be getting warmer.\n",
1,
"The temperature seems to be dropping.\n",
3);
}
else
{
if (outdoorness == 1)
{
if (value > 0 || (value < 0 && weather_obj->query_precipitation() > 0))
return appropriate(value, weather_obj->query_temperature(),
"A sudden noise begins above your head.\n",
"",
"The noise above your head fades to a whisper, then stops.\n",
"");
}
else
return appropriate(value, weather_obj->query_temperature(),
"The air feels warmer.\n",
1,
"The air feels cooler.\n",
3);
}
}
if (weather_type == NIGHT && outdoorness > 1)
{
if (value >= 100)
{
while (value >= 100)
value -= 100;
return appropriate(value, weather_obj->query_night(),
"The sun edges down past the western horizon.\n",
1,
"The eastern sky glows with early dawn.\n",
3);
}
else
{
if (weather_obj->query_night() < value)
return "The sun sets.\n";
else if (weather_obj->query_night() > value)
return "The sun rises.\n";
update_weather(value);
}
}
}
query_last_night() { return last_night; }
update_weather(current)
{
if (!weather_obj)
return;
if (current < 0)
current = weather_obj->query_night();
if (outdoorness < 2)
current = 1; /* treat as night if indoors */
add_light(last_night - current);
last_night = current;
ud_prints(); /* for footprints */
}
appropriate(new, current, st1, st2, st3, st4)
{
if (intp(st1))
st1 = nth(st1, st1, st2, st3, st4);
if (intp(st2))
st2 = nth(st2, st1, st2, st3, st4);
if (intp(st3))
st3 = nth(st3, st1, st2, st3, st4);
if (intp(st4))
st4 = nth(st4, st1, st2, st3, st4);
if (new > current)
if (current == 0)
return st1;
else
return st2;
else if (new < current)
if (new == 0)
return st3;
else
return st4;
}
nth(num, s1, s2, s3, s4)
{
if (num == 1)
return s1;
else if (num == 2)
return s2;
else if (num == 3)
return s3;
else if (num == 4)
return s4;
return 0;
}
/*
* This should be in the perception soul.
*/
peer(dir)
{
/* this function has been moved to the perception soul. The stub has
been left here for backward compatability. This should be logged - Zaph */
LOG->log("OLDCODE", "direct peer (room/room) in " + file_name(this_object()) + "\n");
return this_player()->query_soul("generic/perception")->peer(dir);
}
test_func(kludge)
{
array ret;
string func, arg;
object who;
if (!pointerp(kludge))
{
catch tell_object(find_player("dredd"), "Kludge is fucked = "+kludge+".\n") ;;
return;
}
func = kludge[0];
arg = kludge[1];
who = kludge[2];
if (!who)
return;
if (environment(who) != this_object())
return;
ret = this_object()->(func)(arg, who);
if (ret != 0 && ret[0] != 0)
call_out("test_func", ret[0], ({func, ret[1], who}));
}
check_wet(chance, who)
{
object wet;
if (who)
{
if (!random(chance))
{
wet = present("wet_obj", who);
if (wet)
wet->reset_wet();
else
{
wet = clone_object(WET_OBJ);
if (transfer(wet, who))
destruct(wet);
}
}
else
return ({chance*5, chance});
}
}
add_exit(string dest1, string dir1)
{
array tmp1, co;
int y;
tmp1 = this_object()->query_dest_dir();
if (!tmp1) tmp1 = [];
set_dest_dir(tmp1 + [ dest1, dir1 ]);
/* hack follows -> to force init() for everything in the room. */
co = contents(this_object());
for (y = 0; y < sizeof(co); y ++)
move_object(co[y], this_object());
return 1;
}
remove_exit(string dest1)
{
array tmp1, tmp2;
int x;
if (!pointerp(dest_dir)) return 0;
x = index(dest_dir, dest1);
if (x > -1 && (x / 2 == (x+1)/2))
{
tmp1 = dest_dir[..x-1];
tmp2 = dest_dir[x+2..];
dest_dir = tmp1 + tmp2;
}
else if (x > -1)
{
if (x == 1)
dest_dir = dest_dir[2..sizeof(dest_dir)-1];
else
{
tmp1 = dest_dir[..x-2];
tmp2 = dest_dir[x+1..];
dest_dir = tmp1 + tmp2;
}
}
return 0;
}
/*
* This is always called using this_object()->query_exit(), and so can
* easily be overridden in an inherited file.
*/
query_exit(str, int open)
{
int x;
array DestDir = this_object()->query_dest_dir();
array door;
if (!str) return 0;
if (!pointerp(DestDir)) return;
x = index(DestDir, str);
if (x < 1)
{
/* rare */
for (x = 1; x < sizeof(DestDir); x+=2)
{
door = explode(DestDir[x], "#");
if (door[0] == str)
{
if (open && (atoi(door[1]) != 0))
{
return 0;
}
return DestDir[x-1];
}
}
return 0;
}
/* matched it in an exit slot */
if (x % 2) return DestDir[x-1];
return DestDir[x+1]; /* searching for a filename's exit */
}
/* Jenna March 2000 */
query_direction(str, int open)
{
int i, x;
array DestDir = this_object()->query_dest_dir();
array door;
if (!str) return 0;
if (!pointerp(DestDir)) return;
for (i = 1; i < sizeof(DestDir); i+= 2) {
x = index(DestDir[i], "#");
if (x != -1)
DestDir[i] = DestDir[i][..x-1];
}
x = index(DestDir, str);
return x == -1 ? 0 : DestDir[x+1];
}
set_long(str) { long_desc = str; }
set_short(str) { short_desc = str; }
query_long_desc() { return long_desc; }
query_short_desc() { return short_desc; } /* redundant to short, but hey... */
/* Jenna Dec 98 */
query_long() { return short() + "\n" + long_desc; }
query_short() { return short(); }
set_dest_dir(array arr)
{
int x, Count;
array props, locks, old_doors;
/* check the direction and make sure it doesn't have a leading slash */
/* Also: new backwards compatability stuff for doors - Jenna May 2000 */
for (Count = 0, old_doors = []; Count < sizeof(arr); Count+=2)
{
if ((arr[Count])[0] == '/')
arr[Count] = arr[Count][1..];
x = index(arr[Count+1], "#"); /* old door setup, a la "east#2doorcode" */
if (x != -1)
{
JEN("Got old door setup.");
props = [];
locks = 0;
if (arr[Count+1][x+1] == '0')
props += [ "open" ];
else if (arr[Count+1][x+1] == '1')
props += [ "unlocked" ];
if (arr[Count+1][x+2..] != "")
locks = [ arr[Count+1][x+2..], 2];
arr[Count+1] = arr[Count+1][..x-1];
old_doors += [ arr[Count+1], props, locks ];
}
}
/* a hack for the detection of direct assignments to dest_dir : Hunter */
set_dest_dir_flag = 1;
dest_dir = arr;
/*
* If this is a cloned room, we do _not_ want to add ourselves to
* the mapper - we let our area_controller do it (area controller should
* only add names of rooms with constant dest dirs, and manage all others
* itself).
* Note: almost all virtual objects are cloned - hence the is_cloned hack.
*/
for (Count = 0; Count < sizeof(old_doors); Count += 3)
make_door(old_doors[Count], 2, old_doors[Count+1], old_doors[Count+2]);
call_out("notify_mapper", 0);
#if 0
// actually we probably want to add it (and check if it connects
// to a non virtual area?
if ((!area_controller) && (!is_cloned(this_object())))
{
call_out("notify_mapper", 0);
}
#endif
}
notify_mapper()
{
//catch
MAPKNOWER->add_location(file_name(this_object()), dest_dir, area_controller);
//;
}
/*
* return a _COPY_ of the dest_dir array
*
* Some rooms may override this for special effects. Hence always refer to
* query_dest_dir(), instead of dest_dir, in inherited objects
*/
query_dest_dir()
{
return (pointerp(dest_dir)?( dest_dir+({ }) ):({ }) );
}
/*
* Doorways
*/
make_door(direction, strength, props, locks, magicks)
{
array door = [ "", strength, 0, 0, 0, 0, 0, magicks, 0 ];
int i, x, unlocked, hinges_this_side;
string neighbour;
JEN("make_door in direction: "+direction+".");
if (!dest_dir || index(this_object()->query_dest_dir(), direction) == -1)
return 0;
if (strength == "auto")
{
object or = load_object(query_exit(direction));
array dw = or->query_doorways();
string od = or->query_direction(file_name(this_object()));
int x2;
x = index(dw, od);
if (x == -1) return 0;
add_doorway(direction); /* adds doorway if not already present */
x2 = index(doorways, direction);
doorways[x2+1] = dw[x+1];
return 1;
}
door[0] = "a " + DOOR->describe_strength(strength) + " door";
if (arrayp(props))
{
x = index(props, "unlocked");
if (x != -1)
{
unlocked = 1;
props = props[..x-1] + props[x+1..];
}
x = index(props, "open");
if (x != -1)
{
door[2] = 1;
door[0] += " (open)";
unlocked = 1;
props = props[..x-1] + props[x+1..];
}
x = index(props, "hinges_this_side");
if (x != -1)
{
hinges_this_side = 1;
props = props[..x-1] + props[x+1..];
}
if (sizeof(props))
door[8] = props;
}
neighbour = query_exit(direction);
if (hinges_this_side)
door[5] = [ file_name(this_object()), neighbour ];
else
door[5] = [ neighbour, file_name(this_object()) ];
if (arrayp(locks))
{
door[6] = [ locks[0], locks[1], !unlocked ];
for (i = 2; i < sizeof(locks); i+= 2)
door[6] += [ locks[i], locks[i+1], !unlocked ];
}
add_doorway(direction); /* adds doorway if not already present */
x = index(doorways, direction);
doorways[x+1] = [ DOOR, door ];
return 1;
}
fix_doors()
{
object door;
int i;
set_doorways(0);
for (i = 1; i < sizeof(dest_dir); i += 2)
make_door(dest_dir[i], "auto");
write("Reset room's doors according to neighbours.\n");
return 1;
}
query_doorways() { return doorways; }
/* Returns 1 if any doors exist (but doesn't load them),
* else 0.
*/
has_doors()
{
int i;
if (!doorways || !sizeof(doorways))
return 0;
for (i = 1; i < sizeof(doorways); i += 2)
if (doorways[i])
return 1;
return 0;
}
query_doors()
{
int i;
array ret;
object door;
if (doorways)
for (i = 0, ret = []; i < sizeof(doorways); i+=2)
{
door = query_door(doorways[i]);
if(door)
ret += [ door ];
}
return ret;
}
add_doorways(arg) { return set_doorways(arg); }
set_doorways(arg)
{
int i;
array tmp;
if (stringp(arg))
return add_doorway(arg);
else if (arrayp(arg))
for (i = 0; i < sizeof(arg); i++)
add_doorway(arg[i]);
else if (!arg)
doorways = 0;
}
add_doorway(string dir)
{
JEN("add_doorway(\""+dir+"\").");
if (!arrayp(doorways))
doorways = [];
if (index(doorways, dir) == -1)
doorways += [ dir, 0 ];
}
internal_add_door(string dir, door) /* called by init_arg */
{
int x;
if (!arrayp(doorways))
doorways = [];
x = index(doorways, dir);
if (x == -1)
doorways += [ dir, door ];
else
doorways[x+1] = door;
}
add_door(string dir, object door)
{
int x;
if (!arrayp(doorways))
doorways = [];
x = index(doorways, dir);
if (x == -1)
doorways += [ dir, door ];
else
doorways[x+1] = door;
JEN("Added door "+file_name(door));
}
remove_door(dir_or_door)
{
int x;
if (!arrayp(doorways))
doorways = [];
x = index(doorways, dir_or_door);
if (x != -1)
doorways[x+1-(x%2)] = 0;
}
remove_doorway(string dir)
{
int x;
if (!arrayp(doorways))
doorways = [];
x = index(doorways, dir);
if (x != -1)
doorways = doorways[..x-1] + doorways[x+2..];
}
query_door(string dir, int flag)
{
int x;
if (!arrayp(doorways))
doorways = [];
x = index(doorways, dir);
if (x == -1)
return 0;
if (arrayp(doorways[x+1]))
doorways[x+1] = load_door(doorways[x+1]);
if (objectp(doorways[x+1]))
return doorways[x+1];
return flag;
}
load_door(array door_auto_load)
{
string my_dir;
object other_room;
array installed;
object door = clone_object(door_auto_load[0]);
door->init_arg(door_auto_load[1]);
installed = door->query_installed();
JEN("Loaded door "+file_name(door));
if (installed)
{
other_room = (installed[0] == file_name(this_object()) ?
load_object(installed[1]) :
load_object(installed[0]));
my_dir = other_room->query_direction(file_name(this_object()));
JEN("calling other_room->add_door in direction \""+my_dir+"\"");
if (my_dir)
other_room->add_door(my_dir, door);
}
return door;
}
query_doorway(string dir)
{
return query_door(dir, 1);
}
/*
* do_door() interprets commands for installed doors, stripping out
* any directional command (e.g. "kick east door") and sending it
* to the appropriate door object (e.g. east_door->kick("door")).
* - Jenna May 2000
*/
do_door(str)
{
string verb, direction, stripped;
array args, doors;
object door;
if (!doorways || !sizeof(doorways))
return 0;
verb = query_verb();
notify_fail("Which door do you wish to " + verb + "? \n");
if (!str)
return 0;
args = STRINGS->split_first(str, "door");
if (!args)
return 0;
if (this_player()->query_ghost())
{
notify_fail("Your hand passes straight through the door!\n");
return 0;
}
direction = STRINGS->strip_whitespace(args[0]);
if (verb == "knock" && args[0][..2] == "on ")
{
notify_fail("On which door do you wish to knock?\n");
direction = STRINGS->strip_whitespace(args[0][3..]);
}
else if (verb == "prise" && args[0][..9] == "lock from ")
{
notify_fail("Which of the doors do you want to prise a lock from?\n");
direction = STRINGS->strip_whitespace(args[0][10..]);
}
if (verb == "break")
verb = "smash";
if (direction && direction != "")
{
array tmp;
door = query_door(direction);
if (!door)
{
notify_fail("There is no door to the "+direction+".\n");
return 0;
}
tmp = STRINGS->split_around(str, [ direction + " " ] );
stripped = tmp[0] + tmp[1];
}
else
{
doors = query_doors();
if (!doors)
{
notify_fail("This room has no door.\n");
return 0;
}
else if (sizeof(doors) != 1)
return 0;
door = doors[0];
stripped = str;
}
if (!door)
return 0;
JEN("Calling "+verb+"(\""+stripped+"\")");
return door->(verb)(stripped);
}
/*
* An ID function that tries to find a matching door for the
* string - e.g. "east door", "wooden door". If found, it
* returns the door object.
*
* Jenna May 2000
*/
find_door(str)
{
array a, qd;
int s, x, i;
string desc;
object door;
if (!str)
return 0;
a = explode(str, " ");
s = sizeof(a);
if (a[s-1] != "door")
{
str += " door";
a += [ "door" ];
s++;
}
qd = query_doors();
if (!qd || !sizeof(qd))
return 0;
if (s == 1)
{
door = qd[0];
}
else
{
x = index(doorways, a[s-2]);
if (x != -1 && doorways[x+1])
{
door = doorways[x+1];
}
}
if (!door)
{
for (i = 0; i < sizeof(qd); i++)
{
if (qd[i]->id(str))
return qd[i];
}
}
return (door ? door : 0);
}
owner(dir)
{
string who = "nobody";
(int|array|string) o;
if (!dir) return who;
o = query_exit(dir)->query_owner();
if (o) {
if (stringp(o))
who = capitalize(o);
else
who = implode(o, ", ");
}
return who;
}
query_auto_load()
{
array ret;
int i, c;
if (doorways && sizeof(doorways))
{
ret = allocate(sizeof(doorways));
for (c = 0, i = 1; i < sizeof(doorways); i += 2)
{
if (doorways[i])
{
ret[c++] = doorways[i-1];
if (objectp(doorways[i]))
ret[c++] = doorways[i]->query_auto_load();
else
ret[c++] = doorways[i];
}
}
ret = ret[..c-1];
if (sizeof(ret))
return [ "doorways", ret ];
}
}
init_arg(args)
{
int x, i;
JEN("init_arg.");
if (arrayp(args))
{
x = index(args, "doorways");
if (x != -1)
{
JEN("init_arg provided "+sizeof(args[x+1])+ "/2 doorways.");
for (i = 0; i < sizeof(args[x+1]); i += 2)
internal_add_door(args[x+1][i], args[x+1][i+1]);
}
}
}
/* End doorways */
set_items(arr) { items = arr; }
query_items() { return items; }
/*
* find the specified object in the items array
*/
int
query_item_index( string id )
{
int i;
if(!arrayp(items)) return -1;
for (i = 0; i < sizeof(items); i += 2)
{
if (stringp(items[i]) && items[i] == id)
return i;
if (arrayp(items[i]) && index(items[i], id) != -1)
return i;
}
return -1;
}
query_item_description(id)
{
int id_pos;
id_pos = query_item_index(id);
if (id_pos < 0) return 0;
else
{
if (items[id_pos+1][0] == '@')
{
return this_object()->(items[id_pos+1][1..])(id);
}
return items[id_pos+1];
}
}
add_item(item, desc)
{
int i;
if (!items) items = ({item, desc});
else
{
i = query_item_index(item);
if (i == -1)
items += ({item, desc});
else
items[i+1] = desc;
}
return 1;
}
remove_item(item)
{
int it;
it = query_item_index(item);
if (it < 0) return 0;
items = items[..it-1] + items[it+2..];
return 1;
}
/*
* Coded by Roam, August 1991
*/
snow_make(str)
{
object ball;
notify_fail("Make what?\n");
if (!str)
return 0;
if (str != "snowball" && str != "snow ball" && str != "a snowball" &&
str != "a snow ball")
{
return 0;
}
if (weather_obj->query_precipitation()<2 ||
weather_obj->query_temperature())
{
notify_fail("There's no snow here.\n");
return 0;
}
/* ok to make a snow ball */
write("You pack some snow into a snow ball.\n");
say(this_player()->query_name()+" makes a snow ball.\n");
ball = clone_object("players/nostromo/obj/snow_ball");
transfer(ball, this_player());
return 1;
}
query_destruct() { return 0; }
/*
* FOOTPRINTS.
* Coded by Zaphoid of Shattered World.
* 26/4/92
*
* prints has two states:-
*
* 0 = prints off.
* 1 = prints on.
*
* prints_status has three states.
*
* 0 = no prints.
* 1 = one set of prints in the room.
* 2 = two or more sets of prints in the room.
*
* prints changed to use a timestamp rather than a call_out()
*
*/
/* this function is called whenever someone leaves the room. */
/* geez - this is FAR to big and complex - Dredd */
/* we'd prefer no exit function whatsoever */
exit(obj)
{
string dir;
string race;
string leave;
if (obj) add_light(-1 * obj->query_light());
this_object()->save_to_database();
// The following added by Jenna Aug 01
// Homes/shops need to save if players/mobs drop or pick up items
// within the room -- previously, if they did this and the shop
// didn't buy or sell anything before reboot, the item respawned /
// vanished.
if (this_object()->is_persistent())
this_object()->set_needs_update();
if (query_prints_status() == 0 && query_prints() == 1)
{
dir = query_verb();
if (obj->query_invis()) return 0;
if ((query_win() || query_precip()) && query_prints_status() > 0 )
{
ud_prints();
return 0;
}
else if (obj->query_race())
{
if (obj->query_npc())
{
race = capitalize(obj->query_race());
}
else
{
race = "Player";
}
if (index(query_dest_dir(),dir) != -1)
{
leave = dir;
}
else
{
leave = "X";
}
add_prints(race, leave);
}
}
else
{
if (query_prints_status() == 1)
{
prints_desc_short =
"There are footprints leaving in many directions.\n";
prints_desc_long = "There many footprints in the room. So many "+
"in fact that it is impossible to tell in what direction people "+
"have been leaving.\n";
prints_status = 2;
prints_time = time();
ud_prints();
}
}
}
/* Separated out from exit() by Jenna May 2000 for use with
* new door system.
*/
save_to_database()
{
#ifdef PERSISTENT
// if (!this_object()->is_persistent() && sizeof(contents(this_object())) > 1)
// {
catch W_DATABASE->commit_items(this_object(), 1);;
// }
#endif
}
set_prints(arg)
{
if (arg != 1 && arg != 0)
return 0;
prints = arg;
return 1;
}
query_prints()
{
return prints;
}
query_prints_short()
{
/* returns the current print desc. */
if (query_prints()) {
ud_prints();
return prints_desc_short;
}
}
query_prints_long()
{
/* returns the long description of the footprints. */
if (query_prints())
{
ud_prints();
if (query_prints_status() == 0)
return "The floor is completely clear of footprints.\n";
if (query_prints_status() > 0)
return prints_desc_long;
}
}
query_prints_status()
{
if (query_prints()) {
ud_prints();
return prints_status;
}
}
clean_prints()
{
/* Removes all footprints from the room. */
if (query_prints())
{
prints_desc_long = "";
prints_desc_short = "";
prints_status = 0;
prints_time = 0;
}
}
add_prints(race, leave)
{
/* sets the long and short of print discriptions */
if (query_prints())
{
ud_prints();
if (query_prints_status()== 0)
{
if (leave == "X")
{
prints_desc_short = "There is a set of footprints here.\n";
prints_desc_long = "There is a set of footprints that finish "+
"in the middle of the room. They just stop there.\n";
}
else
{
prints_desc_short = "There is a set of footprints leading "
+ leave + ".\n";
prints_desc_long = "The footprints look as if they have been "+
"made by a "+race+" and point towards the "+leave+" exit" + ".\n";
}
}
prints_status = 1;
prints_time = time();
}
}
query_precip()
{
if (!weather_obj)
return 0;
if (weather_obj -> query_precipitation() > 1 && query_outdoors() >= 2)
return 1;
}
query_win()
{
if (!weather_obj)
return 0;
if (weather_obj -> query_wind() > 2 && query_outdoors() >= 1)
return 1;
}
ud_prints()
{
/* change in the weather calls this so the prints are removed */
if ((query_precip() || query_win())
&& query_prints() /* && (query_prints_status() > 0)*/ )
{
clean_prints();
}
if ((time() - prints_time) >= 300) clean_prints();
}
/*
* Stuff designed to interface with area controllers - G.
* Note that set_dest_dir() relies on area_controller being set before
* it is called.
*/
set_area_controller((object|string) x)
{
if (objectp(x)) x = file_name(x);
area_controller = x;
if (set_dest_dir_flag)
{
LOG->log("BAD_CONTR", file_name(this_object())
+ "; dest dir set before controller (" + x + ")\n");
}
}
query_area_controller()
{
return area_controller;
}
/*
* Controllers can set decay times on rooms - if the decay time is 0,
* the room doesn't decay, otherwise the room will call back its controller
* if it has been empty of all living items for at least the given decay time,
* and is currently empty of objects. If the room has no controller when it decays,
* it will dest itself.
*/
static int decay_time = 0;
set_room_decay(int t)
{
if (0 < t && t < 300)
t = 300; /* 5 mins minimum */
if (t < 0)
t = 0;
decay_time = t;
if (find_call_out("room_decay") == (-1))
call_out("room_decay", t);
}
room_decay()
{
if (!decay_time)
return 0;
if ((time() - last_init_time) < decay_time)
{
call_out("room_decay", decay_time - time() + last_init_time);
return;
}
(int|array) c = contents(this_object());
if (pointerp(c) && !sizeof(c))
c = 0;
if (!c)
{
if (area_controller)
area_controller->room_decay(this_object());
else
this_object()->clean_up();
}
else
call_out("room_decay", decay_time);
}
/* So we can put things into the room! */
can_put_and_get ()
{
return 1;
}
/* added so perception can tell the difference between a room.c room and a
door.c room */
query_room_type() { return "room"; }
query_coords() { return [ 0, 0, 0 ]; }