/* Do not remove the headers from this file! see /USAGE for more info. */ /* * Written by Tigran@Illusions,Lima Bean. 2/6/99 * The purpose of this inheritable is to make it easier for writing complex * exit type objects. This is intended to be used by vehicles, and furniture * and such things as that. * * Unlike SIMPLE_EXIT_OBJ only one exit can be assigned to COMPLEX_EXIT. * This will serve to allow more full utilization of OBJ and all of the * features of the parser. */ inherit "/std/classes/move"; /* * VARIABLES */ private mixed hidden; /* Is this a hidden exit? */ private mapping methods=([]); /* All of the data for methods of this exit */ private mixed obvious_desc; /* The obvious exit description */ private mixed base=this_object();/* The base directory to determine exit * destinations from. This can be either a * string, object, or function pointer */ /* * PROTOTYPES */ string query_primary_id(); string array list_methods(); string the_short(); string query_primary_name(); /* * FUNCTIONS */ //:FUNCTION is_exit //Return whether or not the object is an exit. int is_exit() { return 1; } private string array all_methods_matched(string method) { string verb; string prep; string array matches=({}); /* The first word of the method is expected to be the verb, the rest is * a preposition, if it exists. If it is only one word, that is the verb */ if(sscanf(method,"%s %s",verb,prep)==2) { /* Translate the preposition to it's base form */ prep=PREPOSITION_D->translate_preposition(prep); /* Put the method back together */ method=sprintf("%s %s",verb,prep); } /* Must sweep through the methods to find matches.*/ foreach(string test_method in METHOD_D->list_methods()) { if(member_array(method,METHOD_D->list_method_equivalents(test_method))!=-1) { matches+=({test_method}); } } return matches; } /* Return an array of all of the methods which might match the method passed * to the function. It breaks down the preposition to it's lowest form before * making its determination, allowing for the fullest range of possible forms. */ private string array methods_matched(string method) { string array matches=all_methods_matched(method); /* We only want to return those matches which are also methods on this object * so eliminate those that aren't */ matches=filter(matches, (: member_array($1,list_methods() ) > -1 :) ); /* Return the matches */ return matches; } //:FUNCTION prime_method //This function merely returns nonzero if the argument is a prime method. More //specifically this means that the method is one of the root methods that //others can be built off of. int prime_method(string method) { string verb; string prep; string array matches=({}); /* The first word of the method is expected to be the verb, the rest is * a preposition, if it exists. If it is only one word, that is the verb */ if(sscanf(method,"%s %s",verb,prep)==2) { /* Translate the preposition to it's base form */ prep=PREPOSITION_D->translate_preposition(prep); /* Put the method back together */ method=sprintf("%s %s",verb,prep); } return member_array(method,METHOD_D->list_methods())>-1; } //:FUNCTION set_base //Set the base directory from which relative paths are figured //The argument can be a string (directory), object, or function pointer //evaluating to one of these. void set_base(mixed foo) { base=foo; } //:FUNCTION query_base //Return the evaluated directory string or object from which relative paths //are figured mixed query_base() { return evaluate(base); } /********* Hidden exits ********/ //:FUNCTION set_hidden //Set whether or not the exit is going to show up in the obvious exits list. //Possible arguments are an int, or a function pointer. void set_hidden(int i) { hidden=i; } //:FUNCTION query_hidden //Return 1 if the exit is not to show up as an obvious exit, or 0 int query_hidden() { /* Exits are also hidden if there is no "go" method */ if(member_array("go",keys(methods))==-1) return 0; return !!evaluate(hidden); } /******** Directions ********/ //:FUNCTION set_obvious_description //Set the obvious exit description. This is the string that will appear in //the obvious exits line of a room description. It can be either a function //pointer or a string. void set_obvious_description(mixed desc) { obvious_desc=desc; } //:FUNCTION query_obvious_description //Return the unevaluated obvious description. mixed query_obvious_description() { /* If there is no go method, there is no description to be obvious */ if(member_array("go",keys(methods))==-1) return 0; return obvious_desc?obvious_desc:query_primary_name(); } //:FUNCTION query_direction //Return the diretion of the exit, that is to show up for obvious exits. string query_direction() { string tmp; if(tmp=query_obvious_description()) return tmp; return query_primary_id(); } /******** Destination ********/ //:FUNCTION set_method //Setup a go method. The first argument is the verb to be used, the second //argument is the destination to move the body to when invoked successfully, //the third argument is the check to be performed before invoking the method //the fourth argument is an array of possible exit messages, and the fifth //argument is an array of possible enter messages. Only the first two //arguments are required, the rest are optional. varargs void set_method(string method,mixed destination,mixed checks,mixed array exit_messages,mixed array enter_messages) { class move_data new_method=new(class move_data); string array matches=all_methods_matched(method); if(sizeof(matches)!=1&&!prime_method(method)) { error(sprintf("Cannot set method '%s'. Method is either too ambiguous " "or should be added to METHOD_D", method)); } if(destination) new_method->destination=destination; if(checks) new_method->checks=checks; else new_method->checks=1; if(arrayp(exit_messages)) new_method->exit_messages=exit_messages; else if(stringp(exit_messages)) new_method->exit_messages=({exit_messages}); else new_method->exit_messages=({}); if(arrayp(enter_messages)) new_method->enter_messages=enter_messages; else if(stringp(exit_messages)) new_method->enter_messages=({enter_messages}); else new_method->enter_messages=({}); new_method->exit_dir=(:the_short:); new_method->source=this_object(); methods[matches[0]]=new_method; } //:FUNCTION add_method //Add a go method. The first argument is the verb to be used, the second //argument is the destination to move the body to when invoked successfully, //the third argument is the check to be performed before invoking the method //the fourth argument is an array of possible exit messages, and the fifth //argument is an array of possible enter messages. Only the first two //arguments are required, the rest are optional. varargs void add_method(string method,mixed destination,mixed checks,mixed array exit_messages,mixed array enter_messages) { set_method(method,destination,checks,exit_messages,enter_messages); } //:FUNCTION remove_method //Remove a go method from the exit void remove_method(string method) { map_delete(methods,method); } //:FUNCTION list_methods //Return a list of arrays of all of the current go methods of the exit string array list_methods() { return keys(methods); } //:FUNCTION has_method //Return true if the method exists int has_method(string method) { string array applicable_methods=methods_matched(method); if(!sizeof(applicable_methods)) return 0; return 1; } //:FUNCTION set_method_checks //Set the checks for a method. The checks can be either an int, function //pointer, or string. void set_method_checks(string method,mixed checks) { string array applicable_methods=methods_matched(method); if(!sizeof(applicable_methods)) error("Attempted to set_method_checks on a nonexistant method"); if(sizeof(applicable_methods)>1) error("Attempted to set_method_checks on an ambiguous method"); methods[applicable_methods[0]]->checks=checks; } //:FUNCTION query_method_checks //Return the evaluated checks on the given method. //If 1 is returned, the checks is successful, 0 - the checks is a failure and //the parser will generate an error (maybe), else, a string should be returned //which is the error message received by the body. mixed query_method_checks(string method) { string array applicable_methods=methods_matched(method); if(!sizeof(applicable_methods)) error("Attempted to query_method_checks on a nonexistant method"); if(sizeof(applicable_methods)>1) error("Attempted to query_method_checks on an ambiguous method"); return evaluate(methods[applicable_methods[0]]->checks, query_obvious_description(), this_body()); } //:FUNCTION set_method_destination //Set the destination for a given method //The first argument is the method to have the destination assigned, and //the second argument is either a string or function pointer which will return //a string. void set_method_destination(string method,mixed destination) { string array applicable_methods=methods_matched(method); if(!sizeof(applicable_methods)) error("Attempted to set_method_destination on a nonexistant method"); if(sizeof(applicable_methods)>1) error("Attempted to set_method_destination on an ambiguous method"); methods[applicable_methods[0]]->destination=destination; } //:FUNCTION query_method_destination //Return the evaluated destination for the given method. //The argument is the method being checked mixed query_method_destination(string method) { string array applicable_methods=methods_matched(method); mixed ret; if(!sizeof(applicable_methods)) error("Attempted to query_method_destination on a nonexistant method"); if(sizeof(applicable_methods)>1) error("Attempted to query_method_destination on an ambiguous method"); ret=evaluate(methods[applicable_methods[0]]->destination); if(stringp(ret)) return absolute_path(ret,query_base()); return ret; } //:FUNCTION set_method_enter_messages //Set the enter messages to be used by the given method. //Acceptable arguments are strings, or function pointers, or an array of //either (mixed is acceptable) //The method is to be seen by the bodies in the room that the body is entering varargs void set_method_enter_messages(string method,mixed array messages...) { string array applicable_methods=methods_matched(method); if(!sizeof(applicable_methods)) error("Attempted to set_method_enter_messages on a nonexistant method"); if(sizeof(applicable_methods)>1) error("Attempted to set_method_enter_messages on an ambiguous method"); methods[applicable_methods[0]]->enter_messages=flatten_array(messages); } //:FUNCTION add_method_enter_messages //Add the enter messages to be used by the given method. //Acceptable arguments are strings, or function pointers, or an array of //either (mixed is acceptable) //The method is to be seen by the bodies in the room that the body is entering varargs void add_method_enter_messages(string method,mixed messages...) { string array applicable_methods=methods_matched(method); if(!sizeof(applicable_methods)) error("Attempted to add_method_enter_messages on a nonexistant method"); if(sizeof(applicable_methods)>1) error("Attempted to add_method_enter_messages on an ambiguous method"); methods[applicable_methods[0]]->enter_messages+=flatten_array(messages); } //:FUNCTION remove_method_enter_messages //Remove the enter messages to be used by the given method. //Acceptable arguments are strings, or function pointers, or an array of //either (mixed is acceptable) //The method is to be seen by the bodies in the room that the body is entering varargs void remove_method_enter_messages(string method,mixed messages...) { string array applicable_methods=methods_matched(method); if(!sizeof(applicable_methods)) error("Attempted to remove_method_enter_messages on a nonexistant method"); if(sizeof(applicable_methods)>1) error("Attempted to remove_method_enter_messages on an ambiguous method"); methods[applicable_methods[0]]->enter_messages-=flatten_array(messages); } //:FUNCTION query_method_enter_message //Return a random method enter message //The method is to be seen by the bodies in the room that the body is entering string query_method_enter_message(string method) { string array applicable_methods=methods_matched(method); mixed ret; if(!sizeof(applicable_methods)) error("Attempted to query_method_enter_message on a nonexistant method"); if(sizeof(applicable_methods)>1) error("Attempted to query_method_enter_message on an ambiguous method"); if(!sizeof(methods[applicable_methods[0]]->enter_messages)) return; ret=methods[applicable_methods[0]]->enter_messages; /* We know that ret has to be an array at this point, select one element * randomly */ ret=ret[random(sizeof(ret))]; /* Evaluate it on the chance it's a function pointer */ ret=evaluate(ret); return ret; } //:FUNCTION list_method_enter_messages //Return an array of the method's enter messages mixed array list_method_enter_messages(string method) { string array applicable_methods=methods_matched(method); if(!sizeof(applicable_methods)) error("Attempted to list_method_enter_messages on a nonexistant method"); if(sizeof(applicable_methods)>1) error("Attempted to list_method_enter_messages on an ambiguous method"); return methods[applicable_methods[0]]->enter_messages; } //:FUNCTION set_method_exit_messages //Set the exit messages to be used by the given method. //Acceptable arguments are strings, or function pointers, or an array of //either (mixed is acceptable) //The method is to be seen by the bodies in the room that the body is exiting varargs void set_method_exit_messages(string method,mixed messages...) { string array applicable_methods=methods_matched(method); if(!sizeof(applicable_methods)) error("Attempted to set_method_exit_messages on a nonexistant method"); if(sizeof(applicable_methods)>1) error("Attempted to set_method_exit_messages on an ambiguous method"); methods[applicable_methods[0]]->exit_messages=flatten_array(messages); } //:FUNCTION add_method_exit_messages //Add the exit messages to be used by the given method. //Acceptable arguments are strings, or function pointers, or an array of //either (mixed is acceptable) //The method is to be seen by the bodies in the room that the body is exiting varargs void add_method_exit_messages(string method,mixed messages...) { string array applicable_methods=methods_matched(method); if(!sizeof(applicable_methods)) error("Attempted to add_method_exit_messages on a nonexistant method"); if(sizeof(applicable_methods)>1) error("Attempted to add_method_exit_messages on an ambiguous method"); methods[applicable_methods[0]]->exit_messages+=flatten_array(messages); } //:FUNCTION remove_method_exit_messages //Remove the exit messages to be used by the given method. //Acceptable arguments are strings, or function pointers, or an array of //either (mixed is acceptable) //The method is to be seen by the bodies in the room that the body is exiting varargs void remove_method_exit_messages(string method,mixed messages...) { string array applicable_methods=methods_matched(method); if(!sizeof(applicable_methods)) error("Attempted to remove_method_exit_messages on a nonexistant method"); if(sizeof(applicable_methods)>1) error("Attempted to remove_method_exit_messages on an ambiguous method"); methods[applicable_methods[0]]->exit_messages-=flatten_array(messages); } //:FUNCTION query_method_exit_messages //Return a random method exit message //The method is to be seen by the bodies in the room that the body is exiting string query_method_exit_message(string method) { string array applicable_methods=methods_matched(method); mixed ret; if(!sizeof(applicable_methods)) error("Attempted to query_method_exit_message on a nonexistant method"); if(sizeof(applicable_methods)>1) error("Attempted to query_method_exit_message on an ambiguous method"); if(!sizeof(methods[applicable_methods[0]]->exit_messages)) return; ret=methods[applicable_methods[0]]->exit_messages; /* We know that ret has to be an array at this point, select one element * randomly */ ret=ret[random(sizeof(ret))]; /* Evaluate it on the chance it's a function pointer */ ret=evaluate(ret); return ret; } //:FUNCTION list_method_exit_messages //Return an array of the method's exit messages mixed array list_method_exit_messages(string method) { string array applicable_methods=methods_matched(method); if(!sizeof(applicable_methods)) error("Attempted to list_method_exit_messages on a nonexistant method"); if(sizeof(applicable_methods)>1) error("Attempted to list_method_exit_messages on an ambiguous method"); return methods[applicable_methods[0]]->exit_messages; } /* * Parser Rules */ mixed complex_exit_direct_verb_rule(string verb,string rule,mixed args...) //second_part,mixed third) { string prep; string array matches; string method; /* The first argument is always the verb used. */ /* The second argument is the rule used. For our purposes it should always * be WRD OBJ or OBJ, but lets make sure, just in case.*/ if(rule!="WRD OBJ"&&rule!="OBJ") return 0; // Commenting this out. A direct_verb_rule() check should not error on // the format of a particular rule, especially if no previous // checks eliminated that kind of rule. -- Marroc // error(sprintf("Cannot properly parse the rule in complex_exit_direct_verb_rule().\nUnknown Rule: %s",rule)); if(stringp(args[0])) method=sprintf("%s %s",verb,args[0]); else method=verb; /* Get all possible matches */ matches=methods_matched(method); /* If there are no matches, fail. */ if(sizeof(matches)==0) return 0; /* If there are more than one match, it is ambiguous and we can't determine * which method to use */ if(sizeof(matches)>1) return sprintf("Unable to determine whether you want to %s the %s", format_list(matches,"or"), the_short()); /* Now that the failures have been eliminated, return the check */ return query_method_checks(method); } int complex_exit_do_verb_rule(string verb,string rule,mixed args...) { class move_data data; string prep; string match; string method; mixed dest; object sibling; /* For our purposes the rule should always be WRD OBJ or OBJ or no rule at all. -- Check for no rule first.*/ /* Make a method out of it */ if(rule=="WRD OBJ") { args[0]=PREPOSITION_D->translate_preposition(args[0]); method=sprintf("%s %s",verb,args[0]); } else if(rule=="WRD") { method=sprintf("%s %s",verb,args[0]); } else method=verb; /* We know now that there can be only one match, or it would not have * passed the direct_verb_rule */ match=methods_matched(method)[0]; sscanf(match,"%s %s",method,prep); dest=query_method_destination(match); /* Default to everything that's in the method. */ data=copy(methods[match]); /* We want to do some operation on these */ data->destination=objectp(dest)?file_name(query_method_destination(match)):dest; data->relation=prep; data->exit_messages=query_method_exit_message(match); /* Fetch enter msg from sibling, if one is found */ if (sibling = this_object()->get_sibling()) data->enter_messages = sibling->query_method_enter_message(match); else data->enter_messages = query_method_enter_message(match); /* Set up the through object for M_SMARTMOVE */ data->through = sibling; data->who=this_body(); /* It is now time to move the body to the destination */ return this_body()->move_to(data); } string stat_me() { string ret="Methods: \n"; foreach(string met in list_methods()) { ret=sprintf("%s\t%O: Destination - %O\n", ret, met, query_method_destination(met)); } return ret; } mapping lpscript_attributes() { return ([ "hidden" : ({LPSCRIPT_INT, "setup", "set_hidden" }), "brief" : ({LPSCRIPT_STRING, "setup","set_obvious_description"}), "method" : ({LPSCRIPT_STRING, "setup", "set_method" }), "destination" : ({LPSCRIPT_TWO, (: ({ "setup", "set_method_destination(\""+ $1 + "\",\""+ $2[0] + "\")" }) :) }), "check" : ({ LPSCRIPT_TWO, (: ({ "setup", "set_method_check(\"" + $1 + "\",\"" + $2[0] + "\")" }) :) }), "exit_message" : ({LPSCRIPT_TWO, (: ({ "setup", "add_method_exit_messages(\"" + $1 + "\", \"" + $2[0] + "\")" }) :) }), "enter_message" : ({LPSCRIPT_TWO, (: ({ "setup", "add_method_enter_messages(\"" + $1 + "\", \"" + $2[0] + "\")" }) :) }), ]); }