#include <kernel/kernel.h> #include <phantasmal/log.h> #include <phantasmal/exit.h> #include <phantasmal/map.h> #include <phantasmal/lpc_names.h> #include <type.h> inherit PHRASE_REPOSITORY; private int* exit_segments; private mapping name_for_dir; private mapping shortname_for_dir; private mapping builder_directions; /* When loading files, this lets us resolve room numbers *after* all rooms load... */ private mixed* deferred_add_newexit; /* Prototypes */ private void add_complex_exit_by_unq(int roomnum1, mixed value); void upgraded(varargs int clone); #define PHR(x) PHRASED->new_simple_english_phrase(x) #define FILE(x) PHRASED->file_phrase(EXITD_PHRASES,(x)) #define EXITD_PHRASES "/usr/common/sys/exitd.phr" static void create(varargs int clone) { ::create(clone); if(!find_object(SIMPLE_EXIT)) compile_object(SIMPLE_EXIT); exit_segments = ({ }); deferred_add_newexit = ({ }); upgraded(); if(!load_filemanaged_file(EXITD_PHRASES)) { error("Can't load ExitD phrase file!"); } } void upgraded(varargs int clone) { if(!SYSTEM() && !COMMON()) return; ::upgraded(); name_for_dir = ([ DIR_NORTH : FILE("north"), DIR_SOUTH : FILE("south"), DIR_EAST : FILE("east"), DIR_WEST : FILE("west"), DIR_NORTHWEST : FILE("northwest"), DIR_NORTHEAST : FILE("northeast"), DIR_SOUTHWEST : FILE("southwest"), DIR_SOUTHEAST : FILE("southeast"), DIR_IN : FILE("in"), DIR_OUT : FILE("out"), DIR_UP : FILE("up"), DIR_DOWN : FILE("down"), ]); shortname_for_dir = ([ DIR_NORTH : FILE("n"), DIR_SOUTH : FILE("s"), DIR_EAST : FILE("e"), DIR_WEST : FILE("w"), DIR_NORTHWEST : FILE("nw"), DIR_NORTHEAST : FILE("ne"), DIR_SOUTHWEST : FILE("sw"), DIR_SOUTHEAST : FILE("se"), DIR_IN : FILE("i"), DIR_OUT : FILE("o"), DIR_UP : FILE("u"), DIR_DOWN : FILE("d"), ]); /* This is the set of direction string acceptable for builder commands (currently) and file formats (probably forever) */ builder_directions = ([ "north" : DIR_NORTH, "south" : DIR_SOUTH, "east" : DIR_EAST, "west" : DIR_WEST, "northeast" : DIR_NORTHEAST, "northwest" : DIR_NORTHWEST, "southeast" : DIR_SOUTHEAST, "southwest" : DIR_SOUTHWEST, "up" : DIR_UP, "down" : DIR_DOWN, "in" : DIR_IN, "out" : DIR_OUT, "n" : DIR_NORTH, "s" : DIR_SOUTH, "e" : DIR_EAST, "w" : DIR_WEST, "ne" : DIR_NORTHEAST, "nw" : DIR_NORTHWEST, "se" : DIR_SOUTHEAST, "sw" : DIR_SOUTHWEST, "u" : DIR_UP, "d" : DIR_DOWN, ]); } void destructed(int clone) { if(SYSTEM()) { } } /* Return phrase for direction name */ object get_name_for_dir(int direction) { if (direction <= 0) { error("Can't get the name of a special direction!"); } return name_for_dir[direction]; } /* Return phrase for direction short name */ object get_short_for_dir(int direction) { object phr; if (direction <= 0) { error("Can't get the short name of a special direction!"); } phr = shortname_for_dir[direction]; if(!phr) error("Can't get short name for direction " + direction); return phr; } int direction_by_string(string direc) { if(builder_directions[direc]) return builder_directions[direc]; return DIR_ERR; } int opposite_direction(int direction) { if (direction <= 0) { error("Can't get the opposite direction of a special direction!"); } if(direction % 2) { return direction + 1; } return direction - 1; } private void push_or_add_newexit(int roomnum1, mixed *value) { int ctr, roomnum2; object exit1, exit2; object room1, room2; for(ctr=0;ctr<sizeof(value);ctr++) { if (value[ctr][0]=="destination") { roomnum2 = value[ctr][1]; room1 = MAPD->get_room_by_num(roomnum1); if (roomnum2 > 0) { room2 = MAPD->get_room_by_num(roomnum2); if(room1 && room2) { add_complex_exit_by_unq(roomnum1, value); } else { deferred_add_newexit += ({ ({ roomnum1, value }) }); } } else if (room1) { add_complex_exit_by_unq(roomnum1, value); } else { deferred_add_newexit += ({ ({ roomnum1, value }) }); } } } } void room_request_complex_exit(int roomnum1, mixed value) { if(previous_program() != ROOM) { error("Only ROOM can request deferred exit creation!"); } push_or_add_newexit(roomnum1, value); } void add_deferred_exits(void) { int ctr; mixed* exits, *ex; if(!SYSTEM() && !COMMON() && !GAME()) return; exits = deferred_add_newexit; deferred_add_newexit = ({ }); if (sizeof(exits)) { for(ctr = 0; ctr < sizeof(exits); ctr++) { ex = exits[ctr]; push_or_add_newexit(ex[0], ex[1]); } } } int num_deferred_exits(void) { if(!SYSTEM() && !COMMON() && !GAME()) return -1; if(!deferred_add_newexit) return -1; return sizeof(deferred_add_newexit); } private int allocate_exit_obj(int num, object obj) { int segment; if(num >= 0 && OBJNUMD->get_object(num)) error("Object already exists with number " + num); if(num != -1) { OBJNUMD->allocate_in_segment(num / 100, num, obj); /* If that succeeded, add it to exit_segments */ if(!(sizeof( ({ num / 100 }) & exit_segments ))) { string tmp; exit_segments |= ({ num / 100 }); } return num; } for(segment = 0; segment < sizeof(exit_segments); segment++) { num = OBJNUMD->new_in_segment(exit_segments[segment], obj); if(num != -1) { return num; } } segment = OBJNUMD->allocate_new_segment(); exit_segments += ({ segment }); num = OBJNUMD->new_in_segment(segment, obj); return num; } private void add_complex_exit_by_unq(int roomnum1, mixed value) { int ctr, ctr2, num1, num2, roomnum2; object exit1, exit2; object room1, room2; mixed nouns, adjectives; exit1 = clone_object(SIMPLE_EXIT); room1 = MAPD->get_room_by_num(roomnum1); exit1->set_from_location(room1); for(ctr=0;ctr<sizeof(value);ctr++) { if (value[ctr][0]=="rnumber") { exit1->set_number(value[ctr][1]); } else if (value[ctr][0]=="direction") { exit1->set_direction(value[ctr][1]); } else if (value[ctr][0]=="destination") { room2 = MAPD->get_room_by_num(value[ctr][1]); exit1->set_destination(room2); exit1->set_from_location(room1); } else if (value[ctr][0]=="return") { exit1->set_link(value[ctr][1]); } else if (value[ctr][0]=="type") { exit1->set_exit_type(value[ctr][1]); } else if (value[ctr][0]=="rdetail") { /* what to do? */ } else if (value[ctr][0]=="rbdesc") { exit1->set_brief(value[ctr][1]); } else if (value[ctr][0]=="rgdesc") { /* exit1->set_glance(value[ctr][1]) */ ; } else if (value[ctr][0]=="rldesc") { exit1->set_look(value[ctr][1]); } else if (value[ctr][0]=="redesc") { exit1->set_examine(value[ctr][1]); } else if (value[ctr][0]=="rflags") { exit1->set_all_flags(value[ctr][1]); } else if(value[ctr][0]=="rnouns") { for(ctr2 = 0; ctr2 < sizeof(value[ctr][1]); ctr2++) { exit1->add_noun(value[ctr][1][ctr2]); } } else if(value[ctr][0]=="radjectives") { for(ctr2 = 0; ctr2 < sizeof(value[ctr][1]); ctr2++) { exit1->add_adjective(value[ctr][1][ctr2]); } } } room1->add_exit(exit1->get_direction(), exit1); num1 = allocate_exit_obj(exit1->get_number(), exit1); } /* note: caller must make sure not to override existing exits!!! */ void add_twoway_exit_between(object room1, object room2, int direction, int num1, int num2) { object exit1, exit2; object dir, opp_dir; if(!SYSTEM() && !COMMON() && !GAME()) return; if (direction <= 0) { error("Can't add two-way exit in a nonstandard direction!"); } dir = get_name_for_dir(direction); opp_dir = get_name_for_dir(opposite_direction(direction)); exit1 = clone_object(SIMPLE_EXIT); exit2 = clone_object(SIMPLE_EXIT); exit1->set_destination(room2); exit1->set_from_location(room1); exit1->set_direction(direction); exit1->set_exit_type(ET_TWOWAY); exit1->set_open(TRUE); exit1->set_container(TRUE); exit2->set_destination(room1); exit2->set_from_location(room2); exit2->set_direction(opposite_direction(direction)); exit2->set_exit_type(ET_TWOWAY); exit2->set_open(TRUE); exit2->set_container(TRUE); room1->add_exit(direction, exit1); room2->add_exit(opposite_direction(direction), exit2); num1 = allocate_exit_obj(num1, exit1); num2 = allocate_exit_obj(num2, exit2); if(num1 < 0 || num2 < 0) { error("Exit numbers not assigned successfully!"); } exit1->set_number(num1); exit1->set_link(num2); exit2->set_number(num2); exit2->set_link(num1); if(exit1->get_number() < 0 || exit2->get_number() < 0) { destruct_object(exit1); destruct_object(exit2); error("Exit numbers not assigned successfully!"); } exit1->set_brief(PHRASED->new_simple_english_phrase("exit")); exit2->set_brief(PHRASED->new_simple_english_phrase("exit")); } /* note: caller must make sure not to override existing exits!!! */ void add_oneway_exit_between(object room1, object room2, int direction, int num1) { object exit1, dir; if(!SYSTEM() && !COMMON() && !GAME()) return; if (direction <= 0) { error("Can't add an exit in a special direction!"); } exit1 = clone_object(SIMPLE_EXIT); dir = get_name_for_dir(direction); exit1->set_destination(room2); exit1->set_from_location(room1); exit1->set_direction(direction); exit1->set_exit_type(ET_ONEWAY); exit1->set_open(TRUE); exit1->set_container(TRUE); room1->add_exit(direction, exit1); num1 = allocate_exit_obj(num1, exit1); if(num1 < 0 ) { error("Exit number not assigned successfully!"); } exit1->set_number(num1); exit1->set_link(-1); if(exit1->get_number() < 0) { error("Exit number not assigned successfully!"); destruct_object(exit1); } } void remove_exit(object room, object exit) { if(!SYSTEM() && !COMMON() && !GAME()) return; room->remove_exit(exit); } void clear_exit(object exit) { object exit2; if(!SYSTEM() && !COMMON() && !GAME()) return; if (exit->get_exit_type() == ET_TWOWAY) { exit2 = EXITD->get_exit_by_num(exit->get_link()); destruct_object(exit2); } destruct_object(exit); } void clear_all_exits(object room) { object exit, exit2; if(!SYSTEM() && !COMMON() && !GAME()) return; if(!room) error("Passed nil to clear_all_exits!"); while(exit = room->get_exit_num(0)) { if (exit->get_exit_type() == ET_TWOWAY) { exit2 = EXITD->get_exit_by_num(exit->get_link()); destruct_object(exit2); } destruct_object(exit); } } object get_exit_by_num(int num) { int seg; if(!SYSTEM() && !COMMON() && !GAME()) return nil; if(num < 0) return nil; seg = num / 100; if(sizeof( ({ seg }) & exit_segments )) { return OBJNUMD->get_object(num); } return nil; } int* get_exit_segments(void) { if(!SYSTEM() && !COMMON() && !GAME()) return nil; return exit_segments[..]; } int* get_all_exits(void) { int* exits, *tmp; int iter; if(!SYSTEM() && !COMMON() && !GAME()) return nil; exits = ({ }); for(iter = 0; iter < sizeof(exit_segments); iter++) { tmp = OBJNUMD->objects_in_segment(exit_segments[iter]); if(tmp) exits += tmp; } if(!sizeof(exits)) return nil; return exits; } int* exits_in_segment(int seg) { if(!SYSTEM() && !COMMON() && !GAME()) return nil; if(sizeof( ({ seg}) & exit_segments )) { return OBJNUMD->objects_in_segment(seg); } return nil; }