* Width() and height() take optional second argument for defaults. [RLB]
* Unique() removes contiguous duplicates in lists. [RLB]
* NextDbref() now returns the next object to be created. [RLB]
+ * Added Ulambda(). [RLB]
+ * Add Speak(). [RLB]
+ * Added namelist(). [RLB]
+ * Added stringsecs(). [RLB]
Dbref functions return a dbref or list of dbrefs related to some value
on an object.
- children() con() entrances() exit() followers()
- following() home() lcon() lexits() loc()
- locate() lparent() lplayers() lsearch() lvcon()
- lvexits() lvplayers() next() num() owner()
- parent() pmatch() rloc() rnum() room()
- where() zone()
-
+ children() con() entrances()
+ exit() followers() following()
+ home() lcon() lexits()
+ loc() locate() lparent()
+ lplayers() lsearch() lvcon()
+ lvexits() lvplayers() namelist()
+ next() num() owner()
+ parent() pmatch() rloc()
+ rnum() room() where()
+ zone()
+
See also: DBREF
& Information functions
Information functions return values related to objects or the game.
convsecs() convutcsecs() convtime() ctime() etimefmt()
isdaylight() mtime() restarttime() secs() starttime()
- time() timefmt() timestring() utctime()
+ stringsecs() time() timefmt() timestring() utctime()
& Utility functions
These functions don't quite fit into any other category.
that it will continue to do this for the time being. Don't rely on it.
Related functions: FULLNAME(), ACCNAME(), INAME()
+& NAMELIST()
+ namelist(<player-list>)
+
+ Namelist takes a list of players of the form used by the page
+ command and returns a corresponding list of dbrefs.
+
+ >"[namelist(#1 Javelin "ringo spar" bogus)]
+ You say, "#1 #7 #56 #-1"
+
& NAMEGRAB()
& NAMEGRABALL()
namegrab(<dbref list>,<name>)
Prints <number> number of spaces. Useful for times when you want to
be able to use lots of spaces to separate things. For example,
"a[space(5)]b would print, "Amberyl says, "a b"".
+
+& SPEAK()
+ speak(<speaker>, <string>[, <say string>
+ [, [<transform obj>/]<transform attr>
+ [, [<isnull obj>/]<isnull attr>[, <open>[, <close>]]]]])
+
+ This function is used to format speech-like constructs, and is
+ capable of transforming text within a speech string; it is useful for
+ implementing "language code" and the like.
+
+ When only <speaker> and <string> are given, this function formats
+ <string> as if it were speech from <speaker>, as follows.
+
+ If <string> is... the resulting string is...
+ :<pose> <speaker's name> <pose>
+ : <pose> <speaker's name><pose>
+ ;<pose> <speaker's name><pose>
+ |<emit> <emit>
+ "<speech> <speaker's name> says, "<speech>"
+ <speech> <speaker's name> says, "<speech>"
+
+ If <say string> is specified, it is used instead of "says," / "says".
+
+ Continued in 'help Speak2'.
+& SPEAK2
+
+ Examples:
+
+ > say [name(me)]
+ You say, "Wizard"
+ > @emit [speak(me, :tests.)]
+ Wizard tests.
+ > @emit [speak(me, : 's testing.)]
+ Wizard's testing.
+ > @emit [speak(me, ;'s testing.)]
+ Wizard's testing.
+ > @emit [speak(me, |Test.)]
+ Test.
+ > @emit [speak(me, "Test.)]
+ Wizard says, "Test."
+ > @emit [speak(me, Test.)]
+ Wizard says, "Test."
+ > @emit [speak(me, Test., yells:)]
+ Wizard yells: "Test."
+
+ Continued in 'help Speak3'.
+
+& SPEAK3
+ If <transform> is specified (an object/attribute pair or attribute,
+ as with map() and similar functions), the speech portions of <string>
+ are passed through the transformation function.
+
+ Speech is delimited by double-quotes (i.e., "text"), or by the
+ specified <open> and <close> strings. For instance, if you wanted
+ <<text>> to denote text to be transformed, you would specify <open>
+ as << and close as >> in the function call. Only the portions of the
+ string between those delimiters are transformed. If <close> is not
+ specified, it defaults to <open>.
+
+ The transformation function receives the speech text as %0, the
+ dbref of <speaker> as %1, and the speech fragment number as %2.
+ For non-say input strings (i.e., for an original <string> beginning
+ with the :, ;, or | tokens), fragments are numbered starting with 1;
+ otherwise, fragments are numbered starting with 0. (A fragment is
+ a chunk of speech text within the overall original input string.)
+
+ Continued in 'help Speak4'.
+
+& SPEAK4
+ Examples:
+
+ > @va me = "Fragment %2 is: %0"
+ > @emit speak(me, test, ,va)
+ Wizard says, "Fragment 0 is: test"
+ > @emit speak(me, "test, ,va)
+ Wizard says, "Fragment 0 is: test"
+ > @emit speak(me, "test, yells:, va)
+ Wizard yells: "Fragment 0 is: test"
+ > @emit speak(me, :tests. "Hi.", ,va)
+ Wizard tests. "Fragment 1 is: Hi."
+ > @emit speak(me, : 's testing. "Hi.", ,va)
+ Wizard's testing. "Fragment 1 is: Hi."
+ > @emit speak(me, ;'s testing. "Hi.", ,va)
+ Wizard's testing. "Fragment 1 is: Hi."
+ > @emit speak(me, |This is a test. "Hi.", ,va)
+ This is a test. "Fragment 1 is: Hi."
+ > @emit speak(me, :tests. "Hi." And... "Bye." The end., ,va)
+ Wizard tests. "Fragment 1 is: Hi." And... "Fragment 2 is: Bye." The end.
+ > @emit speak(me, :tests. "Hi." And... <<Bye.>> The end., ,va, , <<, >>)
+ Wizard tests. "Hi." And... "Fragment 1 is: Bye." The end.
+
+ Continued in 'help Speak5'.
+& SPEAK5
+ If the result of transforming a given speech fragment is a null string,
+ and <isnull> is specified (an object/attribute pair or attribute),
+ that function is used evaluate an alternative result, with %0 as
+ the dbref of <speaker>, and %1 as the speech fragment number.
+
+ The <isnull> functionality can be useful for gracefully handling cases
+ where speech may be processed down to nothing, such as with language
+ code where no words are successfully translated.
+
+ Consider this example, where the speech string may be randomly removed:
+
+ > &MUTTER_FN me = [ifelse(rand(2),"%0",)]
+ > &NONE_FN me = [capstr(subj(%0))] mutters something.
+ > @emit speak(me, :tests. "Hello there.", mutters:, MUTTER_FN, NONE_FN)
+ Wizard tests. "Hello there."
+ OR
+ Wizard tests. He mutters something.
+
+ Continued in 'help Speak6'.
+& SPEAK6
+ Elegantly handling an empty string when the type of speech is a plain say
+ is a bit more difficult. In order to facilitate this, when the speech type
+ is a plain say, the '<speaker> says,' is only prepended to the output if
+ the transformation of the first speech fragment produces something
+ non-null. Also note that quotes are not placed around such speech
+ automatically, to allow the user's code to insert whatever is appropriate.
+
+ Below is a more elegant version of the mutter example. Here, we find
+ the use for say-speech fragments being numbered starting from 0 rather
+ than 1 -- if the speech fragment number is 0, we know we haven't
+ given any output yet.
+
+ > &MUTTER_FN me = [ifelse(rand(2),"%0",)]
+ > &NONE_FN me = [switch(%1,0,name(%0),capstr(subj(%0)))] mutters something.
+ > @emit speak(me, Hello there., mutters:, MUTTER_FN, NONE_FN)
+ Wizard mutters: "Hello there."
+ OR
+ Wizard mutters something.
+
+ Continued in 'help Speak7'.
+& SPEAK7
+ Here's another example, where words between + signs are reversed,
+ but those within double-quotes are untouched (demonstrating a technique
+ useful in something where you want to allow users to mix ordinary speech
+ with transformed speech).
+
+ > &REV_FN me = [switch(%2,0,backwards,[capstr(subj(%1))] says backwards)],
+ "[revwords(%0)]"
+ > @emit speak(me,:tests. "Normal speech." +Mixed up speech+ Success!, ,
+ REV_FN, ,+)
+ Wizard tests. "Normal speech." He says backwards, "speech up Mixed" Success!
+
& SPELLNUM()
spellnum(<number>)
> say [timestring(301,2)]
You say, "00d 00h 05m 01s"
+& STRINGSECS()
+ stringsecs(<timestring>)
+
+ The stringsecs() function takes a string of the form produced by
+ timestring() and converts it back into seconds.
+
+ Example:
+ > say [stringsecs(5m 1s)]
+ You say, "301"
+
+See also: timestring(), etimefmt()
+
& TR()
tr(<string>,<find>,<replace>)
extern dbref visible_short_page(dbref player, const char *match);
extern void do_doing(dbref player, const char *message);
+/* from funtime.c */
+extern int etime_to_secs(char *str1, int *secs);
+
/* the following symbols are provided by game.c */
extern void process_command(dbref player, char *command,
dbref cause, dbref realcause, int from_port);
{"NAME", fun_name, 0, 2, FN_REG},
{"NAMEGRAB", fun_namegrab, 2, 3, FN_REG},
{"NAMEGRABALL", fun_namegraball, 2, 3, FN_REG},
+ {"NAMELIST", fun_namelist, 1, 1, FN_REG},
{"NAND", fun_nand, 1, INT_MAX, FN_REG},
{"NATTR", fun_nattr, 1, 1, FN_REG},
{"NCHILDREN", fun_lsearch, 1, 1, FN_REG},
{"STARTTIME", fun_starttime, 0, 0, FN_REG},
{"STEP", fun_step, 3, 5, FN_REG},
{"STRCAT", fun_strcat, 1, INT_MAX, FN_REG},
+ {"STRINGSECS", fun_stringsecs, 1, 1, FN_REG},
{"STRINSERT", fun_strinsert, 3, -3, FN_REG},
{"STRIPACCENTS", fun_stripaccents, 1, 1, FN_REG},
{"STRIPANSI", fun_stripansi, 1, -1, FN_REG},
{"UCSTR", fun_ucstr, 1, -1, FN_REG},
{"UDEFAULT", fun_uldefault, 2, 12, FN_NOPARSE},
{"UFUN", fun_ufun, 1, 11, FN_REG},
+ {"ULAMBDA", fun_ulambda, 1, 11, FN_REG},
{"ULDEFAULT", fun_uldefault, 1, 12, FN_NOPARSE},
{"ULOCAL", fun_ulocal, 1, 11, FN_REG},
{"UNIQUE", fun_unique, 1, 4, FN_REG},
}
}
+/* ARGUSED */
+FUNCTION(fun_namelist)
+{
+
+ int first = 1;
+ char *current;
+ dbref target;
+ const char *start;
+
+ start = args[0];
+ while (start && *start) {
+ if (!first)
+ safe_str(" ", buff, bp);
+ first = 0;
+ current = next_in_list(&start);
+ if (*current == '*')
+ current = current + 1;
+ target = lookup_player(current);
+ if (!GoodObject(target))
+ target = visible_short_page(executor, current);
+ if (target == NOTHING) {
+ safe_str("#-1", buff, bp);
+ } else if (target == AMBIGUOUS) {
+ safe_str("#-2", buff, bp);
+ } else {
+ safe_dbref(target, buff, bp);
+ }
+ }
+}
+
/* ARGSUSED */
FUNCTION(fun_locate)
{
}
return;
}
+
+
+FUNCTION(fun_speak)
+{
+ ufun_attrib transufun;
+ ufun_attrib nullufun;
+ dbref speaker;
+ char *speaker_str;
+ char *open, *close;
+ int transform = 0, null = 0, say = 0;
+ char *wenv[3];
+ int funccount;
+ int fragment = 0;
+ char *say_string;
+ char *string;
+ char rbuff[BUFFER_LEN];
+
+ speaker = match_thing(executor, args[0]);
+ if (speaker == NOTHING || speaker == AMBIGUOUS) {
+ safe_str(T(e_match), buff, bp);
+ return;
+ }
+ speaker_str = unparse_dbref(speaker);
+
+ if (!args[1] || !*args[1])
+ return;
+
+ string = args[1];
+
+ if (nargs > 2 && *args[2] != '\0' && *args[2] != ' ')
+ say_string = args[2];
+ else
+ say_string = (char *) "says,";
+
+ BEGINOOREF_L
+ if (nargs > 3) {
+ if (args[3] != '\0') {
+ /* we have a transform attr */
+ transform = 1;
+ if (!fetch_ufun_attrib(args[3], executor, &transufun, 1)) {
+ ENDOOREF_L
+ safe_str(T(e_atrperm), buff, bp);
+ return;
+ }
+ if (nargs > 4) {
+ if (args[4] != '\0') {
+ /* we have an attr to use when transform returns an empty string */
+ null = 1;
+ if (!fetch_ufun_attrib(args[4], executor, &nullufun, 1)) {
+ ENDOOREF_L
+ safe_str(T(e_atrperm), buff, bp);
+ return;
+ }
+ }
+ }
+ }
+ }
+
+ if (nargs < 6 || args[5] == '\0')
+ open = (char *) "\"";
+ else
+ open = args[5];
+ if (nargs < 7 || args[6] == '\0')
+ close = open;
+ else
+ close = args[6];
+
+ switch (*string) {
+ case ':':
+ safe_str(Name(speaker), buff, bp);
+ string++;
+ if (*string == ' ') {
+ /* semipose it instead */
+ while (*string == ' ')
+ string++;
+ } else
+ safe_chr(' ', buff, bp);
+ break;
+ case ';':
+ string++;
+ safe_str(Name(speaker), buff, bp);
+ if (*string == ' ') {
+ /* pose it instead */
+ safe_chr(' ', buff, bp);
+ while (*string == ' ')
+ string++;
+ }
+ break;
+ case '|':
+ string++;
+ break;
+ case '"':
+ if (CHAT_STRIP_QUOTE)
+ string++;
+ default:
+ say = 1;
+ break;
+ }
+
+ if (!transform || (!say && !strstr(string, open))) {
+ /* nice and easy */
+ if (say)
+ safe_format(buff, bp, "%s %s \"%s\"", Name(speaker), say_string, string);
+ else
+ safe_str(string, buff, bp);
+ ENDOOREF_L
+ return;
+ }
+
+
+ if (say && !strstr(string, close)) {
+ /* the whole string has to be transformed */
+ wenv[0] = string;
+ wenv[1] = speaker_str;
+ wenv[2] = unparse_integer(fragment);
+ if (call_ufun(&transufun, wenv, 3, rbuff, executor, enactor, pe_info)) {
+ ENDOOREF_L
+ return;
+ }
+ if (strlen(rbuff) > 0) {
+ safe_format(buff, bp, "%s %s %s", Name(speaker), say_string, rbuff);
+ ENDOOREF_L
+ return;
+ } else if (null == 1) {
+ wenv[0] = speaker_str;
+ wenv[1] = unparse_integer(fragment);
+ if (call_ufun(&nullufun, wenv, 2, rbuff, executor, enactor, pe_info)) {
+ ENDOOREF_L
+ return;
+ }
+ safe_str(rbuff, buff, bp);
+ ENDOOREF_L
+ return;
+ }
+ } else {
+ /* only transform portions of string between open and close */
+ char *speech;
+ int indx;
+ int finished = 0;
+ int delete = 0;
+
+ if (say) {
+ safe_str(Name(speaker), buff, bp);
+ safe_chr(' ', buff, bp);
+ safe_str(say_string, buff, bp);
+ safe_chr(' ', buff, bp);
+ }
+ funccount = pe_info->fun_invocations;
+ while (!finished && ((say && fragment == 0 && (speech = string))
+ || (speech = strstr(string, open)))) {
+ fragment++;
+ indx = string - speech;
+ if (indx < 0)
+ indx *= -1;
+ if (string != NULL && strlen(string) > 0 && indx > 0)
+ safe_strl(string, indx, buff, bp);
+ if (!say || fragment > 1)
+ speech = speech + strlen(open); /* move past open char */
+ /* find close-char */
+ string = strstr(speech, close);
+ if (!string || !(string = string + strlen(close))) {
+ /* no close char, or nothing after it; we're at the end! */
+ finished = 1;
+ }
+ delete = (string == NULL ? strlen(speech) : strlen(speech) -
+ (strlen(string) + strlen(close)));
+ speech = chopstr(speech, delete);
+ wenv[0] = speech;
+ wenv[1] = speaker_str;
+ wenv[2] = unparse_integer(fragment);
+ if (call_ufun(&transufun, wenv, 3, rbuff, executor, enactor, pe_info))
+ break;
+ if (*bp == (buff + BUFFER_LEN - 1) &&
+ pe_info->fun_invocations == funccount)
+ break;
+ funccount = pe_info->fun_invocations;
+ if ((null == 1) && (strlen(rbuff) == 0)) {
+ wenv[0] = speaker_str;
+ wenv[1] = unparse_integer(fragment);
+ if (call_ufun(&nullufun, wenv, 2, rbuff, executor, enactor, pe_info))
+ break;
+ }
+ if (strlen(rbuff) > 0) {
+ safe_str(rbuff, buff, bp);
+ }
+ if (*bp == (buff + BUFFER_LEN - 1) &&
+ pe_info->fun_invocations == funccount)
+ break;
+ }
+ if (string != NULL && strlen(string) > 0) {
+ safe_str(string, buff, bp); /* remaining string (not speech, so not t) */
+ }
+ }
+ ENDOOREF_L
+}
}
+/* ARGSUSED */
+FUNCTION(fun_stringsecs)
+{
+ int secs;
+ if (etime_to_secs(args[0], &secs))
+ safe_integer(secs, buff, bp);
+ else
+ safe_str(T("#-1 INVALID TIMESTRING"), buff, bp);
+}
+
+/** Convert an elapsed time string (3d 2h 1m 10s) to seconds.
+ * \param str1 a time string.
+ * \param secs pointer to an int to fill with number of seconds.
+ * \retval 1 success.
+ * \retval 0 failure.
+ */
+int
+etime_to_secs(char *str1, int *secs)
+{
+ /* parse the result from timestring() back into a number of seconds */
+ char str2[BUFFER_LEN];
+ int i;
+
+ *secs = 0;
+ while (str1 && *str1) {
+ while (*str1 == ' ')
+ str1++;
+ i = 0;
+ while (isdigit((unsigned char) *str1)) {
+ str2[i] = *str1;
+ str1++;
+ i++;
+ }
+ if (i == 0) {
+ return 0; /* No numbers given */
+ }
+ str2[i] = '\0';
+ if (!*str1) {
+ *secs += parse_integer(str2); // no more chars, just add seconds and stop
+ break;
+ }
+ switch (*str1) {
+ case 'd':
+ case 'D':
+ *secs += (parse_integer(str2) * 86400); // days
+ break;
+ case 'h':
+ case 'H':
+ *secs += (parse_integer(str2) * 3600); // hours
+ break;
+ case 'm':
+ case 'M':
+ *secs += (parse_integer(str2) * 60); // minutes
+ break;
+ case 's':
+ case 'S':
+ case ' ':
+ *secs += parse_integer(str2); // seconds
+ break;
+ default:
+ return 0;
+ }
+ str1++; // move past the time char
+ }
+ return 1;
+}
+
+
/* ARGSUSED */
FUNCTION(fun_timestring)
{
return;
}
+/* ARGSUSED */
+FUNCTION(fun_ulambda)
+{
+ char rbuff[BUFFER_LEN];
+ ufun_attrib ufun;
+
+ BEGINOOREF_L
+
+ if (!fetch_ufun_attrib(args[0], executor, &ufun, 1)) {
+ safe_str(T(ufun.errmess), buff, bp);
+ ENDOOREF_L
+ return;
+ }
+
+ call_ufun(&ufun, args + 1, nargs - 1, rbuff, executor, enactor, pe_info);
+
+ safe_str(rbuff, buff, bp);
+
+ ENDOOREF_L
+ return;
+}
+
+
/* ARGSUSED */
FUNCTION(fun_ulocal)
{
FUNCTION_PROTO(fun_name);
FUNCTION_PROTO(fun_namegrab);
FUNCTION_PROTO(fun_namegraball);
+FUNCTION_PROTO(fun_namelist);
FUNCTION_PROTO(fun_nand);
FUNCTION_PROTO(fun_nattr);
FUNCTION_PROTO(fun_nearby);
FUNCTION_PROTO(fun_soundex);
FUNCTION_PROTO(fun_soundlike);
FUNCTION_PROTO(fun_space);
+FUNCTION_PROTO(fun_speak);
FUNCTION_PROTO(fun_spellnum);
FUNCTION_PROTO(fun_splice);
FUNCTION_PROTO(fun_sql);
FUNCTION_PROTO(fun_stddev);
FUNCTION_PROTO(fun_step);
FUNCTION_PROTO(fun_strcat);
+FUNCTION_PROTO(fun_stringsecs);
FUNCTION_PROTO(fun_strinsert);
FUNCTION_PROTO(fun_stripaccents);
FUNCTION_PROTO(fun_stripansi);
FUNCTION_PROTO(fun_type);
FUNCTION_PROTO(fun_ucstr);
FUNCTION_PROTO(fun_ufun);
+FUNCTION_PROTO(fun_ulambda);
FUNCTION_PROTO(fun_uldefault);
FUNCTION_PROTO(fun_ulocal);
FUNCTION_PROTO(fun_unique);