From 9c653b143d52dfa8469a50ba7a98d50bf7e59ee4 Mon Sep 17 00:00:00 2001 From: gumi Date: Wed, 6 May 2020 13:21:27 -0400 Subject: [PATCH 1/3] add support to is_function() for public functions --- src/map/script.c | 55 +++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 50 insertions(+), 5 deletions(-) diff --git a/src/map/script.c b/src/map/script.c index e4a57194de3..0b1f9b3a843 100644 --- a/src/map/script.c +++ b/src/map/script.c @@ -23900,24 +23900,33 @@ static BUILDIN(getcharip) return true; } +/** possible types of script commands */ enum function_type { + /** not found */ FUNCTION_IS_NONE = 0, + /** script buildin */ FUNCTION_IS_COMMAND, + /** global user-defined function (for use with callfunc) */ FUNCTION_IS_GLOBAL, + /** user-defined function local to the NPC */ FUNCTION_IS_LOCAL, + /** user-defined label (for use with callsub) */ FUNCTION_IS_LABEL, + /** user-defined function local to the NPC, marked as public */ + FUNCTION_IS_PUBLIC, }; /** - * is_function() - **/ + * is_function(""{, }) + * is_function(""{, ""}) + * + * checks the type of a script command + */ static BUILDIN(is_function) { const char *str = script_getstr(st, 2); enum function_type type = FUNCTION_IS_NONE; - // TODO: add support for exported functions (#2142) - if (strdb_exists(script->userfunc_db, str)) { type = FUNCTION_IS_GLOBAL; } else { @@ -23943,6 +23952,41 @@ static BUILDIN(is_function) } } } + + if (type == FUNCTION_IS_LOCAL + || (script_hasdata(st, 3) && type != FUNCTION_IS_COMMAND)) { + struct npc_data *nd = map->id2nd(st->oid); + + if (script_hasdata(st, 3)) { + // at this point we know it's not a global or a buildin, so we + // reset since we want to search another NPC + type = FUNCTION_IS_NONE; + + if (script_isint(st, 3)) { + nd = map->id2nd(script_getnum(st, 3)); + } else if (script_isstring(st, 3)) { + nd = npc->name2id(script_getstr(st, 3)); + } + } + + if (nd != NULL) { + // try to find the label + for (int i = 0; i < nd->u.scr.label_list_num; ++i) { + if (strcmp(nd->u.scr.label_list[i].name, str) == 0) { + if ((nd->u.scr.label_list[i].flags & LABEL_IS_USERFUNC) != 0) { + if ((nd->u.scr.label_list[i].flags & LABEL_IS_EXTERN) != 0) { + type = FUNCTION_IS_PUBLIC; + } else { + type = FUNCTION_IS_LOCAL; + } + } else { + type = FUNCTION_IS_LABEL; + } + break; + } + } + } + } } script_pushint(st, type); @@ -27555,7 +27599,7 @@ static void script_parse_builtin(void) **/ BUILDIN_DEF(getargcount,""), BUILDIN_DEF(getcharip,"?"), - BUILDIN_DEF(is_function,"s"), + BUILDIN_DEF(is_function,"s?"), BUILDIN_DEF(freeloop,"i"), BUILDIN_DEF(getrandgroupitem,"ii"), BUILDIN_DEF(cleanmap,"s"), @@ -28181,6 +28225,7 @@ static void script_hardcoded_constants(void) script->set_constant("FUNCTION_IS_GLOBAL", FUNCTION_IS_GLOBAL, false, false); script->set_constant("FUNCTION_IS_LOCAL", FUNCTION_IS_LOCAL, false, false); script->set_constant("FUNCTION_IS_LABEL", FUNCTION_IS_LABEL, false, false); + script->set_constant("FUNCTION_IS_PUBLIC", FUNCTION_IS_PUBLIC, false, false); script->constdb_comment("item trade restrictions"); script->set_constant("ITR_NONE", ITR_NONE, false, false); From d6324c868de8f97c80834527e92c8b8d3d2ed373 Mon Sep 17 00:00:00 2001 From: gumi Date: Wed, 6 May 2020 13:27:33 -0400 Subject: [PATCH 2/3] update the documentation for is_function() --- doc/script_commands.txt | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/doc/script_commands.txt b/doc/script_commands.txt index a597dfaa230..522df2cd7f1 100644 --- a/doc/script_commands.txt +++ b/doc/script_commands.txt @@ -2040,16 +2040,19 @@ Example: --------------------------------------- -*is_function("") +*is_function(""{, }) +*is_function(""{, ""}) -This command checks whether or not a function exists and returns its type. -Returns false if it cannot be found. +This command checks whether or not a function exists and returns its type. If a +NPC name or ID is provided, it will try to find the label or function within +this NPC instead of the attached NPC. Returns false if it cannot be found. return values: FUNCTION_IS_COMMAND - built-in script command (eg: mes, select, ...) FUNCTION_IS_GLOBAL - user-defined global function (callable with callfunc) FUNCTION_IS_LOCAL - user-defined local function + FUNCTION_IS_PUBLIC - user-defined local function marked as public FUNCTION_IS_LABEL - user-defined label function (callable with callsub) Example: From c12e51118c9aef4a207b35afb9114293e97b476e Mon Sep 17 00:00:00 2001 From: gumi Date: Wed, 27 May 2020 14:52:34 +0000 Subject: [PATCH 3/3] add unit tests for is_function() --- npc/dev/test.txt | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/npc/dev/test.txt b/npc/dev/test.txt index c8c8420558d..d104aa7b212 100644 --- a/npc/dev/test.txt +++ b/npc/dev/test.txt @@ -148,6 +148,10 @@ function script F_TestVarOfAnotherNPC { return set(getarg(0), 1337); } + +OnLabel: + // to test is_function() with the label of another NPC + end; } function script HerculesSelfTestHelper { @@ -825,6 +829,18 @@ function script HerculesSelfTestHelper { callsub(OnCheck, "public local function var reference test", .@refTest, 1337); callsub(OnCheck, "programatic public local call", callfunctionofnpc("export test", "RefTest", .@refTest = 1), 1337); + private function localFunc {} + public function pubLocalFunc {} + callsub(OnCheck, "is_function: not found", is_function("does-not-exist"), false); + callsub(OnCheck, "is_function: built-in script command", is_function("callfunc"), FUNCTION_IS_COMMAND); + callsub(OnCheck, "is_function: global function", is_function("HerculesSelfTestHelper"), FUNCTION_IS_GLOBAL); + callsub(OnCheck, "is_function: local NPC function", is_function("localFunc"), FUNCTION_IS_LOCAL); + callsub(OnCheck, "is_function: public local NPC function", is_function("pubLocalFunc"), FUNCTION_IS_PUBLIC); + callsub(OnCheck, "is_function: local subroutine", is_function("OnCheck"), FUNCTION_IS_LABEL); + callsub(OnCheck, "is_function: local NPC function of another NPC", is_function("Private", "export test"), FUNCTION_IS_LOCAL); + callsub(OnCheck, "is_function: public local NPC function of another NPC", is_function("Public", "export test"), FUNCTION_IS_PUBLIC); + callsub(OnCheck, "is_function: local subroutine of another NPC", is_function("OnLabel", "export test"), FUNCTION_IS_LABEL); + if (.errors) { consolemes(CONSOLEMES_DEBUG, "Script engine self-test [ \033[0;31mFAILED\033[0m ]"); consolemes(CONSOLEMES_DEBUG, "**** The test was completed with " + .errors + " errors. ****");