// Gototh, 10/07/99 /** * This inherit handles all the placement code and description stuff for * player housing. * @author Gototh * @started 10/07/99 */ /* * Todo: * * - Convert from properties to functions. * - Document fully. */ #include <obj_parser.h> #include <dirs.h> #include <room/placement.h> #define PLAYTESTING //#define DEBUG #define MAX_PLACED 10 /* #define PLACEMENT_CURRENT_ROOM_VERB "current room verb" #define CURRENT_ROOM_OTHER "current room other" #define CURRENT_ROOM_POSITION "current room position" #define CURRENT_ROOM_RELATIVE "current room relative" #define IMMOVABLE "immovable" */ #define PLACEMENT_CURRENT_ROOM_HIDDEN "current room hidden" #define OPPOSITE 0 #define ABOVE 1 #define BELOW 2 #define RIGHT 3 #define LEFT 4 #define OPPOSITE_DESCS ({ \ "opposite which", \ "facing which", \ }) #define ABOVE_DESCS ({ \ "above which", \ "over which", \ }) #define BELOW_DESCS ({ \ "under which", \ "below which", \ }) #define RIGHT_DESCS ({ \ "to the right of which", \ }) #define LEFT_DESCS ({ \ "to the left of which", \ }) #define HANGING_VERBS ({ \ "hanging", \ "suspended", \ "dangling", \ }) #define HANGING_PREPOSITIONS ({ \ "from", \ "on", \ }) #define WALL_PREPOSITIONS ({ \ "beside", \ "against", \ }) #define CORNER_PREPOSITIONS ({ \ "in", \ }) #define ABOUT_PREPOSITIONS ({ \ "near", \ "at", \ }) // "in front of", \ string get_pos_desc(int pos_int); int do_place(object *obs, string, string, mixed *args, string); int do_help(object *); int get_pos_int(string str); string extra_look(object); string object_position_desc(object thing); string object_desc(object thing); string object_desc2(object thing); object *build_structure(); int find_opposite(int pos, object *structure); int find_above(int pos, object *structure); int find_below(int pos, object *structure); int find_left(int pos, object *structure); int find_right(int pos, object *structure); int get_structure_index(object ob); varargs int is_beside_wall(int pos, int flag); varargs int is_in_corner(int pos, int flag); nosave object *needs_described; nosave object *have_described; nosave int orientation; nosave mapping _wall_names; nosave string furniture_long = ""; void init() { if(!this_player() || !interactive(this_player())) return; this_player()->add_command("place", this_object(), "<indirect:object'furniture'> so it is <word'verb'> " "{"+implode(ABOUT_PREPOSITIONS+HANGING_PREPOSITIONS+WALL_PREPOSITIONS+CORNER_PREPOSITIONS, "|")+"} " "the <string:'location'> [wall|corner]"); // Without a verb. this_player()->add_command("place", this_object(), "<indirect:object'furniture'> " "{"+implode(ABOUT_PREPOSITIONS+HANGING_PREPOSITIONS+WALL_PREPOSITIONS+CORNER_PREPOSITIONS, "|")+"} the <string'location'> [wall|corner]"); this_player()->add_command("displace", this_object(), "<indirect:object'furniture'>"); this_player()->add_command("place", this_object(), "help", (: do_help(0) :)); this_player()->add_command("place", this_object(), "help <indirect:object'furniture'>", (: do_help($1[0]) :)); } /** * This method sets a special description for a wall. For example making * it a fence or something. * @param direction the direction to do a thing for * @param name the nice name */ void set_wall_name(string direction, string name) { int pos; if (!_wall_names) { _wall_names = ([ ]); } pos = get_pos_int(direction); _wall_names[pos] = name; } /* set_wall_name() */ /** * This method gets the wall name from the integer position. * @param pos the integer position * @return the wall name */ string query_wall_name_pos(int pos) { if (!_wall_names) { _wall_names = ([ ]); } if (_wall_names[pos]) { return _wall_names[pos]; } if(!orientation) { if(pos % 2) { return "corner"; } return "wall"; } if(pos % 2) { return "wall"; } return "corner"; } /* query_wall_name_pos() */ /** * This method returns the special description for the wall in that * direction. * @param direction * @return the special description, or wall if none */ string query_wall_name(string direction) { int pos; pos = get_pos_int(direction); return query_wall_name_pos(pos); } /* query_wall_name() */ string query_another(object ob) { object *obs; string another; obs = filter(have_described, (: $1->query_plural() == $(ob->query_plural()) :)); switch(sizeof(obs)) { case 0 : another = " a"; break; case 1 : another = " another"; break; default : another = " yet another"; break; } return another; } /* query_another() */ string query_more(object ob) { object *obs; string more; obs = filter(have_described, (: $1->query_plural() == $(ob->query_plural()) :)); switch(sizeof(obs)) { case 0 : more = ""; break; default : more = " more"; break; } return more; } /* query_more() */ string clutter_string(object ob) { mixed *things; string cluttered = ""; if(ob->query_has_surface()) { things = ob->find_inv_match("all", ob); things = unique_array(things, (: $1->query_plural() :)); switch(sizeof(things)) { case 1 : if(sizeof(things[0]) == 1) cluttered = " is"; else cluttered = " are"; cluttered += " " + query_multiple_short(things[0]); break; default : break; } } return cluttered; } /* clutter_string() */ string query_multiple_furniture_short(object *obs) { object *group; mixed *things; string str; string *strs; strs = ({ }); things = unique_array(obs, (: $1->query_plural() :)); foreach(group in things) { switch(sizeof(group)) { case 0 : break; case 1 : str = query_another(group[0])[1..]; str += " " + group[0]->query_short(); break; default : str = query_num(sizeof(group)); str += query_more(group[0]); str += " " + group[0]->query_plural(); break; } strs += ({ str }); } return query_multiple_short(strs); } /* query_multiple_furniture_short() */ /* * This function returns true if the object has been placed in * the corner. */ varargs int is_in_corner(int pos, int flag) { if(orientation && !flag) return is_beside_wall(pos, 1); if(member_array(pos, ({ 3, 5, 7, 9 })) != -1) return 1; return 0; } /* is_in_corner() */ /* * This function returns true if the object has been placed * beside a wall. */ varargs int is_beside_wall(int pos, int flag) { if(orientation && !flag) { return is_in_corner(pos, 1); } if(member_array(pos, ({ 2, 4, 6, 8 })) != -1) { return 1; } return 0; } /* is_beside_wall() */ /* * This function returns true if the object is hanging from a * wall. */ int is_wall_hanging(int pos) { if(orientation) { if(member_array(pos, ({ 13, 15, 17, 19 })) != -1) { return 1; } return 0; } if(member_array(pos, ({ 12, 14, 16, 18 })) != -1) { return 1; } return 0; } /* is_hanging() */ int is_in_centre(int pos) { if(pos == 1) { return 1; } return 0; } /* is_hanging() */ int query_max_space_around(int pos) { if(is_in_corner(pos)) { return 2; } if(is_beside_wall(pos)) { return 3; } if(is_in_centre(pos)) { return 4; } return 0; } /* query_max_space_around() */ int do_help(object ob) { string str; string *bits; if(ob && !ob->query_furniture()) return this_player()->add_failed_mess(this_object(), "$I is not a piece of furniture.\n", ({ ob })); str = "The place command requires a furniture object, an optional verb, " "a preposition and a location. "; if(!ob) { str += "Which verbs are available depends on the furniture. Hanging " "furniture can use " + query_multiple_short(HANGING_VERBS) + ". "; } else { if(ob->query_allowed_room_verbs()) bits = filter(keys(ob->query_allowed_room_verbs()), (: $1 != "" :)); switch(sizeof(bits)) { case 0: str += "The " + ob->the_short() + " has no verbs.\n"; break; case 1: str += "The verb for " + ob->the_short() + " is " + query_multiple_short(bits) + ".\n"; break; default: str += "The verbs available for " + ob->the_short() + " are " + query_multiple_short(bits) + ".\n"; } } str += "The prepositions for hanging things are " + query_multiple_short(HANGING_PREPOSITIONS) + ", while items may be " "placed " + query_multiple_short(WALL_PREPOSITIONS) + " walls, " + query_multiple_short(CORNER_PREPOSITIONS) + " corners, or " + query_multiple_short(ABOUT_PREPOSITIONS) + " other objects. " "The " + query_multiple_short(({"ceiling", "floor", "centre", "north", "south", "east", "west", "northeast", "southeast", "northwest", "southwest"}))+ " may be used as locations.\n"; write(str); return 1; } int do_place(object *obs, string, string, mixed *args, string) { int pos, allowed; string k, v; mapping allowed_verbs; object *things; object *placed; object* already_around; class obj_match result; #ifdef DEBUG debug_printf("Obs: %O, args: %O", obs, args); #endif if(this_player() && !this_object()->test_occupier(this_player()->query_name()) && !this_player()->query_creator()) { this_player()->add_failed_mess(this_object(), "You don't own this house.\n", ({ })); return 0; } if(sizeof(obs) > 1) { this_player()->add_failed_mess(this_object(), "You may only place one object at a time.\n", ({ })); return 0; } if(!obs[0]->query_furniture()) { this_player()->add_failed_mess(this_object(), "$I is not suitable furniture.\n", ({ obs[0] })); return 0; } placed = filter(all_inventory(this_object()), (: $1->query_property(PLACEMENT_CURRENT_ROOM_POSITION) :)); if(!obs[0]->query_property(PLACEMENT_CURRENT_ROOM_POSITION)) { if(sizeof(placed) >= MAX_PLACED) { if(this_player()) this_player()->add_failed_mess(this_object(), "$C$"+this_object()->the_short() + " is too cluttered to place anything " "else.\n", ({ })); return 0; } } if(obs[0]->query_property(PLACEMENT_IMMOVABLE)) { this_player()->add_failed_mess(this_object(), "Try as you might you cannot move $I.\n", ({ obs[0] })); return 0; } /* * Wrestle with the args to see if they're not using a verb. If * they're not, reposition the args array so it still works. */ if(member_array(args[1], HANGING_PREPOSITIONS + WALL_PREPOSITIONS + CORNER_PREPOSITIONS + ABOUT_PREPOSITIONS) != -1) { // allow "north wall" or "northern wall" args[2] = replace_string(args[2], "ern ", " "); if((member_array(EXPAND_EXIT(args[2]), ({"north", "south", "east", "west", "northeast", "southeast", "southwest", "northwest", "centre"})) != -1) || sizeof(match_objects_for_existence(args[2], ({ this_object() }) ))) { int i; string *new_args; new_args = ({ }); new_args += ({ args[0] }); new_args += ({ "" }); for(i = 1; i < sizeof(args); i++) { new_args += ({ args[i] }); } args = new_args; } } allowed_verbs = obs[0]->query_allowed_room_verbs(); if(allowed_verbs) { allowed_verbs += ([ "" : "" ]); foreach(k, v in allowed_verbs) { if(k == args[1] || v == args[1]) { allowed = 1; break; } } } if(!allowed) { string *verb_keys; string extra = ""; // Tell them the verbs they're allowed to use. if(allowed_verbs) { verb_keys = keys(allowed_verbs); verb_keys -= ({ "" }); verb_keys = map(verb_keys, (: ("\"" + $1 + "\"") :)); extra = " You may only place $I " + query_multiple_short(verb_keys) + "."; } if(this_player()) this_player()->add_failed_mess(this_object(), "You can't place $I with that verb." + extra + "\n", ({ obs[0] })); return 0; } /* * Needed for compatability with two command syntaxes. */ if(sizeof(args) == 3) { string *shrapnel; shrapnel = explode(args[2], " "); args -= ({ args[2] }); args += shrapnel; } /* * Makes handling messages easier later on. */ if(args[1] != "") { args[1] = " " + args[1]; } pos = get_pos_int(args[3]); // Prevent "on the centre" by turning "on the floor" into "in the centre" if(args[3] == "floor" && args[2] == "on") args[2] = "in"; if(member_array(args[2], ABOUT_PREPOSITIONS) != -1) { /* * Check to see if they're trying to position the object relative * to another object. */ if(!pos) { result = (class obj_match)match_objects_in_environments(args[3], ({ this_object() })); if (result->result != OBJ_PARSER_SUCCESS) { if(this_player()) { add_failed_mess( match_objects_failed_mess( result )); } return 0; } things = result->objects; pos = things[0]->query_property(PLACEMENT_CURRENT_ROOM_POSITION); if(pos < -1) { if(this_player()) this_player()->add_failed_mess(this_object(), "$I is placed around another piece of " "furniture.\n", ({ things[0]->the_short() })); return 0; } if(pos == get_structure_index(obs[0])) { if(this_player()) { if(sizeof(args) >= 4) this_player()->add_failed_mess(this_object(), "You can't place $I " + args[2] + " the " + args[3] + ".\n", ({ obs[0] })); else this_player()->add_failed_mess(this_object(), "You can't place $I " + args[2] + " itself.\n", ({ obs[0] })); } return 0; } if(pos > 9) { if(this_player()) this_player()->add_failed_mess(this_object(), "You can't place $I " + args[2] + " " + things[0]->the_short() + ".\n", ({ obs[0] })); return 0; } if(pos == -1) { if(this_player()) this_player()->add_failed_mess(this_object(), "$I isn't placed.\n", ({ things[0] })); return 0; } } /* * Okay, change the sign on pos. This'll mark it as one of the * object positioned around the object in pos. */ already_around = filter(all_inventory(this_object()), (: $1->query_property(PLACEMENT_CURRENT_ROOM_POSITION) == -($(pos)) :)); if(sizeof(already_around) > query_max_space_around(pos)) { if(this_player()) this_player()->add_failed_mess(this_object(), "There isn't enough room to place " + obs[0]->the_short() + " there.\n", ({ })); return 0; } pos = -(pos); } if(pos > 0) { object *already_there; if(member_array(args[1][1..], HANGING_VERBS) != -1) { /* * Make sure they're not trying to 'hang' it in a corner. */ if(is_in_corner(pos)) { if(this_player()) this_player()->add_failed_mess(this_object(), "You may not place $I" + args[1] + " in a " "corner.\n", ({ obs[0] })); return 0; } pos += 10; } already_there = filter(all_inventory(this_object()), (: $1->query_property(PLACEMENT_CURRENT_ROOM_POSITION) == $(pos) :)); if((sizeof(already_there)) && (already_there[0] != obs[0])) { if(this_player()) this_player()->add_failed_mess(this_object(), "$I is already" + already_there[0]->query_property(PLACEMENT_CURRENT_ROOM_VERB) + " " + already_there[0]->query_property(PLACEMENT_CURRENT_ROOM_OTHER) + " " + get_pos_desc(pos) + ".\n", ({ already_there[0] })); return 0; } } if(is_wall_hanging(pos)) { if(member_array(args[2], HANGING_PREPOSITIONS) == -1) { string *preps; string extra = ""; preps = HANGING_PREPOSITIONS; preps = map(preps, (: ("\"" + $1 + "\"") :)); extra = " You may only use " + query_multiple_short(preps) + " as prepositions."; #ifdef PLAYTESTING extra += " Mail suggestions for more to Gototh."; #endif if(this_player()) this_player()->add_failed_mess(this_object(), "You may not place $I"+ args[1] + " \"" + args[2] + "\" " + get_pos_desc(pos) + "." + extra + "\n", ({ obs[0] })); return 0; } } if(is_beside_wall(pos)) { if(member_array(args[2], WALL_PREPOSITIONS) == -1) { string *preps; string extra = ""; preps = WALL_PREPOSITIONS; preps = map(preps, (: ("\"" + $1 + "\"") :)); extra = " You may only use " + query_multiple_short(preps) + " as prepositions."; #ifdef PLAYTESTING extra += " Mail suggestions for more to Gototh."; #endif if(this_player()) this_player()->add_failed_mess(this_object(), "You may not place $I"+ args[1] + " \"" + args[2] + "\" " + get_pos_desc(pos) + "." + extra + "\n", ({ obs[0] })); return 0; } } if(is_in_corner(pos)) { if(member_array(args[2], CORNER_PREPOSITIONS) == -1) { string *preps; string extra = ""; preps = CORNER_PREPOSITIONS; preps = map(preps, (: ("\"" + $1 + "\"") :)); extra = " You may only use " + query_multiple_short(preps) + " as a preposition."; #ifdef PLAYTESTING extra += " Mail suggestions for more to Gototh."; #endif if(this_player()) this_player()->add_failed_mess(this_object(), "You may not place $I"+ args[1] + " \"" + args[2] + "\" " + get_pos_desc(pos) + "." + extra + "\n", ({ obs[0] })); return 0; } } if(!pos) { if(this_player()) this_player()->add_failed_mess(this_object(), "You may not place $I"+ args[1] + " " + args[2] + " the \"" + args[3] + "\".\n", ({ obs[0] })); return 0; } obs[0]->add_property(PLACEMENT_CURRENT_ROOM_VERB, args[1]); obs[0]->add_property(PLACEMENT_CURRENT_ROOM_OTHER, args[2]); obs[0]->add_property(PLACEMENT_CURRENT_ROOM_POSITION, pos); if(environment(obs[0]) != this_object()) { obs[0]->move(this_object()); } if(pos > 0) { if(this_player()) this_player()->add_succeeded_mess(this_object(), "$N $V $I so that it is" + args[1] + " " + args[2] + " " + get_pos_desc(pos) + ".\n", ({ obs[0] })); } else { if(sizeof(things)) { if(this_player()) this_player()->add_succeeded_mess(this_object(), "$N $V $I so that it is" + args[1] + " " + args[2] + " " + things[0]->the_short() + ".\n", ({ obs[0] })); } else { pos = -(pos); if(this_player()) this_player()->add_succeeded_mess(this_object(), "$N $V $I so that it is" + args[1] + " " + args[2] + " " + get_pos_desc(pos) + ".\n", ({ obs[0] })); } } event(this_object(), "save", this_object()); furniture_long = ""; return 1; } /* do_place() */ int get_pos_int(string str) { string dir; int pos_int; dir = explode(str, " ")[0]; dir = EXPAND_EXIT(dir); switch(dir) { case "ceiling" : case "floor" : case "centre" : pos_int = 1; break; case "north" : pos_int = 2; break; case "south" : pos_int = 6; break; case "east" : pos_int = 4; break; case "west" : pos_int = 8; break; case "northeast" : pos_int = 3; break; case "southeast" : pos_int = 5; break; case "southwest" : pos_int = 7; break; case "northwest" : pos_int = 9; break; } return pos_int; } /* get_pos_int() */ /* * The following functions are all geared towards returning * the index of where an object sits in relation to the object * in index position 'pos' in the structure array. */ int find_opposite(int pos, object *structure) { int opp; if(pos == 1 || pos == 11) { return 0; } if((pos > 5 && pos < 10) || (pos > 11 && pos > 15)) { opp = pos - 4; } else { opp = pos + 4; } return opp; } /* find_opposite() */ int find_above(int pos, object *structure) { int above; if(pos < 10) { above = pos + 10; } return above; } /* find_above() */ int find_below(int pos, object *structure) { int below; if(pos > 10) { below = pos - 10; } return below; } /* find_below() */ int find_left(int pos, object *structure) { int left; if(pos == 1 || pos == 11) { return 0; } left = pos - 1; if(left == 1) { left = 9; } if(left == 11) { left = 19; } return left; } /* find_left() */ int find_right(int pos, object *structure) { int right; if(pos == 1 || pos == 11) { return 0; } right = pos + 1; if(right == 10) { right = 2; } if(right == 20) { right = 12; } return right; } /* find_right() */ string query_furniture_ordinal(object ob) { int index; string str_num; object *obs; str_num = ""; obs = match_objects_for_existence(ob->query_plural(), ({ this_object() })); index = member_array(ob, obs); if((index != -1) && (sizeof(obs) != 1)) { // Cheers to Presto for word_ordinal. :) str_num = " " + word_ordinal(index + 1); } if((index != -1) && (sizeof(obs) != 1)) { // Cheers to Presto for word_ordinal. :) str_num = " " + word_ordinal(index + 1); } return str_num; } /* query_furniture_ordinal() */ /* * Should produce "squatting against the eastern wall" */ string object_position_desc(object thing) { string verb; string other; string position; string str; verb = thing->query_property(PLACEMENT_CURRENT_ROOM_VERB); other = thing->query_property(PLACEMENT_CURRENT_ROOM_OTHER); position = get_pos_desc(thing->query_property(PLACEMENT_CURRENT_ROOM_POSITION)); if(verb == "") { str = other + " " + position; } else { str = verb[1..] + " " + other + " " + position; } return str; } /* object_position_desc() */ /* * Returns a description of how two objects are in relation to * each other. */ string describe_relative_position(int index) { string relative_desc; switch(index) { case OPPOSITE : relative_desc = OPPOSITE_DESCS[random(sizeof(OPPOSITE_DESCS))]; break; case ABOVE : relative_desc = ABOVE_DESCS[random(sizeof(ABOVE_DESCS))]; break; case BELOW : relative_desc = BELOW_DESCS[random(sizeof(BELOW_DESCS))]; break; case RIGHT : relative_desc = RIGHT_DESCS[random(sizeof(RIGHT_DESCS))]; break; case LEFT : relative_desc = LEFT_DESCS[random(sizeof(LEFT_DESCS))]; break; } return relative_desc; } /* describe_relative_position() */ /* * Should produce "squats a stove" or "is a stove" */ string object_desc2(object thing) { string verb; string other; string position; string str; mapping allowed_verbs; verb = thing->query_property(PLACEMENT_CURRENT_ROOM_VERB); allowed_verbs = thing->query_allowed_room_verbs(); if (!allowed_verbs) { allowed_verbs = ([ ]); } verb = allowed_verbs[verb[1..]]; if(!verb || verb == "") { verb = "is"; } other = thing->query_property(PLACEMENT_CURRENT_ROOM_OTHER); position = get_pos_desc(thing->query_property(PLACEMENT_CURRENT_ROOM_POSITION)); str = verb + " " + thing->a_short(); return str; } /* object_desc2() */ /* * Should produce "squatting against the eastern wall is a stove" * or "a stove is squatting against the eastern wall". */ string object_desc(object thing) { string str; /* * There may be more ingenious ways of phrasing this. */ switch(random(2)) { case 0 : str = object_position_desc(thing) + " is " + thing->a_short(); break; default : str = object_position_desc(thing) + " is " + thing->a_short(); break; } return str; } /* object_desc() */ /** * @ignore yes * * This function returns an array of the indexes which are * opposite, above, below, right, and left of the object * with the index 'index' in the array 'structure'. If * no object exists in that position a 0 will be inserted. */ int *find_relative_indexes(int index, mixed structure) { int *relatives; relatives = allocate(5); relatives[OPPOSITE] = find_opposite(index, structure); relatives[ABOVE] = find_above(index, structure); relatives[BELOW] = find_below(index, structure); relatives[RIGHT] = find_right(index, structure); relatives[LEFT] = find_left(index, structure); return relatives; } /* find_relative_indexes() */ /** * @ignore yes * * This function generates strings like... "Lying in the centre of * the room is a Klatchian rug near which is an armchair.", "Lying * in the centre of the room is a Klatchian rug around which are * three armchairs.", and "Lying in the centre of the room is a * Klatchian rug around which are two armchairs, a sofa, and a * foot stool." */ string desc_around_with(object ob, object *around) { // don't list items already described. around -= have_described; if(sizeof(around) == 1) { return "$C$" + object_desc(ob) + " " + around[0]->query_property(PLACEMENT_CURRENT_ROOM_OTHER) + " which is " + query_multiple_furniture_short(around) + ". "; } return "$C$" + object_desc(ob) + " around which are " + query_multiple_furniture_short(around) + ". "; } /* desc_around_with() */ /** * @ignore yes * * This function generates strings like... "Near the Klatchian rug * is an armchair.", "Around the Klatchian rug are three armchairs." * and "Around the Klatchian rug are two armchairs, a sofa, and a * foot stool." */ string desc_around_without(object *around, object *structure, int location) { if(location < 0) { location = -(location); } if(!structure[location]) { if(sizeof(around) == 1) { return "$C$" + around[0]->query_property(PLACEMENT_CURRENT_ROOM_OTHER) + " " + get_pos_desc(location) + " is" + query_another(around[0]) + " " + around[0]->query_short() + ". "; } else { return "Around " + get_pos_desc(location) + " are " + query_multiple_furniture_short(around) + ". "; } } else { if(sizeof(around) == 1) { return "$C$" + around[0]->query_property(PLACEMENT_CURRENT_ROOM_OTHER) + " the" + query_furniture_ordinal(structure[location]) + " " + structure[location]->query_short() + " is" + query_another(around[0]) + " " + around[0]->query_short() + ". "; } else { return "Around " + structure[location]->the_short() + " are " + query_multiple_furniture_short(around) + ". "; } } } /* desc_around_without() */ /** * @ignore yes * * This function removes spaces off the end of a string. */ string trim_trailing_spaces(string str) { int i; if(!sizeof(str)) { return str; } for(i = (sizeof(str)) - 1; str[i] == ' '; i--); return str[0..i]; } /* trim_trailing_spaces() */ /** * @ignore yes * * This function chooses an object from an array of indexes. It * it doesn't find one it returns zero. */ int choose_relative(int *relatives, object *structure) { int num, found; int *rands; rands = ({ 0, 1, 2, 3, 4 }); while(sizeof(rands)) { num = rands[random(sizeof(rands))]; if(objectp(structure[relatives[num]])) { if(member_array(structure[relatives[num]], needs_described) != -1) { found = 1; break; } } rands -= ({ num }); } if(found) return num; return -1; } /* choose_relative() */ /** * @ignore yes * * This function returns the objects placed around the specified * object. */ object *get_around(object ob) { object *around; around = filter(all_inventory(this_object()), (: $1->query_property(PLACEMENT_CURRENT_ROOM_POSITION) && $1->query_property(PLACEMENT_CURRENT_ROOM_POSITION) == -($(ob->query_property(PLACEMENT_CURRENT_ROOM_POSITION))) :)); return around; } /* get_around() */ void set_orientation(int num) { orientation = num; } /* set_orientation() */ int query_orientation() { return orientation; } /* set_orientation() */ string wall_or_corner(int pos) { return query_wall_name_pos(pos); } /* wall_or_corner() */ /** * @ignore yes * * This function returns a string describing objects in relation * to each other and the objects placed around them. */ string desc_obs_relatively(int *indexes, object *structure) { int i; int *relatives; string desc; object *around; int rel_num; object rel_ob; desc = ""; while(sizeof(indexes)) { i = indexes[random(sizeof(indexes))]; // Remove object object's index from the indexes array. indexes -= ({ i }); // Make absolutely sure the object hasn't been described before. if(member_array(structure[i], needs_described) == -1) { continue; } // Remove the object from the needs described array. needs_described -= ({ structure[i] }); have_described += ({ structure[i] }); // Find out what we've got relative to us. relatives = find_relative_indexes(i, structure); // Pick one of the things we've got relative to us. rel_num = choose_relative(relatives, structure); if(rel_num == -1) rel_ob = 0; else rel_ob = structure[relatives[rel_num]]; // Find out what we've got around us. around = get_around(structure[i]); // Check if we have an object relative to this object. if(!rel_ob) { // Check if we've got anything placed around this object. if(sizeof(around)) { // Generate a string describing the location of this object // and what has been placed around it. desc += desc_around_with(structure[i], around); // Remove the around objects from the needs described array. needs_described -= around; have_described += around; continue; } else { if(clutter_string(structure[i]) == "") { // Nothing to work with so just describe the object. desc += "$C$" + object_desc(structure[i]) + ". "; } else { desc += "$C$" + object_desc(structure[i]) + " upon which" + clutter_string(structure[i]) + ". "; } continue; } } // Remove the relative object from the needs described array. needs_described -= ({ rel_ob }); have_described += ({ rel_ob }); // Remove the relative object's index from the indexes array. indexes -= ({ relatives[rel_num] }); // Build the sentence described the two objects relations to // each other. desc += "$C$" + object_desc(structure[i]) + " " + describe_relative_position(rel_num) + " " + object_desc2(rel_ob) + ". "; // Check for objects on the first object. if(clutter_string(structure[i]) != "") { desc += "On " + structure[i]->the_short() + clutter_string(structure[i]); if(clutter_string(rel_ob) != "") { // It was here that Gototh realised he was totally pissed, // but carried on regardless. desc += " while on " + rel_ob->the_short() + clutter_string(rel_ob) + ". "; } else { desc += ". "; } } else if(clutter_string(rel_ob) != "") { desc += "On " + rel_ob->the_short() + clutter_string(rel_ob) + ". "; } // Check for objects placed around the first object. if(sizeof(around)) { // Describe objects around the first object. desc += desc_around_without(around, structure, i); needs_described -= around; have_described += around; continue; } // Find out what objects are around the second object. around = get_around(rel_ob); // Check for objects placed around the second object. if(sizeof(around)) { // Describe objects around the second object. desc += desc_around_without(around, structure, relatives[rel_num]); needs_described -= around; have_described += around; continue; } } return desc; } /* desc_objects_relatively() */ int room_order(object *obs1, object *obs2, object *structure) { int pos1, pos2; object ob1, ob2; int order1, order2; pos1 = -(obs1[0]->query_property(PLACEMENT_CURRENT_ROOM_POSITION)); pos2 = -(obs2[0]->query_property(PLACEMENT_CURRENT_ROOM_POSITION)); if (pos1 < 0) { ob1 = 0; } else { ob1 = structure[pos1]; } if (pos2 < 0) { ob2 = 0; } else { ob2 = structure[pos2]; } if(!ob1 || !ob2) { return 0; } if(ob1->query_plural() != ob2->query_plural()) { return 0; } order1 = member_array(ob1, match_objects_for_existence(ob1->query_plural(), ({ this_object() }))); order2 = member_array(ob2, match_objects_for_existence(ob2->query_plural(), ({ this_object() }))); if(order1 > order2) { return 1; } return -1; } /* room_order() */ /** * @ignore yes * * This function describes objects which have been placed around * room locations or objects which are no longer there. */ string describe_remaining_objects(object *structure) { int pos; string desc; object *group; mixed *orphans; desc = ""; if(sizeof(needs_described)) { // Group them into arrays depending on where they're positioned. orphans = unique_array(needs_described, (: $1->query_property(PLACEMENT_CURRENT_ROOM_POSITION) :)); if(sizeof(orphans)) { orphans = sort_array(orphans, (: room_order($1, $2, $(structure)) :)); foreach(group in orphans) { // Find out where they're positioned. pos = group[0]->query_property(PLACEMENT_CURRENT_ROOM_POSITION); // Described where they're positioned. desc += desc_around_without(group, structure, pos); // Remove them from the need described array. needs_described -= group; have_described += group; } } } return desc; } /* describe_remaining_objects() */ string get_group_verb(object *obs) { mixed *verbs; verbs = map(obs, (: $1->query_property(PLACEMENT_CURRENT_ROOM_VERB) :)); verbs = unique_array(verbs, (: $1 :)); if(sizeof(verbs) == 1) { return verbs[0][0]; } return ""; } /* query_same_verb() */ string get_group_other(object *obs) { mixed *others; others = map(obs, (: $1->query_property(PLACEMENT_CURRENT_ROOM_OTHER) :)); others = unique_array(others, (: $1 :)); if(sizeof(others) == 1) { return others[0][0]; } // Can't think of a good way to do this. return others[random(sizeof(others))][0]; } /* query_same_other() */ string describe_plural_objects(int *indexes, object *structure) { int i, j; int flag; int rel_num; int used_pos; mixed *things; string desc = ""; string *strs; strs = ({ }); things = ({ }); // Check for multiple objects with the same plural. for(i = 0; i < sizeof(indexes); i++) { things += ({ structure[indexes[i]] }); } things = unique_array(things, (: $1->query_plural() :)); things = filter(things, (: sizeof($1) > 1 :) ); if(sizeof(things) > 0) { // Deal with these and remove them from the list. for(i = 0; i < sizeof(things); i++) { needs_described -= things[i]; have_described += things[i]; // Find out if there's four of them. if(sizeof(things[i]) == 4) { int they_are = 1; int *rel_nums; int num; rel_nums = map(things[i], (: $1->query_property(PLACEMENT_CURRENT_ROOM_POSITION) :)); #ifdef DEBUG debug_printf("rel_nums: %O", rel_nums); #endif // Find out if they're in each corner. foreach(num in rel_nums) if(is_in_corner(num)) they_are = 0; if(they_are) { desc += "$C$" + things[i][0]->query_plural() + " are" + get_group_verb(things[i]) + " " + get_group_other(things[i]) + " each " + wall_or_corner(rel_nums[0]) + ". "; #ifdef DEBUG debug_printf("4 corner: %s\n", desc); #endif continue; } they_are = 1; // Find out if they're at each wall. foreach(num in rel_nums) { if(is_beside_wall(num)) { they_are = 0; } } if(they_are) { desc += "$C$" + things[i][0]->query_plural() + " are" + get_group_verb(things[i]) + " " + get_group_other(things[i]) + " each " + wall_or_corner(3) + ". "; continue; } } if(sizeof(things[i]) == 3) { int *rel_nums; rel_nums = map(things[i], (: $1->query_property(PLACEMENT_CURRENT_ROOM_POSITION) :)); rel_nums = sort_array(rel_nums, 1); if(rel_nums[2] == 9) { rel_nums -= ({ 9 }); rel_nums = ({ 1 }) + rel_nums; } if(is_beside_wall(rel_nums[1]) && rel_nums[0] == rel_nums[1] - 1 && rel_nums[2] == rel_nums[1] + 1) { desc += "$C$" + query_multiple_short(things[i]) + " are" + get_group_verb(things[i]) + " beside each other " + structure[rel_nums[1]]->query_property(PLACEMENT_CURRENT_ROOM_OTHER) + " the " + get_pos_desc(rel_nums[1]) + ". "; continue; } } // Find out if there's two of them. if(sizeof(things[i]) == 2) { // Find out if they're positioned relative to each other. rel_num = member_array(get_structure_index(things[i][1]), find_relative_indexes(get_structure_index(things[i][0]), structure)); if(rel_num != -1) { desc += "$C$" + query_multiple_short(things[i]); switch(rel_num) { case OPPOSITE : desc += " face each other across the room. "; continue; break; case RIGHT : case LEFT : desc += " are" + get_group_verb(things[i]) + " beside each other "; break; default : break; } if(!orientation) { // Check if it's odd. if(get_structure_index(things[i][0]) & 1) { used_pos = things[i][1]->query_property(PLACEMENT_CURRENT_ROOM_POSITION); } else { used_pos = things[i][0]->query_property(PLACEMENT_CURRENT_ROOM_POSITION); } } else { // Check if it's odd. if(get_structure_index(things[i][0]) & 1) { used_pos = things[i][0]->query_property(PLACEMENT_CURRENT_ROOM_POSITION); } else { used_pos = things[i][1]->query_property(PLACEMENT_CURRENT_ROOM_POSITION); } } desc += structure[used_pos]->query_property(PLACEMENT_CURRENT_ROOM_OTHER) + " the " + get_pos_desc(used_pos) + ". "; continue; } } desc += "$C$" + query_num(sizeof(things[i]), 4) + " " + things[i][0]->query_plural() + " are positioned " "around the room. "; #ifdef DEBUG debug_printf("2 items: %s\n", desc); #endif // If there's more than four say no more about them. if(sizeof(things[i]) > 4) { flag = 1; } else { desc += "They are "; } for(j = 0; j < sizeof(things[i]); j++) { if(!flag) { strs += ({ object_position_desc(things[i][j]) }); } indexes -= ({ things[i][j]->query_property(PLACEMENT_CURRENT_ROOM_POSITION) }); } if(!flag) { desc += query_multiple_short(strs)+". "; #ifdef DEBUG debug_printf("position: %s [%s]\n", query_multiple_short(strs), desc); #endif strs = ({ }); } } } return desc; } /* describe_plural_objects() */ string furniture_long() { string desc, d1, d2, d3; object *structure; int i, *indexes; if(furniture_long != "") { return furniture_long; } structure = build_structure(); indexes = ({ }); // Build an array of indexes where objects are stored. for(i = 0; i < sizeof(structure); i++) { if(objectp(structure[i])) { indexes += ({ i }); } } needs_described = filter(all_inventory(this_object()), (: $1->query_property(PLACEMENT_CURRENT_ROOM_POSITION) :)); have_described = ({ }); d1 = describe_plural_objects(indexes, structure); d2 = desc_obs_relatively(indexes, structure); d3 = describe_remaining_objects(structure); #ifdef DEBUG debug_printf("1: %s\n2:%s\n3:%s\n",d1, d2, d3); #endif desc = d1 + d2 + d3; if(desc == "") desc = "$C$" + this_object()->the_short() + "$C$" + " is completely empty."; furniture_long = trim_trailing_spaces(desc); return furniture_long; } /* furniture_long() */ /** * @ignore yes * * This function returns a string describing the location of * the index in the structure array. */ string get_pos_desc(int pos_int) { string str; switch(pos_int) { case 1 : str = "the centre of " + this_object()->the_short(); break; case 2 : case 12 : str = "the north " + wall_or_corner(pos_int); break; case 3 : case 13 : str = "the northeast " + wall_or_corner(pos_int); break; case 4 : case 14 : str = "the east " + wall_or_corner(pos_int); break; case 5 : case 15 : str = "the southeast " + wall_or_corner(pos_int); break; case 6 : case 16 : str = "the south " + wall_or_corner(pos_int); break; case 7 : case 17 : str = "the southwest " + wall_or_corner(pos_int); break; case 8 : case 18 : str = "the west " + wall_or_corner(pos_int); break; case 9 : case 19 : str = "the northwest " + wall_or_corner(pos_int); break; case 11 : str = "the centre of the ceiling"; break; } return str; } /* get_pos_desc() */ /** * @ignore yes * * This builds a structure based on what things are in the room so * that it can be determined where the objects are in relation to * one another. * * 19---12---13 * |\ \ * | 18 11 14 * | \ \ * | 17---16---15 * | * 9----2----3 * \ \ * 8 1 4 * \ \ * 7----6----5 */ object *build_structure() { int i; object *positions, *obs, *dummies; positions = allocate(20); obs = all_inventory(this_object()); for(i = 1; i < sizeof(positions); i++) { dummies = filter(obs, (: $1->query_property(PLACEMENT_CURRENT_ROOM_POSITION) == $(i) :)); if(sizeof(dummies)) { positions[i] = dummies[0]; } } return positions; } /* build_structure() */ /** * @ignore yes * * This function returns the position of the object in the * structure array. */ int get_structure_index(object ob) { return member_array(ob, build_structure()); } /* get_structure_index() */ /** * @ignore yes * * This function is used from describing the individual walls, * corners, floor and ceiling for use in add_item()s. */ string desc_surface(string dir) { int i; int pos; int *indexes; int current_pos; string desc; object *structure; object *around; structure = build_structure(); needs_described = ({ }); have_described = ({ }); if(dir == "ceiling") { pos = 11; } else { pos = get_pos_int(dir); } desc = ""; indexes = ({ }); for(i = 1; i < sizeof(structure); i++) { if(!objectp(structure[i])) { continue; } current_pos = structure[i]->query_property(PLACEMENT_CURRENT_ROOM_POSITION); if(current_pos != pos) { if(pos != 1 && pos != 11) { if((current_pos != pos + 10) && (current_pos != pos - 10)) { structure[i] = 0; } else { needs_described += ({ structure[i] }); indexes += ({ i }); } } } else { needs_described += ({ structure[i] }); around = get_around(structure[i]); if(around) { needs_described += around; } indexes += ({ i }); } } desc += desc_obs_relatively(indexes, structure); return desc; } /* desc_surface() */ void displace_object(object ob) { // Make sure it is in the room first. if (ob->query_property(PLACEMENT_CURRENT_ROOM_POSITION)) { ob->remove_property(PLACEMENT_CURRENT_ROOM_VERB); ob->remove_property(PLACEMENT_CURRENT_ROOM_OTHER); ob->remove_property(PLACEMENT_CURRENT_ROOM_POSITION); // Make sure the long is fixed up. furniture_long = ""; } } /* displace_object() */ int do_displace(object *obs) { if(this_player() && !this_object()->test_occupier(this_player()->query_name()) && !this_player()->query_creator()) { this_player()->add_failed_mess(this_object(), "You don't own this house.\n"); return 0; } if(sizeof(obs) > 1) { this_player()->add_succeeded_mess(this_object(), "You may only displace one thing at a time.\n", ({ })); return 1; } if(!obs[0]->query_property(PLACEMENT_CURRENT_ROOM_POSITION)) { this_player()->add_failed_mess(this_object(), "$C$" + obs[0]->the_short() + " is not placed.\n", ({ })); return 0; } if(obs[0]->query_property(PLACEMENT_IMMOVABLE)) { this_player()->add_failed_mess(this_object(), "Try as you might you cannot move $I.\n", ({ obs[0] })); return 0; } displace_object(obs[0]); this_player()->add_succeeded_mess(this_object(), "$N drag$s " + obs[0]->a_short() + " out of position.\n", ({ })); furniture_long = ""; return 1; } /* do_displace() */ /** * @ignore yes * * This removes the placement properties from an object when it is * removed from the room. */ void event_exit(object ob, string, object) { displace_object(ob); } /* event_exit() */ int test_remove(object thing, int flag, mixed dest) { if(thing->query_property(PLACEMENT_IMMOVABLE)) return 0; // Let stuff that's kind of placed (eg. curtains) be notified when they // are removed. if(thing->query_property(PLACEMENT_CURRENT_ROOM_HIDDEN)) thing->removed(); return 1; }