From 06e65c769bbb66045e0d51da6a321a4c4ef1ff42 Mon Sep 17 00:00:00 2001 From: gumi Date: Sun, 22 Jul 2018 23:37:10 -0400 Subject: allow local NPC functions to be public or private --- src/map/map.h | 8 + src/map/npc.c | 11 +- src/map/npc.h | 1 + src/map/script.c | 547 ++++++++++++++++++++++++++++++++++++++++--------------- src/map/script.h | 15 +- 5 files changed, 423 insertions(+), 159 deletions(-) (limited to 'src') diff --git a/src/map/map.h b/src/map/map.h index 2de6df2f7..b3cdef025 100644 --- a/src/map/map.h +++ b/src/map/map.h @@ -329,6 +329,14 @@ enum bl_type { enum npc_subtype { WARP, SHOP, SCRIPT, CASHSHOP, TOMB }; +/** optional flags for script labels, used by the label db */ +enum script_label_flags { + /** the label can be called from outside the local scope of the NPC */ + LABEL_IS_EXTERN = 0x1, + /** the label is a public or private local NPC function */ + LABEL_IS_USERFUNC = 0x2, +}; + /** * Race type IDs. * diff --git a/src/map/npc.c b/src/map/npc.c index 40ec380ee..570305ba1 100644 --- a/src/map/npc.c +++ b/src/map/npc.c @@ -389,7 +389,8 @@ static int npc_event_export(struct npc_data *nd, int i) Assert_ret(i >= 0 && i < nd->u.scr.label_list_num); lname = nd->u.scr.label_list[i].name; pos = nd->u.scr.label_list[i].pos; - if ((lname[0] == 'O' || lname[0] == 'o') && (lname[1] == 'N' || lname[1] == 'n')) { + + if ((nd->u.scr.label_list[i].flags & LABEL_IS_EXTERN) != 0 && (nd->u.scr.label_list[i].flags & LABEL_IS_USERFUNC) == 0) { struct event_data *ev; struct linkdb_node **label_linkdb = NULL; char buf[EVENT_NAME_LENGTH]; @@ -3054,11 +3055,11 @@ static int npc_unload(struct npc_data *nd, bool single, bool unload_mobs) aFree(nd->u.shop.shop_item); /// src check for duplicate shops. [Orcao] } else if (nd->subtype == SCRIPT) { char evname[EVENT_NAME_LENGTH]; - + snprintf(evname, ARRAYLENGTH(evname), "%s::OnNPCUnload", nd->exname); struct event_data *ev = strdb_get(npc->ev_db, evname); - + if (ev != NULL) script->run_npc(nd->u.scr.script, ev->pos, 0, nd->bl.id); /// Run OnNPCUnload. @@ -3665,6 +3666,7 @@ static void npc_convertlabel_db(struct npc_label_list *label_list, const char *f for( i = 0; i < script->label_count; i++ ) { const char* lname = script->get_str(script->labels[i].key); int lpos = script->labels[i].pos; + enum script_label_flags flags = script->labels[i].flags; struct npc_label_list* label; const char *p; size_t len; @@ -3686,6 +3688,7 @@ static void npc_convertlabel_db(struct npc_label_list *label_list, const char *f safestrncpy(label->name, lname, sizeof(label->name)); label->pos = lpos; + label->flags = flags; } } @@ -5606,7 +5609,7 @@ static int npc_reload(void) npc->npc_last_npd = NULL; npc->npc_last_path = NULL; npc->npc_last_ref = NULL; - + const int npc_new_min = npc->npc_id; struct s_mapiterator *iter = mapit_geteachiddb(); diff --git a/src/map/npc.h b/src/map/npc.h index 1585a2bc8..a73a3ae06 100644 --- a/src/map/npc.h +++ b/src/map/npc.h @@ -59,6 +59,7 @@ struct npc_timerevent_list { struct npc_label_list { char name[NAME_LENGTH]; int pos; + enum script_label_flags flags; }; struct npc_barter_currency { diff --git a/src/map/script.c b/src/map/script.c index c08f5e829..9d6b9d8a2 100644 --- a/src/map/script.c +++ b/src/map/script.c @@ -848,79 +848,134 @@ static const char *parse_callfunc(const char *p, int require_paren, int is_custo nullpo_retr(NULL, p); // is need add check for arg null pointer below? - func = script->add_word(p); - if (script->str_data[func].type == C_FUNC) { - script->syntax.nested_call++; - if (script->syntax.last_func != -1) { - if (script->str_data[func].val == script->buildin_lang_macro_offset) { - script->syntax.lang_macro_active = true; - macro = true; - } else if (script->str_data[func].val == script->buildin_lang_macro_fmtstring_offset) { - script->syntax.lang_macro_fmtstring_active = true; - macro = true; - } + + if (*p == '"') { + p2 = ++p; // jump to the start of the word + + // find the closing quote + while (*p2 != '"') { + ++p2; } - if( !macro ) { - // buildin function + if (p2[1] == ':' && p2[2] == ':') { + func = script->add_str("callfunctionofnpc"); + arg = "*"; // we already take care of the "vs" part of "vs*" + + script->syntax.nested_call++; script->syntax.last_func = script->str_data[func].val; script->addl(func); script->addc(C_ARG); - } - arg = script->buildin[script->str_data[func].val]; - if (script->str_data[func].deprecated) - DeprecationWarning(p); - if( !arg ) arg = &null_arg; // Use a dummy, null string - } else if( script->str_data[func].type == C_USERFUNC || script->str_data[func].type == C_USERFUNC_POS ) { - // script defined function - script->addl(script->buildin_callsub_ref); - script->addc(C_ARG); - script->addl(func); - arg = script->buildin[script->str_data[script->buildin_callsub_ref].val]; - if( *arg == 0 ) - disp_error_message("parse_callfunc: callsub has no arguments, please review its definition",p); - if( *arg != '*' ) - ++arg; // count func as argument + script->addc(C_STR); + do { + script->addb(*p++); // npc name + } while (p < p2); + script->addb(0); + + p = p2 + 3; // skip to start of func name + p2 = script->skip_word(p); + + script->addc(C_STR); + do { + script->addb(*p++); // func name + } while (p < p2); + script->addb(0); + + p = p2; // skip to just before the () + } else { + disp_error_message("script:parse_callfunc: invalid public function call syntax!", p2 + 1); + } } else { + func = script->add_word(p); + if (script->str_data[func].type == C_FUNC) { + script->syntax.nested_call++; + + if (script->syntax.last_func != -1) { + if (script->str_data[func].val == script->buildin_lang_macro_offset) { + script->syntax.lang_macro_active = true; + macro = true; + } else if (script->str_data[func].val == script->buildin_lang_macro_fmtstring_offset) { + script->syntax.lang_macro_fmtstring_active = true; + macro = true; + } + } + + if (!macro) { + // buildin function + script->syntax.last_func = script->str_data[func].val; + script->addl(func); + script->addc(C_ARG); + } + + arg = script->buildin[script->str_data[func].val]; + + if (script->str_data[func].deprecated == 1) { + DeprecationWarning(p); + } + + if (arg == NULL) { + arg = &null_arg; // Use a dummy, null string + } + } else if (script->str_data[func].type == C_USERFUNC || script->str_data[func].type == C_USERFUNC_POS) { + // script defined function + script->addl(script->buildin_callsub_ref); + script->addc(C_ARG); + script->addl(func); + arg = script->buildin[script->str_data[script->buildin_callsub_ref].val]; + + if (*arg == 0) { + disp_error_message("script:parse_callfunc: callsub has no arguments, please review its definition", p); + } + + if (*arg != '*') { + ++arg; // count func as argument + } + } else { #ifdef SCRIPT_CALLFUNC_CHECK - const char* name = script->get_str(func); - if( !is_custom && strdb_get(script->userfunc_db, name) == NULL ) { + const char *name = script->get_str(func); + if (is_custom == 0 && strdb_get(script->userfunc_db, name) == NULL) { #endif - disp_error_message("parse_line: expect command, missing function name or calling undeclared function",p); + disp_error_message("script:parse_callfunc: expect command, missing function name or calling undeclared function", p); #ifdef SCRIPT_CALLFUNC_CHECK - } else {; - script->addl(script->buildin_callfunc_ref); - script->addc(C_ARG); - script->addc(C_STR); - while( *name ) script->addb(*name ++); - script->addb(0); - arg = script->buildin[script->str_data[script->buildin_callfunc_ref].val]; - if( *arg != '*' ) ++ arg; - } + } else { + script->addl(script->buildin_callfunc_ref); + script->addc(C_ARG); + script->addc(C_STR); + + while (*name != '\0') { + script->addb(*name++); + } + + script->addb(0); + arg = script->buildin[script->str_data[script->buildin_callfunc_ref].val]; + + if (*arg != '*') { + ++ arg; + } + } #endif + } } p = script->skip_word(p); p = script->skip_space(p); script->syntax.curly[script->syntax.curly_count].type = TYPE_ARGLIST; script->syntax.curly[script->syntax.curly_count].count = 0; - if( *p == ';' ) - {// ';' + + if (*p == ';') { + // ';' script->syntax.curly[script->syntax.curly_count].flag = ARGLIST_NO_PAREN; - } else if( *p == '(' && *(p2=script->skip_space(p+1)) == ')' ) - {// '(' ')' + } else if (*p == '(' && *(p2 = script->skip_space(p + 1)) == ')') { + // '(' ')' script->syntax.curly[script->syntax.curly_count].flag = ARGLIST_PAREN; p = p2; - /* - } else if( 0 && require_paren && *p != '(' ) - {// - script->syntax.curly[script->syntax.curly_count].flag = ARGLIST_NO_PAREN; - */ - } else {// - if( require_paren ) { - if( *p != '(' ) - disp_error_message("need '('",p); + } else { + // + if (require_paren == 1) { + if (*p != '(') { + disp_error_message("script:parse_callfunc: need '('", p); + } + ++p; // skip '(' script->syntax.curly[script->syntax.curly_count].flag = ARGLIST_PAREN; } else if( *p == '(' ) { @@ -928,41 +983,65 @@ static const char *parse_callfunc(const char *p, int require_paren, int is_custo } else { script->syntax.curly[script->syntax.curly_count].flag = ARGLIST_NO_PAREN; } + ++script->syntax.curly_count; - while( *arg ) { - p2=script->parse_subexpr(p,-1); - if( p == p2 ) - break; // not an argument - if( *arg != '*' ) - ++arg; // next argument - p=script->skip_space(p2); - if( *arg == 0 || *p != ',' ) - break; // no more arguments + while (*arg != '\0') { + p2 = script->parse_subexpr(p, -1); + + if (p == p2) { + // not an argument + break; + } + + if (*arg != '*') { + // next argument + ++arg; + } + + p = script->skip_space(p2); + + if (*arg == 0 || *p != ',') { + // no more arguments + break; + } + ++p; // skip comma } + --script->syntax.curly_count; } - if( arg && *arg && *arg != '?' && *arg != '*' ) - disp_error_message2("parse_callfunc: not enough arguments, expected ','", p, script->config.warn_func_mismatch_paramnum); - if( script->syntax.curly[script->syntax.curly_count].type != TYPE_ARGLIST ) - disp_error_message("parse_callfunc: DEBUG last curly is not an argument list",p); - if( script->syntax.curly[script->syntax.curly_count].flag == ARGLIST_PAREN ) { - if( *p != ')' ) - disp_error_message("parse_callfunc: expected ')' to close argument list",p); + + if (arg != NULL && *arg != '\0' && *arg != '?' && *arg != '*') { + disp_error_message2("script:parse_callfunc: not enough arguments, expected ','", p, script->config.warn_func_mismatch_paramnum); + } + + if (script->syntax.curly[script->syntax.curly_count].type != TYPE_ARGLIST) { + disp_error_message("parse_callfunc: DEBUG last curly is not an argument list", p); + } + + if (script->syntax.curly[script->syntax.curly_count].flag == ARGLIST_PAREN) { + if (*p != ')') { + disp_error_message("script:parse_callfunc: expected ')' to close argument list", p); + } + ++p; - if (script->str_data[func].val == script->buildin_lang_macro_offset) + if (script->str_data[func].val == script->buildin_lang_macro_offset) { script->syntax.lang_macro_active = false; - else if (script->str_data[func].val == script->buildin_lang_macro_fmtstring_offset) + } else if (script->str_data[func].val == script->buildin_lang_macro_fmtstring_offset) { script->syntax.lang_macro_fmtstring_active = false; + } } if (!macro) { - if (0 == --script->syntax.nested_call) + if (0 == --script->syntax.nested_call) { script->syntax.last_func = -1; + } + script->addc(C_FUNC); } + return p; } @@ -1230,16 +1309,29 @@ static int script_string_dup(char *str) *------------------------------------------*/ static const char *parse_simpleexpr(const char *p) { - p=script->skip_space(p); + p = script->skip_space(p); nullpo_retr(NULL, p); - if (*p == ';' || *p == ',') - disp_error_message("parse_simpleexpr: unexpected end of expression",p); + + if (*p == ';' || *p == ',') { + disp_error_message("script:parse_simpleexpr: unexpected end of expression", p); + } + if (*p == '(') { return script->parse_simpleexpr_paren(p); } else if (is_number(p)) { return script->parse_simpleexpr_number(p); } else if(*p == '"') { + const char *p2 = p + 1; + + while (*p2 != '"') { + ++p2; + } + + if (p2[1] == ':' && p2[2] == ':') { + return script->parse_callfunc(p, 1, 0); // XXX: why does callfunc use int for booleans? + } + return script->parse_simpleexpr_string(p); } else { return script->parse_simpleexpr_name(p); @@ -1577,6 +1669,85 @@ static const char *parse_line(const char *p) return p; } +/** + * parses a local function expression + * + * expects these formats: + * function ; + * function {