From 832bd69b753d5e34cb82e824d91fe64eb1d3859e Mon Sep 17 00:00:00 2001 From: nveid Date: Sun, 8 Apr 2007 07:22:21 +0000 Subject: [PATCH] Added Ulambda, namelist, stringsecs, and speak functions. (cherry picked from commit 9a75f746e3176d66906933be410d6f338fa0816d) --- game/txt/changes/0.73 | 4 + game/txt/hlp/cobra_func.hlp | 186 ++++++++++++++++++++++++++++++++-- hdrs/externs.h | 3 + src/function.c | 3 + src/fundb.c | 30 ++++++ src/funstr.c | 195 ++++++++++++++++++++++++++++++++++++ src/funtime.c | 68 +++++++++++++ src/funufun.c | 23 +++++ win32/funs.h | 4 + 9 files changed, 508 insertions(+), 8 deletions(-) diff --git a/game/txt/changes/0.73 b/game/txt/changes/0.73 index 35ff9fa..2c7e9e7 100644 --- a/game/txt/changes/0.73 +++ b/game/txt/changes/0.73 @@ -177,4 +177,8 @@ CobraMUSH Version 0.73 * 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] diff --git a/game/txt/hlp/cobra_func.hlp b/game/txt/hlp/cobra_func.hlp index a9af65f..e6da2e5 100644 --- a/game/txt/hlp/cobra_func.hlp +++ b/game/txt/hlp/cobra_func.hlp @@ -113,13 +113,17 @@ 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. @@ -208,7 +212,7 @@ 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. @@ -2544,6 +2548,15 @@ for an object named "Test", preferring a thing over other types. that it will continue to do this for the time being. Don't rely on it. Related functions: FULLNAME(), ACCNAME(), INAME() +& NAMELIST() + namelist() + + 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(,) @@ -3427,6 +3440,151 @@ for an object named "Test", preferring a thing over other types. Prints 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(, [, + [, [/] + [, [/][, [, ]]]]]) + + 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 and are given, this function formats + as if it were speech from , as follows. + + If is... the resulting string is... + : + : + ; + | + " says, "" + says, "" + + If 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 is specified (an object/attribute pair or attribute, + as with map() and similar functions), the speech portions of + are passed through the transformation function. + + Speech is delimited by double-quotes (i.e., "text"), or by the + specified and strings. For instance, if you wanted + <> to denote text to be transformed, you would specify + as << and close as >> in the function call. Only the portions of the + string between those delimiters are transformed. If is not + specified, it defaults to . + + The transformation function receives the speech text as %0, the + dbref of as %1, and the speech fragment number as %2. + For non-say input strings (i.e., for an original 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... <> 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 is specified (an object/attribute pair or attribute), + that function is used evaluate an alternative result, with %0 as + the dbref of , and %1 as the speech fragment number. + + The 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 ' 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() @@ -3955,6 +4113,18 @@ for an object named "Test", preferring a thing over other types. > say [timestring(301,2)] You say, "00d 00h 05m 01s" +& STRINGSECS() + stringsecs() + + 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(,,) diff --git a/hdrs/externs.h b/hdrs/externs.h index a53f6f0..bdb2a6b 100644 --- a/hdrs/externs.h +++ b/hdrs/externs.h @@ -65,6 +65,9 @@ extern dbref short_page(const char *match); 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); diff --git a/src/function.c b/src/function.c index 5f1fee0..398f040 100644 --- a/src/function.c +++ b/src/function.c @@ -497,6 +497,7 @@ FUNTAB flist[] = { {"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}, @@ -632,6 +633,7 @@ FUNTAB flist[] = { {"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}, @@ -661,6 +663,7 @@ FUNTAB flist[] = { {"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}, diff --git a/src/fundb.c b/src/fundb.c index 0bec8f9..35b1971 100644 --- a/src/fundb.c +++ b/src/fundb.c @@ -1612,6 +1612,36 @@ FUNCTION(fun_pmatch) } } +/* 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) { diff --git a/src/funstr.c b/src/funstr.c index 427d3fb..a47d9b6 100644 --- a/src/funstr.c +++ b/src/funstr.c @@ -2115,3 +2115,198 @@ FUNCTION(fun_align) } 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 +} diff --git a/src/funtime.c b/src/funtime.c index 54c3a61..7cac6dc 100644 --- a/src/funtime.c +++ b/src/funtime.c @@ -205,6 +205,74 @@ FUNCTION(fun_etimefmt) } +/* 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) { diff --git a/src/funufun.c b/src/funufun.c index 5ce90fa..7d85ea0 100644 --- a/src/funufun.c +++ b/src/funufun.c @@ -167,6 +167,29 @@ FUNCTION(fun_ufun) 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) { diff --git a/win32/funs.h b/win32/funs.h index 2143067..591c329 100644 --- a/win32/funs.h +++ b/win32/funs.h @@ -216,6 +216,7 @@ FUNCTION_PROTO(fun_munge); 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); @@ -310,6 +311,7 @@ FUNCTION_PROTO(fun_sortkey); 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); @@ -322,6 +324,7 @@ FUNCTION_PROTO(fun_starttime); 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); @@ -350,6 +353,7 @@ FUNCTION_PROTO(fun_trunc); 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); -- 2.30.2