diff options
Diffstat (limited to 'src/map/script.c')
-rw-r--r-- | src/map/script.c | 1286 |
1 files changed, 898 insertions, 388 deletions
diff --git a/src/map/script.c b/src/map/script.c index 9a5d33604..841e21169 100644 --- a/src/map/script.c +++ b/src/map/script.c @@ -57,6 +57,7 @@ #include "map/status.h" #include "map/storage.h" #include "map/unit.h" +#include "map/achievement.h" #include "common/cbasetypes.h" #include "common/conf.h" #include "common/db.h" @@ -612,6 +613,18 @@ static int script_add_str(const char *p) return script->str_num++; } +static int script_add_variable(const char *varname) +{ + int key = script->search_str(varname); + + if (key < 0) { + key = script->add_str(varname); + script->str_data[key].type = C_NAME; + } + + return key; +} + /** * Appends 1 byte to the script buffer. * @@ -1365,6 +1378,11 @@ static const char *parse_simpleexpr_name(const char *p) disp_error_message("parse_simpleexpr: unmatched ']'", p); ++p; script->addc(C_FUNC); + } else if (script->str_data[l].type == C_INT) { + script->addc(C_NAME); + script->addb(l); + script->addb(l >> 8); + script->addb(l >> 16); } else { script->addl(l); } @@ -2885,8 +2903,7 @@ static struct script_data *get_val(struct script_state *st, struct script_data * return data; } - //##TODO use reference_tovariable(data) when it's confirmed that it works [FlavioJS] - if (!reference_toconstant(data) && not_server_variable(prefix) && reference_getref(data) == NULL) { + if (((reference_tovariable(data) && not_server_variable(prefix)) || reference_toparam(data)) && reference_getref(data) == NULL) { sd = script->rid2sd(st); if (sd == NULL) {// needs player attached if (postfix == '$') {// string variable @@ -3031,6 +3048,10 @@ static const void *get_val2(struct script_state *st, int64 uid, struct reg_db *r script->get_val(st, data); if (data->type == C_INT) // u.num is int32 because it comes from script->get_val return (const void *)h64BPTRSIZE((int32)data->u.num); + else if (data_isreference(data) && reference_toconstant(data)) + return (const void *)h64BPTRSIZE((int32)reference_getconstant(data)); + else if (data_isreference(data) && reference_toparam(data)) + return (const void *)h64BPTRSIZE((int32)reference_getparamtype(data)); else return (const void *)h64BPTRSIZE(data->u.str); } @@ -3295,95 +3316,28 @@ static void set_reg_npcscope_str(struct script_state *st, struct reg_db *n, int6 static void set_reg_pc_ref_str(struct script_state *st, struct reg_db *n, int64 num, const char *name, const char *str) { - struct script_reg_str *p = NULL; - unsigned int index = script_getvaridx(num); - - nullpo_retv(n); - - if ((p = i64db_get(n->vars, num)) != NULL) { - if (str[0]) { - if (p->value) { - aFree(p->value); - } else if (index) { - script->array_update(n, num, false); - } - p->value = aStrdup(str); - } else { - p->value = NULL; - if (index) { - script->array_update(n, num, true); - } - } + struct DBIterator *iter = db_iterator(map->pc_db); - if (!pc->reg_load) { - p->flag.update = 1; - } - } else if (str[0]) { - struct DBData prev; - if (index) { - script->array_update(n, num, false); - } - - p = ers_alloc(pc->str_reg_ers, struct script_reg_str); - p->value = aStrdup(str); - - if (!pc->reg_load) { - p->flag.update = 1; - } - p->flag.type = 1; - - if(n->vars->put(n->vars, DB->i642key(num), DB->ptr2data(p), &prev)) { - p = DB->data2ptr(&prev); - if (p->value) { - aFree(p->value); - } - ers_free(pc->str_reg_ers, p); + for (struct map_session_data *sd = dbi_first(iter); dbi_exists(iter); sd = dbi_next(iter)) { + if (sd != NULL && n == &sd->regs) { + pc->setregistry_str(sd, num, str); + break; } } + dbi_destroy(iter); } static void set_reg_pc_ref_num(struct script_state *st, struct reg_db *n, int64 num, const char *name, int val) { - struct script_reg_num *p = NULL; - unsigned int index = script_getvaridx(num); - - nullpo_retv(n); - - if ((p = i64db_get(n->vars, num)) != NULL) { - if (val) { - if (!p->value && index) { - script->array_update(n, num, false); - } - p->value = val; - } else { - p->value = 0; - if (index) { - script->array_update(n, num, true); - } - } - - if (!pc->reg_load) { - p->flag.update = 1; - } - } else if (val) { - struct DBData prev; - if (index) { - script->array_update(n, num, false); - } - - p = ers_alloc(pc->num_reg_ers, struct script_reg_num); - p->value = val; - - if (!pc->reg_load) { - p->flag.update = 1; - } - p->flag.type = 1; + struct DBIterator *iter = db_iterator(map->pc_db); - if(n->vars->put(n->vars, DB->i642key(num), DB->ptr2data(p), &prev)) { - p = DB->data2ptr(&prev); - ers_free(pc->num_reg_ers, p); + for (struct map_session_data *sd = dbi_first(iter); dbi_exists(iter); sd = dbi_next(iter)) { + if (sd != NULL && n == &sd->regs) { + pc->setregistry(sd, num, val); + break; } } + dbi_destroy(iter); } static void set_reg_npcscope_num(struct script_state *st, struct reg_db *n, int64 num, const char *name, int val) @@ -3459,6 +3413,19 @@ static int set_reg(struct script_state *st, struct map_session_data *sd, int64 n nullpo_ret(name); prefix = name[0]; + if (script->str_data[script_getvarid(num)].type != C_NAME && script->str_data[script_getvarid(num)].type != C_PARAM) { + ShowError("script:set_reg: not a variable! '%s'\n", name); + + // to avoid this don't do script->add_str(") without setting its type. + // either use script->add_variable() or manually set the type + + if (st) { + script->reportsrc(st); + st->state = END; + } + return 0; + } + if (strlen(name) > SCRIPT_VARNAME_LENGTH) { ShowError("script:set_reg: variable name too long. '%s'\n", name); if (st) { @@ -3578,12 +3545,26 @@ static int set_reg(struct script_state *st, struct map_session_data *sd, int64 n static int set_var(struct map_session_data *sd, char *name, void *val) { - return script->set_reg(NULL, sd, reference_uid(script->add_str(name),0), name, val, NULL); + int key = script->add_variable(name); + + if (script->str_data[key].type != C_NAME) { + ShowError("script:setd_sub: `%s` is already used by something that is not a variable.\n", name); + return -1; + } + + return script->set_reg(NULL, sd, reference_uid(key, 0), name, val, NULL); } static void setd_sub(struct script_state *st, struct map_session_data *sd, const char *varname, int elem, const void *value, struct reg_db *ref) { - script->set_reg(st, sd, reference_uid(script->add_str(varname),elem), varname, value, ref); + int key = script->add_variable(varname); + + if (script->str_data[key].type != C_NAME) { + ShowError("script:setd_sub: `%s` is already used by something that is not a variable.\n", varname); + return; + } + + script->set_reg(st, sd, reference_uid(key, elem), varname, value, ref); } /// Converts the data to a string @@ -4118,10 +4099,10 @@ static void op_2str(struct script_state *st, int op, const char *s1, const char int i; for (i = 0; i < offsetcount; i++) { libpcre->get_substring(s1, offsets, offsetcount, i, &pcre_match); - mapreg->setregstr(reference_uid(script->add_str("$@regexmatch$"), i), pcre_match); + mapreg->setregstr(reference_uid(script->add_variable("$@regexmatch$"), i), pcre_match); libpcre->free_substring(pcre_match); } - mapreg->setreg(script->add_str("$@regexmatchcount"), i); + mapreg->setreg(script->add_variable("$@regexmatchcount"), i); a = offsetcount; } else { // C_RE_NE a = (offsetcount == 0); @@ -4904,7 +4885,12 @@ static void script_cleararray_pc(struct map_session_data *sd, const char *varnam unsigned int i, *list = NULL, size = 0; int key; - key = script->add_str(varname); + key = script->add_variable(varname); + + if (script->str_data[key].type != C_NAME) { + ShowError("script:cleararray_pc: `%s` is already used by something that is not a variable.\n", varname); + return; + } if( !(src = script->array_src(NULL,sd,varname,NULL) ) ) return; @@ -4929,12 +4915,17 @@ static void script_setarray_pc(struct map_session_data *sd, const char *varname, { int key; - if( idx >= SCRIPT_MAX_ARRAYSIZE ) { + if (idx > SCRIPT_MAX_ARRAYSIZE) { ShowError("script_setarray_pc: Variable '%s' has invalid index '%u' (char_id=%d).\n", varname, idx, sd->status.char_id); return; } - key = ( refcache && refcache[0] ) ? refcache[0] : script->add_str(varname); + key = ( refcache && refcache[0] ) ? refcache[0] : script->add_variable(varname); + + if (script->str_data[key].type != C_NAME) { + ShowError("script:setarray_pc: `%s` is already used by something that is not a variable.\n", varname); + return; + } script->set_reg(NULL,sd,reference_uid(key, idx),varname,value,NULL); @@ -5907,10 +5898,14 @@ static bool script_sprintf_helper(struct script_state *st, int start, struct Str static BUILDIN(mes) { struct map_session_data *sd = script->rid2sd(st); + if (sd == NULL) return true; - clif->scriptmes(sd, st->oid, script_getstr(st, 2)); + if (script_hasdata(st, 2)) + clif->scriptmes(sd, st->oid, script_getstr(st, 2)); + else + clif->scriptmes(sd, st->oid, ""); return true; } @@ -6104,11 +6099,11 @@ static BUILDIN(menu) sd->state.menu_or_input = 1; /* menus beyond this length crash the client (see bugreport:6402) */ - if( StrBuf->Length(&buf) >= 2047 ) { + if( StrBuf->Length(&buf) >= MAX_MENU_LENGTH - 1 ) { struct npc_data * nd = map->id2nd(st->oid); char* menu; - CREATE(menu, char, 2048); - safestrncpy(menu, StrBuf->Value(&buf), 2047); + CREATE(menu, char, MAX_MENU_LENGTH); + safestrncpy(menu, StrBuf->Value(&buf), MAX_MENU_LENGTH - 1); ShowWarning("NPC Menu too long! (source:%s / length:%d)\n",nd?nd->name:"Unknown",StrBuf->Length(&buf)); clif->scriptmenu(sd, st->oid, menu); aFree(menu); @@ -6117,13 +6112,13 @@ static BUILDIN(menu) StrBuf->Destroy(&buf); - if( sd->npc_menu >= 0xff ) + if( sd->npc_menu >= MAX_MENU_OPTIONS ) {// client supports only up to 254 entries; 0 is not used and 255 is reserved for cancel; excess entries are displayed but cause 'uint8' overflow - ShowWarning("buildin_menu: Too many options specified (current=%d, max=254).\n", sd->npc_menu); + ShowWarning("buildin_menu: Too many options specified (current=%d, max=%d).\n", sd->npc_menu, MAX_MENU_OPTIONS - 1); script->reportsrc(st); } } - else if( sd->npc_menu == 0xff ) + else if( sd->npc_menu == MAX_MENU_OPTIONS ) {// Cancel was pressed sd->state.menu_or_input = 0; st->state = END; @@ -6161,7 +6156,7 @@ static BUILDIN(menu) st->state = END; return false; } - pc->setreg(sd, script->add_str("@menu"), menu); + pc->setreg(sd, script->add_variable("@menu"), menu); st->pos = script_getnum(st, i + 1); st->state = GOTO; } @@ -6205,11 +6200,11 @@ static BUILDIN(select) sd->state.menu_or_input = 1; /* menus beyond this length crash the client (see bugreport:6402) */ - if( StrBuf->Length(&buf) >= 2047 ) { + if( StrBuf->Length(&buf) >= MAX_MENU_LENGTH - 1 ) { struct npc_data * nd = map->id2nd(st->oid); char* menu; - CREATE(menu, char, 2048); - safestrncpy(menu, StrBuf->Value(&buf), 2047); + CREATE(menu, char, MAX_MENU_LENGTH); + safestrncpy(menu, StrBuf->Value(&buf), MAX_MENU_LENGTH - 1); ShowWarning("NPC Menu too long! (source:%s / length:%d)\n",nd?nd->name:"Unknown",StrBuf->Length(&buf)); clif->scriptmenu(sd, st->oid, menu); aFree(menu); @@ -6217,107 +6212,31 @@ static BUILDIN(select) clif->scriptmenu(sd, st->oid, StrBuf->Value(&buf)); StrBuf->Destroy(&buf); - if( sd->npc_menu >= 0xff ) { - ShowWarning("buildin_select: Too many options specified (current=%d, max=254).\n", sd->npc_menu); + if( sd->npc_menu >= MAX_MENU_OPTIONS ) { + ShowWarning("buildin_select: Too many options specified (current=%d, max=%d).\n", sd->npc_menu, MAX_MENU_OPTIONS - 1); script->reportsrc(st); } - } else if( sd->npc_menu == 0xff ) {// Cancel was pressed - sd->state.menu_or_input = 0; - st->state = END; - } else {// return selected option - int menu = 0; - + } else if(sd->npc_menu == MAX_MENU_OPTIONS) { // Cancel was pressed sd->state.menu_or_input = 0; - for( i = 2; i <= script_lastdata(st); ++i ) { - text = script_getstr(st, i); - sd->npc_menu -= script->menu_countoptions(text, sd->npc_menu, &menu); - if( sd->npc_menu <= 0 ) - break;// entry found - } - pc->setreg(sd, script->add_str("@menu"), menu); - script_pushint(st, menu); - st->state = RUN; - } - return true; -} - -/// Displays a menu with options and returns the selected option. -/// Behaves like 'menu' without the target labels, except when cancel is -/// pressed. -/// When cancel is pressed, the script continues and 255 is returned. -/// -/// prompt(<option_text>{,<option_text>,...}) -> <selected_option> -/// -/// @see menu -static BUILDIN(prompt) -{ - int i; - const char *text; - struct map_session_data *sd = script->rid2sd(st); - if (sd == NULL) - return true; - -#ifdef SECURE_NPCTIMEOUT - sd->npc_idle_type = NPCT_MENU; -#endif - - if( sd->state.menu_or_input == 0 ) - { - struct StringBuf buf; - - StrBuf->Init(&buf); - sd->npc_menu = 0; - for( i = 2; i <= script_lastdata(st); ++i ) - { - text = script_getstr(st, i); - if( sd->npc_menu > 0 ) - StrBuf->AppendStr(&buf, ":"); - StrBuf->AppendStr(&buf, text); - sd->npc_menu += script->menu_countoptions(text, 0, NULL); - } - - st->state = RERUNLINE; - sd->state.menu_or_input = 1; - /* menus beyond this length crash the client (see bugreport:6402) */ - if( StrBuf->Length(&buf) >= 2047 ) { - struct npc_data * nd = map->id2nd(st->oid); - char* menu; - CREATE(menu, char, 2048); - safestrncpy(menu, StrBuf->Value(&buf), 2047); - ShowWarning("NPC Menu too long! (source:%s / length:%d)\n",nd?nd->name:"Unknown",StrBuf->Length(&buf)); - clif->scriptmenu(sd, st->oid, menu); - aFree(menu); - } else - clif->scriptmenu(sd, st->oid, StrBuf->Value(&buf)); - StrBuf->Destroy(&buf); - - if( sd->npc_menu >= 0xff ) - { - ShowWarning("buildin_prompt: Too many options specified (current=%d, max=254).\n", sd->npc_menu); - script->reportsrc(st); + if (strncmp(get_buildin_name(st), "prompt", 6) == 0) { + pc->setreg(sd, script->add_variable("@menu"), MAX_MENU_OPTIONS); + script_pushint(st, MAX_MENU_OPTIONS); // XXX: we should really be pushing -1 instead + st->state = RUN; + } else { + st->state = END; } - } - else if( sd->npc_menu == 0xff ) - {// Cancel was pressed - sd->state.menu_or_input = 0; - pc->setreg(sd, script->add_str("@menu"), 0xff); - script_pushint(st, 0xff); - st->state = RUN; - } - else - {// return selected option + } else {// return selected option int menu = 0; sd->state.menu_or_input = 0; - for( i = 2; i <= script_lastdata(st); ++i ) - { + for( i = 2; i <= script_lastdata(st); ++i ) { text = script_getstr(st, i); sd->npc_menu -= script->menu_countoptions(text, sd->npc_menu, &menu); if( sd->npc_menu <= 0 ) break;// entry found } - pc->setreg(sd, script->add_str("@menu"), menu); + pc->setreg(sd, script->add_variable("@menu"), menu); // TODO: throw a deprecation warning for scripts using @menu script_pushint(st, menu); st->state = RUN; } @@ -6936,7 +6855,7 @@ static BUILDIN(heal) hp=script_getnum(st,2); sp=script_getnum(st,3); - status->heal(&sd->bl, hp, sp, 1); + status->heal(&sd->bl, hp, sp, STATUS_HEAL_FORCED); return true; } /*========================================== @@ -7540,7 +7459,7 @@ static BUILDIN(getelementofarray) id = reference_getid(data); i = script_getnum(st, 3); - if (i < 0 || i >= SCRIPT_MAX_ARRAYSIZE) { + if (i < 0 || i > SCRIPT_MAX_ARRAYSIZE) { ShowWarning("script:getelementofarray: index out of range (%"PRId64")\n", i); script->reportdata(data); script_pushnil(st); @@ -7634,7 +7553,6 @@ static BUILDIN(viewpoint) *------------------------------------------*/ static BUILDIN(countitem) { - int nameid, i; int count = 0; struct item_data* id = NULL; @@ -7656,11 +7574,12 @@ static BUILDIN(countitem) return false; } - nameid = id->nameid; + int nameid = id->nameid; - for(i = 0; i < MAX_INVENTORY; i++) - if(sd->status.inventory[i].nameid == nameid) + for (int i = 0; i < sd->status.inventorySize; i++) { + if (sd->status.inventory[i].nameid == nameid) count += sd->status.inventory[i].amount; + } script_pushint(st,count); return true; @@ -7674,7 +7593,6 @@ static BUILDIN(countitem2) { int nameid, iden, ref, attr, c1, c2, c3, c4; int count = 0; - int i; struct item_data* id = NULL; struct map_session_data *sd = script->rid2sd(st); @@ -7704,7 +7622,7 @@ static BUILDIN(countitem2) c3 = script_getnum(st,8); c4 = script_getnum(st,9); - for(i = 0; i < MAX_INVENTORY; i++) + for (int i = 0; i < sd->status.inventorySize; i++) if (sd->status.inventory[i].nameid > 0 && sd->inventory_data[i] != NULL && sd->status.inventory[i].amount > 0 && sd->status.inventory[i].nameid == nameid && sd->status.inventory[i].identify == iden && sd->status.inventory[i].refine == ref && @@ -8779,32 +8697,23 @@ static BUILDIN(getcharid) return true; } + /*========================================== * returns the GID of an NPC *------------------------------------------*/ static BUILDIN(getnpcid) { - int num = script_getnum(st,2); - struct npc_data* nd = NULL; - - if( script_hasdata(st,3) ) - {// unique npc name - if( ( nd = npc->name2id(script_getstr(st,3)) ) == NULL ) - { - ShowError("buildin_getnpcid: No such NPC '%s'.\n", script_getstr(st,3)); - script_pushint(st,0); - return false; + if (script_hasdata(st, 2)) { + if (script_isinttype(st, 2)) { + // Deprecate old form - getnpcid(<type>{, <"npc name">}) + ShowWarning("buildin_getnpcid: Use of type is deprecated. Format - getnpcid({<\"npc name\">})\n"); + script_pushint(st, 0); + } else { + struct npc_data *nd = npc->name2id(script_getstr(st, 2)); + script_pushint(st, (nd != NULL) ? nd->bl.id : 0); } - } - - switch (num) { - case 0: - script_pushint(st,nd ? nd->bl.id : st->oid); - break; - default: - ShowError("buildin_getnpcid: invalid parameter (%d).\n", num); - script_pushint(st,0); - return false; + } else { + script_pushint(st, st->oid); } return true; @@ -8856,19 +8765,19 @@ static BUILDIN(getpartymember) if(p->party.member[i].account_id) { switch (type) { case 2: - mapreg->setreg(reference_uid(script->add_str("$@partymemberaid"), j),p->party.member[i].account_id); + mapreg->setreg(reference_uid(script->add_variable("$@partymemberaid"), j),p->party.member[i].account_id); break; case 1: - mapreg->setreg(reference_uid(script->add_str("$@partymembercid"), j),p->party.member[i].char_id); + mapreg->setreg(reference_uid(script->add_variable("$@partymembercid"), j),p->party.member[i].char_id); break; default: - mapreg->setregstr(reference_uid(script->add_str("$@partymembername$"), j),p->party.member[i].name); + mapreg->setregstr(reference_uid(script->add_variable("$@partymembername$"), j),p->party.member[i].name); } j++; } } } - mapreg->setreg(script->add_str("$@partymembercount"),j); + mapreg->setreg(script->add_variable("$@partymembercount"),j); return true; } @@ -8998,20 +8907,20 @@ static BUILDIN(getguildmember) if ( g->member[i].account_id ) { switch (type) { case 2: - mapreg->setreg(reference_uid(script->add_str("$@guildmemberaid"), j),g->member[i].account_id); + mapreg->setreg(reference_uid(script->add_variable("$@guildmemberaid"), j),g->member[i].account_id); break; case 1: - mapreg->setreg(reference_uid(script->add_str("$@guildmembercid"), j), g->member[i].char_id); + mapreg->setreg(reference_uid(script->add_variable("$@guildmembercid"), j), g->member[i].char_id); break; default: - mapreg->setregstr(reference_uid(script->add_str("$@guildmembername$"), j), g->member[i].name); + mapreg->setregstr(reference_uid(script->add_variable("$@guildmembername$"), j), g->member[i].name); break; } j++; } } } - mapreg->setreg(script->add_str("$@guildmembercount"), j); + mapreg->setreg(script->add_variable("$@guildmembercount"), j); return true; } @@ -9238,13 +9147,15 @@ static BUILDIN(getequipname) *------------------------------------------*/ static BUILDIN(getbrokenid) { - int i,num,id=0,brokencounter=0; + int num,id=0,brokencounter=0; struct map_session_data *sd = script->rid2sd(st); if (sd == NULL) return true; num=script_getnum(st,2); - for(i=0; i<MAX_INVENTORY; i++) { + for (int i = 0; i < sd->status.inventorySize; i++) { + if (sd->status.inventory[i].card[0] == CARD0_PET) + continue; if ((sd->status.inventory[i].attribute & ATTR_BROKEN) != 0) { brokencounter++; if(num==brokencounter) { @@ -9269,7 +9180,9 @@ static BUILDIN(getbrokencount) if (sd == NULL) return true; - for (i = 0; i < MAX_INVENTORY; i++) { + for (i = 0; i < sd->status.inventorySize; i++) { + if (sd->status.inventory[i].card[0] == CARD0_PET) + continue; if ((sd->status.inventory[i].attribute & ATTR_BROKEN) != 0) counter++; } @@ -9284,20 +9197,21 @@ static BUILDIN(getbrokencount) *------------------------------------------*/ static BUILDIN(repair) { - int i,num; int repaircounter=0; struct map_session_data *sd = script->rid2sd(st); if (sd == NULL) return true; - num=script_getnum(st,2); - for(i=0; i<MAX_INVENTORY; i++) { + int num = script_getnum(st, 2); + for(int i = 0; i < sd->status.inventorySize; i++) { + if (sd->status.inventory[i].card[0] == CARD0_PET) + continue; if ((sd->status.inventory[i].attribute & ATTR_BROKEN) != 0) { repaircounter++; if(num==repaircounter) { sd->status.inventory[i].attribute |= ATTR_BROKEN; sd->status.inventory[i].attribute ^= ATTR_BROKEN; - clif->equiplist(sd); + clif->equipList(sd); clif->produce_effect(sd, 0, sd->status.inventory[i].nameid); clif->misceffect(&sd->bl, 3); break; @@ -9313,13 +9227,15 @@ static BUILDIN(repair) *------------------------------------------*/ static BUILDIN(repairall) { - int i, repaircounter = 0; + int repaircounter = 0; struct map_session_data *sd = script->rid2sd(st); if (sd == NULL) return true; - for(i = 0; i < MAX_INVENTORY; i++) + for (int i = 0; i < sd->status.inventorySize; i++) { + if (sd->status.inventory[i].card[0] == CARD0_PET) + continue; if (sd->status.inventory[i].nameid && (sd->status.inventory[i].attribute & ATTR_BROKEN) != 0) { sd->status.inventory[i].attribute |= ATTR_BROKEN; @@ -9332,7 +9248,7 @@ static BUILDIN(repairall) if(repaircounter) { clif->misceffect(&sd->bl, 3); - clif->equiplist(sd); + clif->equipList(sd); } return true; @@ -9574,6 +9490,11 @@ static BUILDIN(successrefitem) clif->additem(sd,i,1,0); pc->equipitem(sd,i,ep); clif->misceffect(&sd->bl,3); + + achievement->validate_refine(sd, i, true); // Achievements [Smokexyz/Hercules] + + /* The following check is exclusive to characters (possibly only whitesmiths) + * that create equipments and refine them to level 10. */ if(sd->status.inventory[i].refine == 10 && sd->status.inventory[i].card[0] == CARD0_FORGE && sd->status.char_id == (int)MakeDWord(sd->status.inventory[i].card[2],sd->status.inventory[i].card[3]) @@ -9611,6 +9532,9 @@ static BUILDIN(failedrefitem) if (num > 0 && num <= ARRAYLENGTH(script->equip)) i=pc->checkequip(sd,script->equip[num-1]); if(i >= 0) { + // Call before changing refine to 0. + achievement->validate_refine(sd, i, false); + sd->status.inventory[i].refine = 0; pc->unequipitem(sd, i, PCUNEQUIPITEM_RECALC|PCUNEQUIPITEM_FORCE); //recalculate bonus clif->refine(sd->fd,1,i,sd->status.inventory[i].refine); //notify client of failure @@ -9658,6 +9582,9 @@ static BUILDIN(downrefitem) clif->additem(sd,i,1,0); pc->equipitem(sd,i,ep); + + achievement->validate_refine(sd, i, false); // Achievements [Smokexyz/Hercules] + clif->misceffect(&sd->bl,2); } @@ -9728,6 +9655,25 @@ static BUILDIN(statusup2) return true; } + +/*========================================== +* Returns the number of stat points needed to change the specified stat by val. +* needed_status_point(<type>,<val>{,<char id>}); [secretdataz] +*------------------------------------------*/ +static BUILDIN(needed_status_point) +{ + int type = script_getnum(st, 2); + int val = script_getnum(st, 3);; + struct map_session_data *sd = script->rid2sd(st); + + if (sd == NULL) + script_pushint(st, 0); + else + script_pushint(st, pc->need_status_point(sd, type, val)); + + return true; +} + /// See 'doc/item_bonus.txt' /// /// bonus <bonus type>,<val1>; @@ -10897,13 +10843,13 @@ static BUILDIN(getmobdrops) if( itemdb->exists(monster->dropitem[i].nameid) == NULL ) continue; - mapreg->setreg(reference_uid(script->add_str("$@MobDrop_item"), j), monster->dropitem[i].nameid); - mapreg->setreg(reference_uid(script->add_str("$@MobDrop_rate"), j), monster->dropitem[i].p); + mapreg->setreg(reference_uid(script->add_variable("$@MobDrop_item"), j), monster->dropitem[i].nameid); + mapreg->setreg(reference_uid(script->add_variable("$@MobDrop_rate"), j), monster->dropitem[i].p); j++; } - mapreg->setreg(script->add_str("$@MobDrop_count"), j); + mapreg->setreg(script->add_variable("$@MobDrop_count"), j); script_pushint(st, 1); return true; @@ -11362,7 +11308,7 @@ static int buildin_getunits_sub(struct block_list *bl, va_list ap) (const void *)h64BPTRSIZE(bl->id), ref); (*count)++; - return 0; + return 1; } static int buildin_getunits_sub_pc(struct map_session_data *sd, va_list ap) @@ -11434,18 +11380,10 @@ static BUILDIN(getunits) int16 x2 = script_getnum(st, 8); int16 y2 = script_getnum(st, 9); - // FIXME: map_foreachinarea does NOT stop iterating when the callback - // function returns -1. we still limit the array size, but - // this doesn't break the loop. We need a foreach function - // that behaves like map_foreachiddb, but for areas - map->foreachinarea(buildin_getunits_sub, m, x1, y1, x2, y2, type, + map->forcountinarea(buildin_getunits_sub, m, x1, y1, x2, y2, limit, type, st, sd, id, start, &count, limit, name, ref, type); } else { - // FIXME: map_foreachinmap does NOT stop iterating when the callback - // function returns -1. we still limit the array size, but - // this doesn't break the loop. We need a foreach function - // that behaves like map_foreachiddb, but for maps - map->foreachinmap(buildin_getunits_sub, m, type, + map->forcountinmap(buildin_getunits_sub, m, limit, type, st, sd, id, start, &count, limit, name, ref, type); } } else { @@ -12254,7 +12192,9 @@ static BUILDIN(sc_end) } //This should help status_change_end force disabling the SC in case it has no limit. - sce->val1 = sce->val2 = sce->val3 = sce->val4 = 0; + if (type != SC_BERSERK) + sce->val1 = 0; // SC_BERSERK requires skill_lv that's stored in sce->val1 when being removed [KirieZ] + sce->val2 = sce->val3 = sce->val4 = 0; status_change_end(bl, (sc_type)type, INVALID_TIMER); } else @@ -12341,9 +12281,18 @@ static BUILDIN(getstatus) *------------------------------------------*/ static BUILDIN(debugmes) { - const char *str; - str=script_getstr(st,2); - ShowDebug("script debug : %d %d : %s\n",st->rid,st->oid,str); + struct StringBuf buf; + StrBuf->Init(&buf); + + if (!script->sprintf_helper(st, 2, &buf)) { + StrBuf->Destroy(&buf); + script_pushint(st, 0); + return false; + } + + ShowDebug("script debug : %d %d : %s\n", st->rid, st->oid, StrBuf->Value(&buf)); + StrBuf->Destroy(&buf); + script_pushint(st, 1); return true; } @@ -12834,7 +12783,7 @@ static BUILDIN(getwaitingroomstate) for (i = 0; i < cd->users; i++) { struct map_session_data *sd = cd->usersd[i]; nullpo_retr(false, sd); - mapreg->setreg(reference_uid(script->add_str("$@chatmembers"), i), sd->bl.id); + mapreg->setreg(reference_uid(script->add_variable("$@chatmembers"), i), sd->bl.id); } script_pushint(st, cd->users); break; @@ -12904,7 +12853,7 @@ static BUILDIN(warpwaitingpc) pc->payzeny(sd, cd->zeny, LOG_TYPE_NPC, NULL); } - mapreg->setreg(reference_uid(script->add_str("$@warpwaitingpc"), i), sd->bl.id); + mapreg->setreg(reference_uid(script->add_variable("$@warpwaitingpc"), i), sd->bl.id); if( strcmp(map_name,"Random") == 0 ) pc->randomwarp(sd,CLR_TELEPORT); @@ -12913,7 +12862,7 @@ static BUILDIN(warpwaitingpc) else pc->setpos(sd, script->mapindexname2id(st,map_name), x, y, CLR_OUTSIGHT); } - mapreg->setreg(script->add_str("$@warpwaitingpcnum"), i); + mapreg->setreg(script->add_variable("$@warpwaitingpcnum"), i); return true; } @@ -13200,6 +13149,7 @@ static BUILDIN(setmapflag) case MF_PVP_NOGUILD: map->list[m].flag.pvp_noguild = 1; break; case MF_GVG: { struct block_list bl; + memset(&bl, 0, sizeof(bl)); map->list[m].flag.gvg = 1; clif->map_property_mapall(m, MAPPROPERTY_AGITZONE); bl.type = BL_NUL; @@ -13286,6 +13236,7 @@ static BUILDIN(removemapflag) case MF_NOZENYPENALTY: map->list[m].flag.nozenypenalty = 0; break; case MF_PVP: { struct block_list bl; + memset(&bl, 0, sizeof(bl)); bl.type = BL_NUL; bl.m = m; map->list[m].flag.pvp = 0; @@ -13297,6 +13248,7 @@ static BUILDIN(removemapflag) case MF_PVP_NOGUILD: map->list[m].flag.pvp_noguild = 0; break; case MF_GVG: { struct block_list bl; + memset(&bl, 0, sizeof(bl)); bl.type = BL_NUL; bl.m = m; map->list[m].flag.gvg = 0; @@ -13364,6 +13316,7 @@ static BUILDIN(pvpon) struct s_mapiterator* iter; struct block_list bl; + memset(&bl, 0, sizeof(bl)); str = script_getstr(st,2); m = map->mapname2mapid(str); if( m < 0 || map->list[m].flag.pvp ) @@ -13424,6 +13377,7 @@ static BUILDIN(pvpoff) const char *str; struct block_list bl; + memset(&bl, 0, sizeof(bl)); str=script_getstr(st,2); m = map->mapname2mapid(str); if(m < 0 || !map->list[m].flag.pvp) @@ -13453,6 +13407,7 @@ static BUILDIN(gvgon) if(m >= 0 && !map->list[m].flag.gvg) { struct block_list bl; + memset(&bl, 0, sizeof(bl)); if( !strdb_exists(map->zone_db,MAP_ZONE_GVG_NAME) ) { ShowError("buildin_gvgon: zone_db missing '%s'\n",MAP_ZONE_GVG_NAME); return true; @@ -13477,6 +13432,7 @@ static BUILDIN(gvgoff) m = map->mapname2mapid(str); if(m >= 0 && map->list[m].flag.gvg) { struct block_list bl; + memset(&bl, 0, sizeof(bl)); map->zone_change2(m, map->list[m].prev_zone); map->list[m].flag.gvg = 0; clif->map_property_mapall(m, MAPPROPERTY_NOTHING); @@ -14383,6 +14339,9 @@ static BUILDIN(getiteminfo) case ITEMINFO_VIEWSPRITE: script_pushint(st, it->view_sprite); break; + case ITEMINFO_TRADE: + script_pushint(st, it->flag.trade_restriction); + break; default: ShowError("buildin_getiteminfo: Invalid item type %d.\n", n); script_pushint(st,-1); @@ -14649,6 +14608,9 @@ static BUILDIN(setiteminfo) case ITEMINFO_VIEWSPRITE: it->view_sprite = value; break; + case ITEMINFO_TRADE: + it->flag.trade_restriction = value; + break; default: ShowError("buildin_setiteminfo: invalid type %d.\n", n); script_pushint(st,-1); @@ -14777,39 +14739,39 @@ static BUILDIN(getinventorylist) struct map_session_data *sd = script->rid2sd(st); char card_var[SCRIPT_VARNAME_LENGTH]; - int i,j=0,k; + int j=0,k; if(!sd) return true; - for(i=0;i<MAX_INVENTORY;i++) { + for (int i = 0;i < sd->status.inventorySize; i++) { if(sd->status.inventory[i].nameid > 0 && sd->status.inventory[i].amount > 0) { - pc->setreg(sd,reference_uid(script->add_str("@inventorylist_id"), j),sd->status.inventory[i].nameid); - pc->setreg(sd,reference_uid(script->add_str("@inventorylist_amount"), j),sd->status.inventory[i].amount); + pc->setreg(sd,reference_uid(script->add_variable("@inventorylist_id"), j),sd->status.inventory[i].nameid); + pc->setreg(sd,reference_uid(script->add_variable("@inventorylist_amount"), j),sd->status.inventory[i].amount); if(sd->status.inventory[i].equip) { - pc->setreg(sd,reference_uid(script->add_str("@inventorylist_equip"), j),pc->equippoint(sd,i)); + pc->setreg(sd,reference_uid(script->add_variable("@inventorylist_equip"), j),pc->equippoint(sd,i)); } else { - pc->setreg(sd,reference_uid(script->add_str("@inventorylist_equip"), j),0); + pc->setreg(sd,reference_uid(script->add_variable("@inventorylist_equip"), j),0); } - pc->setreg(sd,reference_uid(script->add_str("@inventorylist_refine"), j),sd->status.inventory[i].refine); - pc->setreg(sd,reference_uid(script->add_str("@inventorylist_identify"), j),sd->status.inventory[i].identify); - pc->setreg(sd,reference_uid(script->add_str("@inventorylist_attribute"), j),sd->status.inventory[i].attribute); + pc->setreg(sd,reference_uid(script->add_variable("@inventorylist_refine"), j),sd->status.inventory[i].refine); + pc->setreg(sd,reference_uid(script->add_variable("@inventorylist_identify"), j),sd->status.inventory[i].identify); + pc->setreg(sd,reference_uid(script->add_variable("@inventorylist_attribute"), j),sd->status.inventory[i].attribute); for (k = 0; k < MAX_SLOTS; k++) { sprintf(card_var, "@inventorylist_card%d",k+1); - pc->setreg(sd,reference_uid(script->add_str(card_var), j),sd->status.inventory[i].card[k]); + pc->setreg(sd,reference_uid(script->add_variable(card_var), j),sd->status.inventory[i].card[k]); } for (k = 0; k < MAX_ITEM_OPTIONS; k++) { sprintf(card_var, "@inventorylist_opt_id%d", k + 1); - pc->setreg(sd, reference_uid(script->add_str(card_var), j), sd->status.inventory[i].option[k].index); + pc->setreg(sd, reference_uid(script->add_variable(card_var), j), sd->status.inventory[i].option[k].index); sprintf(card_var, "@inventorylist_opt_val%d", k + 1); - pc->setreg(sd, reference_uid(script->add_str(card_var), j), sd->status.inventory[i].option[k].value); + pc->setreg(sd, reference_uid(script->add_variable(card_var), j), sd->status.inventory[i].option[k].value); sprintf(card_var, "@inventorylist_opt_param%d", k + 1); - pc->setreg(sd, reference_uid(script->add_str(card_var), j), sd->status.inventory[i].option[k].param); + pc->setreg(sd, reference_uid(script->add_variable(card_var), j), sd->status.inventory[i].option[k].param); } - pc->setreg(sd,reference_uid(script->add_str("@inventorylist_expire"), j),sd->status.inventory[i].expire_time); - pc->setreg(sd,reference_uid(script->add_str("@inventorylist_bound"), j),sd->status.inventory[i].bound); + pc->setreg(sd,reference_uid(script->add_variable("@inventorylist_expire"), j),sd->status.inventory[i].expire_time); + pc->setreg(sd,reference_uid(script->add_variable("@inventorylist_bound"), j),sd->status.inventory[i].bound); j++; } } - pc->setreg(sd,script->add_str("@inventorylist_count"),j); + pc->setreg(sd,script->add_variable("@inventorylist_count"),j); return true; } @@ -14823,30 +14785,30 @@ static BUILDIN(getcartinventorylist) for(i=0;i<MAX_CART;i++) { if(sd->status.cart[i].nameid > 0 && sd->status.cart[i].amount > 0) { - pc->setreg(sd,reference_uid(script->add_str("@cartinventorylist_id"), j),sd->status.cart[i].nameid); - pc->setreg(sd,reference_uid(script->add_str("@cartinventorylist_amount"), j),sd->status.cart[i].amount); - pc->setreg(sd,reference_uid(script->add_str("@cartinventorylist_equip"), j),sd->status.cart[i].equip); - pc->setreg(sd,reference_uid(script->add_str("@cartinventorylist_refine"), j),sd->status.cart[i].refine); - pc->setreg(sd,reference_uid(script->add_str("@cartinventorylist_identify"), j),sd->status.cart[i].identify); - pc->setreg(sd,reference_uid(script->add_str("@cartinventorylist_attribute"), j),sd->status.cart[i].attribute); + pc->setreg(sd,reference_uid(script->add_variable("@cartinventorylist_id"), j),sd->status.cart[i].nameid); + pc->setreg(sd,reference_uid(script->add_variable("@cartinventorylist_amount"), j),sd->status.cart[i].amount); + pc->setreg(sd,reference_uid(script->add_variable("@cartinventorylist_equip"), j),sd->status.cart[i].equip); + pc->setreg(sd,reference_uid(script->add_variable("@cartinventorylist_refine"), j),sd->status.cart[i].refine); + pc->setreg(sd,reference_uid(script->add_variable("@cartinventorylist_identify"), j),sd->status.cart[i].identify); + pc->setreg(sd,reference_uid(script->add_variable("@cartinventorylist_attribute"), j),sd->status.cart[i].attribute); for (k = 0; k < MAX_SLOTS; k++) { sprintf(card_var, "@cartinventorylist_card%d",k+1); - pc->setreg(sd,reference_uid(script->add_str(card_var), j),sd->status.cart[i].card[k]); + pc->setreg(sd,reference_uid(script->add_variable(card_var), j),sd->status.cart[i].card[k]); } for (k = 0; k < MAX_ITEM_OPTIONS; k++) { sprintf(card_var, "@cartinventorylist_opt_id%d", k + 1); - pc->setreg(sd, reference_uid(script->add_str(card_var), j), sd->status.cart[i].option[k].index); + pc->setreg(sd, reference_uid(script->add_variable(card_var), j), sd->status.cart[i].option[k].index); sprintf(card_var, "@cartinventorylist_opt_val%d", k + 1); - pc->setreg(sd, reference_uid(script->add_str(card_var), j), sd->status.cart[i].option[k].value); + pc->setreg(sd, reference_uid(script->add_variable(card_var), j), sd->status.cart[i].option[k].value); sprintf(card_var, "@cartinventorylist_opt_param%d", k + 1); - pc->setreg(sd, reference_uid(script->add_str(card_var), j), sd->status.cart[i].option[k].param); + pc->setreg(sd, reference_uid(script->add_variable(card_var), j), sd->status.cart[i].option[k].param); } - pc->setreg(sd,reference_uid(script->add_str("@cartinventorylist_expire"), j),sd->status.cart[i].expire_time); - pc->setreg(sd,reference_uid(script->add_str("@cartinventorylist_bound"), j),sd->status.cart[i].bound); + pc->setreg(sd,reference_uid(script->add_variable("@cartinventorylist_expire"), j),sd->status.cart[i].expire_time); + pc->setreg(sd,reference_uid(script->add_variable("@cartinventorylist_bound"), j),sd->status.cart[i].bound); j++; } } - pc->setreg(sd,script->add_str("@cartinventorylist_count"),j); + pc->setreg(sd,script->add_variable("@cartinventorylist_count"),j); return true; } @@ -14858,23 +14820,22 @@ static BUILDIN(getskilllist) return true; for (i = 0; i < MAX_SKILL_DB; i++) { if(sd->status.skill[i].id > 0 && sd->status.skill[i].lv > 0) { - pc->setreg(sd,reference_uid(script->add_str("@skilllist_id"), j),sd->status.skill[i].id); - pc->setreg(sd,reference_uid(script->add_str("@skilllist_lv"), j),sd->status.skill[i].lv); - pc->setreg(sd,reference_uid(script->add_str("@skilllist_flag"), j),sd->status.skill[i].flag); + pc->setreg(sd,reference_uid(script->add_variable("@skilllist_id"), j),sd->status.skill[i].id); + pc->setreg(sd,reference_uid(script->add_variable("@skilllist_lv"), j),sd->status.skill[i].lv); + pc->setreg(sd,reference_uid(script->add_variable("@skilllist_flag"), j),sd->status.skill[i].flag); j++; } } - pc->setreg(sd,script->add_str("@skilllist_count"),j); + pc->setreg(sd,script->add_variable("@skilllist_count"),j); return true; } static BUILDIN(clearitem) { struct map_session_data *sd = script->rid2sd(st); - int i; if (sd == NULL) return true; - for (i=0; i<MAX_INVENTORY; i++) { + for (int i = 0; i < sd->status.inventorySize; i++) { if (sd->status.inventory[i].amount) { pc->delitem(sd, i, sd->status.inventory[i].amount, 0, DELITEM_NORMAL, LOG_TYPE_SCRIPT); } @@ -15331,6 +15292,50 @@ static BUILDIN(specialeffect2) return true; } +static BUILDIN(removespecialeffect) +{ + struct block_list *bl = NULL; + int type = script_getnum(st, 2); + enum send_target target = AREA; + + if (script_hasdata(st, 3)) { + target = script_getnum(st, 3); + } + + if (script_hasdata(st, 4)) { + if (script_isstringtype(st, 4)) { + struct npc_data *nd = npc->name2id(script_getstr(st, 4)); + if (nd != NULL) { + bl = &nd->bl; + } + } else { + bl = map->id2bl(script_getnum(st, 4)); + } + } else { + bl = map->id2bl(st->oid); + } + + if (bl == NULL) { + return true; + } + + if (target == SELF) { + struct map_session_data *sd; + if (script_hasdata(st, 5)) { + sd = map->id2sd(script_getnum(st, 5)); + } else { + sd = script->rid2sd(st); + } + if (sd != NULL) { + clif->removeSpecialEffect_single(bl, type, &sd->bl); + } + } else { + clif->removeSpecialEffect(bl, type, target); + } + + return true; +} + /*========================================== * Nude [Valaris] *------------------------------------------*/ @@ -15601,19 +15606,18 @@ static BUILDIN(getmercinfo) *------------------------------------------*/ static BUILDIN(checkequipedcard) { - int n,i,c=0; struct map_session_data *sd = script->rid2sd(st); if (sd == NULL) return true; - c = script_getnum(st,2); + int c = script_getnum(st,2); - for( i=0; i<MAX_INVENTORY; i++) { + for (int i = 0; i < sd->status.inventorySize; i++) { if(sd->status.inventory[i].nameid > 0 && sd->status.inventory[i].amount && sd->inventory_data[i]) { if (itemdb_isspecial(sd->status.inventory[i].card[0])) continue; - for(n=0;n<sd->inventory_data[i]->slot;n++) { + for (int n = 0; n < sd->inventory_data[i]->slot; n++) { if(sd->status.inventory[i].card[n]==c) { script_pushint(st,1); return true; @@ -15689,6 +15693,29 @@ static BUILDIN(message) return true; } +static BUILDIN(servicemessage) +{ + struct map_session_data *sd = NULL; + + if (script_hasdata(st, 4)) { + if (script_isstringtype(st, 4)) + sd = script->nick2sd(st, script_getstr(st, 4)); + else + sd = script->id2sd(st, script_getnum(st, 4)); + } else { + sd = script->rid2sd(st); + } + + if (sd == NULL) + return true; + + const char *message = script_getstr(st, 2); + const int color = script_getnum(st, 3); + clif->serviceMessageColor(sd, color, message); + + return true; +} + /*========================================== * npctalk (sends message to surrounding area) * usage: npctalk("<message>"{, "<npc name>"{, <show_name>}}); @@ -16458,9 +16485,9 @@ static BUILDIN(equip) ShowError("wrong item ID : equipitem(%d)\n",nameid); return false; } - ARR_FIND( 0, MAX_INVENTORY, i, sd->status.inventory[i].nameid == nameid && sd->status.inventory[i].equip == 0 ); - if( i < MAX_INVENTORY ) - pc->equipitem(sd,i,item_data->equip); + ARR_FIND(0, sd->status.inventorySize, i, sd->status.inventory[i].nameid == nameid && sd->status.inventory[i].equip == 0); + if (i < sd->status.inventorySize) + pc->equipitem(sd, i, item_data->equip); return true; } @@ -16518,21 +16545,21 @@ static BUILDIN(equip2) c2 = script_getnum(st, 7); c3 = script_getnum(st, 8); - ARR_FIND( 0, MAX_INVENTORY, i,( sd->status.inventory[i].equip == 0 && + ARR_FIND(0, sd->status.inventorySize, i, (sd->status.inventory[i].equip == 0 && sd->status.inventory[i].nameid == nameid && sd->status.inventory[i].refine == ref && sd->status.inventory[i].attribute == attr && sd->status.inventory[i].card[0] == c0 && sd->status.inventory[i].card[1] == c1 && sd->status.inventory[i].card[2] == c2 && - sd->status.inventory[i].card[3] == c3 ) ); + sd->status.inventory[i].card[3] == c3)); - if( i < MAX_INVENTORY ) { + if (i < sd->status.inventorySize) { script_pushint(st,1); pc->equipitem(sd,i,item_data->equip); - } - else + } else { script_pushint(st,0); + } return true; } @@ -16719,6 +16746,63 @@ static BUILDIN(getdatatype) return true; } +static BUILDIN(data_to_string) +{ + if (script_hasdata(st, 2)) { + struct script_data *data = script_getdata(st, 2); + + if (data_isstring(data)) { + script_pushcopy(st, 2); + } else if (data_isint(data)) { + char *str = NULL; + + CREATE(str, char, 20); + safesnprintf(str, 20, "%"PRId64"", data->u.num); + script_pushstr(st, str); + } else if (data_islabel(data)) { + const char *str = ""; + + // XXX: because all we have is the label pos we can't be sure which + // one is the correct label if more than one has the same pos. + // We might want to store both the pos and str_data index in + // data->u.num, similar to how C_NAME stores both the array + // index and str_data index in u.num with bitmasking. This + // would also avoid the awkward for() loops as we could + // directly access the string with script->get_str(). + + if (st->oid) { + struct npc_data *nd = map->id2nd(st->oid); + + for (int i = 0; i < nd->u.scr.label_list_num; ++i) { + if (nd->u.scr.label_list[i].pos == data->u.num) { + str = nd->u.scr.label_list[i].name; + break; + } + } + } else { + for (int i = LABEL_START; script->str_data[i].next != 0; i = script->str_data[i].next) { + if (script->str_data[i].label == data->u.num) { + str = script->get_str(i); + break; + } + } + } + + script_pushconststr(st, str); + } else if (data_isreference(data)) { + script_pushstrcopy(st, reference_getname(data)); + } else { + ShowWarning("script:data_to_string: unknown data type!\n"); + script->reportdata(data); + script_pushconststr(st, ""); + } + } else { + script_pushconststr(st, ""); // NIL + } + + return true; +} + //======================================================= // chr <int> //------------------------------------------------------- @@ -17764,14 +17848,25 @@ static BUILDIN(getd) char varname[100]; const char *buffer; int elem; + int id; buffer = script_getstr(st, 2); if (sscanf(buffer, "%99[^[][%d]", varname, &elem) < 2) elem = 0; + id = script->add_variable(varname); + + if (script->str_data[id].type != C_NAME && // variable + script->str_data[id].type != C_PARAM && // param + script->str_data[id].type != C_INT) { // constant + ShowError("script:getd: `%s` is already used by something that is not a variable.\n", varname); + st->state = END; + return false; + } + // Push the 'pointer' so it's more flexible [Lance] - script->push_val(st->stack, C_NAME, reference_uid(script->add_str(varname), elem),NULL); + script->push_val(st->stack, C_NAME, reference_uid(id, elem),NULL); return true; } @@ -18724,7 +18819,7 @@ static BUILDIN(setunitdata) md->level = val; break; case UDT_HP: - status->set_hp(bl, (unsigned int) val, 0); + status->set_hp(bl, (unsigned int) val, STATUS_HEAL_DEFAULT); clif->charnameack(0, &md->bl); break; case UDT_MAXHP: @@ -18732,7 +18827,7 @@ static BUILDIN(setunitdata) clif->charnameack(0, &md->bl); break; case UDT_SP: - status->set_sp(bl, (unsigned int) val, 0); + status->set_sp(bl, (unsigned int) val, STATUS_HEAL_DEFAULT); break; case UDT_MAXSP: md->status.max_sp = (unsigned int) val; @@ -18893,13 +18988,13 @@ static BUILDIN(setunitdata) hd->homunculus.level = (short) val; break; case UDT_HP: - status->set_hp(bl, (unsigned int) val, 0); + status->set_hp(bl, (unsigned int) val, STATUS_HEAL_DEFAULT); break; case UDT_MAXHP: hd->homunculus.max_hp = val; break; case UDT_SP: - status->set_sp(bl, (unsigned int) val, 0); + status->set_sp(bl, (unsigned int) val, STATUS_HEAL_DEFAULT); break; case UDT_MAXSP: hd->homunculus.max_sp = val; @@ -19032,13 +19127,13 @@ static BUILDIN(setunitdata) pd->pet.level = (short) val; break; case UDT_HP: - status->set_hp(bl, (unsigned int) val, 0); + status->set_hp(bl, (unsigned int) val, STATUS_HEAL_DEFAULT); break; case UDT_MAXHP: pd->status.max_hp = (unsigned int) val; break; case UDT_SP: - status->set_sp(bl, (unsigned int) val, 0); + status->set_sp(bl, (unsigned int) val, STATUS_HEAL_DEFAULT); break; case UDT_MAXSP: pd->status.max_sp = (unsigned int) val; @@ -19166,13 +19261,13 @@ static BUILDIN(setunitdata) mc->base_status.size = (unsigned char) val; break; case UDT_HP: - status->set_hp(bl, (unsigned int) val, 0); + status->set_hp(bl, (unsigned int) val, STATUS_HEAL_DEFAULT); break; case UDT_MAXHP: mc->base_status.max_hp = (unsigned int) val; break; case UDT_SP: - status->set_sp(bl, (unsigned int) val, 0); + status->set_sp(bl, (unsigned int) val, STATUS_HEAL_DEFAULT); break; case UDT_MAXSP: mc->base_status.max_sp = (unsigned int) val; @@ -19300,13 +19395,13 @@ static BUILDIN(setunitdata) ed->base_status.size = (unsigned char) val; break; case UDT_HP: - status->set_hp(bl, (unsigned int) val, 0); + status->set_hp(bl, (unsigned int) val, STATUS_HEAL_DEFAULT); break; case UDT_MAXHP: ed->base_status.max_hp = (unsigned int) val; break; case UDT_SP: - status->set_sp(bl, (unsigned int) val, 0); + status->set_sp(bl, (unsigned int) val, STATUS_HEAL_DEFAULT); break; case UDT_MAXSP: ed->base_status.max_sp = (unsigned int) val; @@ -19432,13 +19527,13 @@ static BUILDIN(setunitdata) nd->level = (unsigned short) val; break; case UDT_HP: - status->set_hp(bl, (unsigned int) val, 0); + status->set_hp(bl, (unsigned int) val, STATUS_HEAL_DEFAULT); break; case UDT_MAXHP: nd->status.max_hp = (unsigned int) val; break; case UDT_SP: - status->set_sp(bl, (unsigned int) val, 0); + status->set_sp(bl, (unsigned int) val, STATUS_HEAL_DEFAULT); break; case UDT_MAXSP: nd->status.max_sp = (unsigned int) val; @@ -20592,7 +20687,7 @@ static BUILDIN(getvariableofpc) } if (!sd->regs.vars) - sd->regs.vars = i64db_alloc(DB_OPT_RELEASE_DATA); + sd->regs.vars = i64db_alloc(DB_OPT_BASE); script->push_val(st->stack, C_NAME, reference_getuid(data), &sd->regs); return true; @@ -20749,7 +20844,7 @@ static BUILDIN(mercenary_heal) hp = script_getnum(st,2); sp = script_getnum(st,3); - status->heal(&sd->md->bl, hp, sp, 0); + status->heal(&sd->md->bl, hp, sp, STATUS_HEAL_DEFAULT); return true; } @@ -21012,17 +21107,26 @@ static BUILDIN(setquestinfo) } case QINFO_ITEM: { - struct item item = { 0 }; + struct questinfo_itemreq item = { 0 }; item.nameid = script_getnum(st, 3); - item.amount = script_getnum(st, 4); + item.min = script_hasdata(st, 4) ? script_getnum(st, 4) : 0; + item.max = script_hasdata(st, 5) ? script_getnum(st, 5) : 0; if (itemdb->exists(item.nameid) == NULL) { ShowWarning("buildin_setquestinfo: non existing item (%d) have been given.\n", item.nameid); return false; } - if (item.amount <= 0 || item.amount > MAX_AMOUNT) { - ShowWarning("buildin_setquestinfo: given amount (%d) must be bigger than 0 and smaller than %d.\n", item.amount, MAX_AMOUNT + 1); + if (item.min > item.max) { + ShowWarning("buildin_setquestinfo: minimal amount (%d) is bigger than the maximal amount (%d).\n", item.min, item.max); + return false; + } + if (item.min < 0 || item.min > MAX_AMOUNT) { + ShowWarning("buildin_setquestinfo: given amount (%d) must be bigger than or equal to 0 and smaller than %d.\n", item.min, MAX_AMOUNT + 1); + return false; + } + if (item.max < 0 || item.max > MAX_AMOUNT) { + ShowWarning("buildin_setquestinfo: given amount (%d) must be bigger than or equal to 0 and smaller than %d.\n", item.max, MAX_AMOUNT + 1); return false; } if (VECTOR_LENGTH(qi->items) == 0) @@ -21075,6 +21179,17 @@ static BUILDIN(setquestinfo) VECTOR_PUSH(qi->quest_requirement, quest_req); break; } + case QINFO_MERCENARY_CLASS: + { + int mer_class = script_getnum(st, 3); + + if (!mercenary->class(mer_class)) { + ShowWarning("buildin_setquestinfo: invalid mercenary class given (%d).\n", mer_class); + return false; + } + qi->mercenary_class = mer_class; + break; + } default: ShowWarning("buildin_setquestinfo: invalid type given (%u).\n", type); return false; @@ -21228,24 +21343,85 @@ static BUILDIN(showevent) } } -#if PACKETVER >= 20170315 - if (icon < 0 || (icon > 10 && icon != 9999)) - icon = 9999; -#elif PACKETVER >= 20120410 - if (icon < 0 || (icon > 8 && icon != 9999) || icon == 7) - icon = 9999; // Default to nothing if icon id is invalid. -#else - if (icon < 0 || icon > 7) - icon = 0; - else - icon = icon + 1; -#endif + icon = quest->questinfo_validate_icon(icon); clif->quest_show_event(sd, &nd->bl, icon, color); return true; } /*========================================== + * Achievement System [Smokexyz/Hercules] + *-----------------------------------------*/ +/** + * Validates an objective index for the given achievement. + * Can be used for any achievement type. + * @command achievement_progress(<ach_id>,<obj_idx>,<progress>,<incremental?>{,<char_id>}); + * @param aid - achievement ID + * @param obj_idx - achievement objective index. + * @param progress - objective progress towards goal. + * @Param incremental - (boolean) true to add the progress towards the goal, + * false to use the progress only as a comparand. + * @param account_id - (optional) character ID to perform on. + * @return true on success, false on failure. + * @push 1 on success, 0 on failure. + */ +static BUILDIN(achievement_progress) +{ + struct map_session_data *sd = script->rid2sd(st); + int aid = script_getnum(st, 2); + int obj_idx = script_getnum(st, 3); + int progress = script_getnum(st, 4); + int incremental = script_getnum(st, 5); + int account_id = script_hasdata(st, 6) ? script_getnum(st, 6) : 0; + const struct achievement_data *ad = NULL; + + if ((ad = achievement->get(aid)) == NULL) { + ShowError("buildin_achievement_progress: Invalid achievement ID %d received.\n", aid); + script_pushint(st, 0); + return false; + } + + if (obj_idx <= 0 || obj_idx > VECTOR_LENGTH(ad->objective)) { + ShowError("buildin_achievement_progress: Invalid objective index %d received. (min: %d, max: %d)\n", obj_idx, 0, VECTOR_LENGTH(ad->objective)); + script_pushint(st, 0); + return false; + } + + obj_idx--; // convert to array index. + + if (progress <= 0 || progress > VECTOR_INDEX(ad->objective, obj_idx).goal) { + ShowError("buildin_achievement_progress: Progress exceeds goal limit for achievement id %d.\n", aid); + script_pushint(st, 0); + return false; + } + + if (incremental < 0 || incremental > 1) { + ShowError("buildin_achievement_progress: Argument 4 expects boolean (0/1). provided value: %d\n", incremental); + script_pushint(st, 0); + return false; + } + + if (script_hasdata(st, 6)) { + if (account_id <= 0) { + ShowError("buildin_achievement_progress: Invalid Account id %d provided.\n", account_id); + script_pushint(st, 0); + return false; + } else if ((sd = map->id2sd(account_id)) == NULL) { + ShowError("buildin_achievement_progress: Account with id %d was not found.\n", account_id); + script_pushint(st, 0); + return false; + } + } + + if (achievement->validate(sd, aid, obj_idx, progress, incremental ? true : false)) + script_pushint(st, progress); + else + script_pushint(st, 0); + + return true; +} + +/*========================================== * BattleGround System *------------------------------------------*/ static BUILDIN(waitingroom2bg) @@ -21293,12 +21469,12 @@ static BUILDIN(waitingroom2bg) for (i = 0; i < n && i < MAX_BG_MEMBERS; i++) { struct map_session_data *sd = cd->usersd[i]; if (sd != NULL && bg->team_join(bg_id, sd)) - mapreg->setreg(reference_uid(script->add_str("$@arenamembers"), i), sd->bl.id); + mapreg->setreg(reference_uid(script->add_variable("$@arenamembers"), i), sd->bl.id); else - mapreg->setreg(reference_uid(script->add_str("$@arenamembers"), i), 0); + mapreg->setreg(reference_uid(script->add_variable("$@arenamembers"), i), 0); } - mapreg->setreg(script->add_str("$@arenamembersnum"), i); + mapreg->setreg(script->add_variable("$@arenamembersnum"), i); script_pushint(st,bg_id); return true; } @@ -22360,18 +22536,52 @@ static BUILDIN(getcharip) return true; } +enum function_type { + FUNCTION_IS_NONE = 0, + FUNCTION_IS_COMMAND, + FUNCTION_IS_GLOBAL, + FUNCTION_IS_LOCAL, + FUNCTION_IS_LABEL, +}; + /** - * is_function(<function name>) -> 1 if function exists, 0 otherwise + * is_function(<function name>) **/ static BUILDIN(is_function) { - const char* str = script_getstr(st,2); + const char *str = script_getstr(st, 2); + enum function_type type = FUNCTION_IS_NONE; - if( strdb_exists(script->userfunc_db, str) ) - script_pushint(st,1); - else - script_pushint(st,0); + // TODO: add support for exported functions (#2142) + + if (strdb_exists(script->userfunc_db, str)) { + type = FUNCTION_IS_GLOBAL; + } else { + int n = script->search_str(str); + + if (n >= 0) { + switch (script->str_data[n].type) { + case C_FUNC: + type = FUNCTION_IS_COMMAND; + break; + case C_USERFUNC: + case C_USERFUNC_POS: + type = FUNCTION_IS_LOCAL; + break; + case C_POS: + type = FUNCTION_IS_LABEL; + break; + case C_NAME: + if (script->str_data[n].label >= 0) { + // WTF... ? + // for some reason local functions can have type C_NAME + type = FUNCTION_IS_LOCAL; + } + } + } + } + script_pushint(st, type); return true; } @@ -23463,7 +23673,7 @@ static BUILDIN(bg_join_team) *------------------------------------------*/ static BUILDIN(countbound) { - int i, type, j=0, k=0; + int type, j=0, k=0; struct map_session_data *sd = script->rid2sd(st); if (sd == NULL) @@ -23471,12 +23681,12 @@ static BUILDIN(countbound) type = script_hasdata(st,2)?script_getnum(st,2):0; - for(i=0;i<MAX_INVENTORY;i++) { + for (int i = 0; i < sd->status.inventorySize; i++) { if(sd->status.inventory[i].nameid > 0 && ( (!type && sd->status.inventory[i].bound > 0) || (type && sd->status.inventory[i].bound == type) )) { - pc->setreg(sd,reference_uid(script->add_str("@bound_items"), k),sd->status.inventory[i].nameid); + pc->setreg(sd,reference_uid(script->add_variable("@bound_items"), k),sd->status.inventory[i].nameid); k++; j += sd->status.inventory[i].amount; } @@ -23517,20 +23727,21 @@ static BUILDIN(checkbound) ShowError("script_checkbound: Not a valid bind type! Type=%d\n", bound_type); } - ARR_FIND( 0, MAX_INVENTORY, i, (sd->status.inventory[i].nameid == nameid && + ARR_FIND(0, sd->status.inventorySize, i, (sd->status.inventory[i].nameid == nameid && ( sd->status.inventory[i].refine == (script_hasdata(st,4)? script_getnum(st,4) : sd->status.inventory[i].refine) ) && ( sd->status.inventory[i].attribute == (script_hasdata(st,5)? script_getnum(st,5) : sd->status.inventory[i].attribute) ) && ( sd->status.inventory[i].card[0] == (script_hasdata(st,6)? script_getnum(st,6) : sd->status.inventory[i].card[0]) ) && ( sd->status.inventory[i].card[1] == (script_hasdata(st,7)? script_getnum(st,7) : sd->status.inventory[i].card[1]) ) && ( sd->status.inventory[i].card[2] == (script_hasdata(st,8)? script_getnum(st,8) : sd->status.inventory[i].card[2]) ) && ( sd->status.inventory[i].card[3] == (script_hasdata(st,9)? script_getnum(st,9) : sd->status.inventory[i].card[3]) ) && - ((sd->status.inventory[i].bound > 0 && !bound_type) || sd->status.inventory[i].bound == bound_type )) ); + ((sd->status.inventory[i].bound > 0 && !bound_type) || sd->status.inventory[i].bound == bound_type))); - if( i < MAX_INVENTORY ){ + if (i < sd->status.inventorySize) { script_pushint(st, sd->status.inventory[i].bound); return true; - } else + } else { script_pushint(st,0); + } return true; } @@ -23667,6 +23878,7 @@ static BUILDIN(sellitem) struct item_data *it; int i = 0, id = script_getnum(st,2); int value = 0; + int value2 = 0; int qty = 0; if( !(nd = map->id2nd(st->oid)) ) { @@ -23677,19 +23889,45 @@ static BUILDIN(sellitem) return false; } - value = script_hasdata(st,3) ? script_getnum(st, 3) : it->value_buy; - if( value == -1 ) - value = it->value_buy; - - if( !nd->u.scr.shop ) - npc->trader_update(nd->src_id?nd->src_id:nd->bl.id); - else {/* no need to run this if its empty */ - for( i = 0; i < nd->u.scr.shop->items; i++ ) { - if( nd->u.scr.shop->item[i].nameid == id ) - break; + if (!nd->u.scr.shop) { + npc->trader_update(nd->src_id ? nd->src_id : nd->bl.id); + if (nd->u.scr.shop->type == NST_BARTER) { + if (!script_hasdata(st, 5)) { + ShowError("buildin_sellitem: invalid number of parameters for barter-type shop!\n"); + return false; + } + value = script_getnum(st, 4); + value2 = script_getnum(st, 5); + } + } else {/* no need to run this if its empty */ + if (nd->u.scr.shop->type == NST_BARTER) { + if (!script_hasdata(st, 5)) { + ShowError("buildin_sellitem: invalid number of parameters for barter-type shop!\n"); + return false; + } + value = script_getnum(st, 4); + value2 = script_getnum(st, 5); + for (i = 0; i < nd->u.scr.shop->items; i++) { + const struct npc_item_list *const item = &nd->u.scr.shop->item[i]; + if (item->nameid == id && item->value == value && item->value2 == value2) { + break; + } + } + } else { + for (i = 0; i < nd->u.scr.shop->items; i++) { + if (nd->u.scr.shop->item[i].nameid == id) { + break; + } + } } } + if (nd->u.scr.shop->type != NST_BARTER) { + value = script_hasdata(st,3) ? script_getnum(st, 3) : it->value_buy; + if( value == -1 ) + value = it->value_buy; + } + if( nd->u.scr.shop->type == NST_MARKET ) { if( !script_hasdata(st,4) || ( qty = script_getnum(st, 4) ) <= 0 ) { ShowError("buildin_sellitem: invalid 'qty' for market-type shop!\n"); @@ -23702,19 +23940,29 @@ static BUILDIN(sellitem) it->name, id, value, (int)(value*0.75), it->value_sell, (int)(it->value_sell*1.24), nd->exname, nd->path); } - if( i != nd->u.scr.shop->items ) { + if (nd->u.scr.shop->type == NST_BARTER) { + qty = script_getnum(st, 3); + if (qty < -1 || value <= 0 || value2 <= 0) { + ShowError("buildin_sellitem: invalid parameters for barter-type shop!\n"); + return false; + } + } + + if (i != nd->u.scr.shop->items) { nd->u.scr.shop->item[i].value = value; nd->u.scr.shop->item[i].qty = qty; - if( nd->u.scr.shop->type == NST_MARKET ) /* has been manually updated, make it reflect on sql */ - npc->market_tosql(nd,i); + if (nd->u.scr.shop->type == NST_MARKET) /* has been manually updated, make it reflect on sql */ + npc->market_tosql(nd, i); + else if (nd->u.scr.shop->type == NST_BARTER) /* has been manually updated, make it reflect on sql */ + npc->barter_tosql(nd, i); } else { - for( i = 0; i < nd->u.scr.shop->items; i++ ) { - if( nd->u.scr.shop->item[i].nameid == 0 ) + for (i = 0; i < nd->u.scr.shop->items; i++) { + if (nd->u.scr.shop->item[i].nameid == 0) break; } - if( i == nd->u.scr.shop->items ) { - if( nd->u.scr.shop->items == USHRT_MAX ) { + if (i == nd->u.scr.shop->items) { + if (nd->u.scr.shop->items == USHRT_MAX) { ShowWarning("buildin_sellitem: Can't add %s (%s/%s), shop list is full!\n", it->name, nd->exname, nd->path); return false; } @@ -23724,6 +23972,7 @@ static BUILDIN(sellitem) nd->u.scr.shop->item[i].nameid = it->nameid; nd->u.scr.shop->item[i].value = value; + nd->u.scr.shop->item[i].value2 = value2; nd->u.scr.shop->item[i].qty = qty; } @@ -23740,35 +23989,55 @@ static BUILDIN(sellitem) static BUILDIN(stopselling) { struct npc_data *nd; - int i, id = script_getnum(st,2); + int i, id = script_getnum(st, 2); - if( !(nd = map->id2nd(st->oid)) || !nd->u.scr.shop ) { + if (!(nd = map->id2nd(st->oid)) || !nd->u.scr.shop) { ShowWarning("buildin_stopselling: trying to run without a proper NPC!\n"); return false; } - for( i = 0; i < nd->u.scr.shop->items; i++ ) { - if( nd->u.scr.shop->item[i].nameid == id ) - break; + if (nd->u.scr.shop->type == NST_BARTER) { + if (!script_hasdata(st, 4)) { + ShowError("buildin_stopselling: called with wrong number of arguments\n"); + return false; + } + const int id2 = script_getnum(st, 3); + const int amount2 = script_getnum(st, 4); + for (i = 0; i < nd->u.scr.shop->items; i++) { + const struct npc_item_list *const item = &nd->u.scr.shop->item[i]; + if (item->nameid == id && item->value == id2 && item->value2 == amount2) { + break; + } + } + } else { + for (i = 0; i < nd->u.scr.shop->items; i++) { + if (nd->u.scr.shop->item[i].nameid == id) { + break; + } + } } - if( i != nd->u.scr.shop->items ) { + if (i != nd->u.scr.shop->items) { int cursor; - if( nd->u.scr.shop->type == NST_MARKET ) - npc->market_delfromsql(nd,i); + if (nd->u.scr.shop->type == NST_MARKET) + npc->market_delfromsql(nd, i); + if (nd->u.scr.shop->type == NST_BARTER) + npc->barter_delfromsql(nd, i); nd->u.scr.shop->item[i].nameid = 0; nd->u.scr.shop->item[i].value = 0; + nd->u.scr.shop->item[i].value2 = 0; nd->u.scr.shop->item[i].qty = 0; - for( i = 0, cursor = 0; i < nd->u.scr.shop->items; i++ ) { - if( nd->u.scr.shop->item[i].nameid == 0 ) + for (i = 0, cursor = 0; i < nd->u.scr.shop->items; i++) { + if (nd->u.scr.shop->item[i].nameid == 0) continue; - if( cursor != i ) { + if (cursor != i) { nd->u.scr.shop->item[cursor].nameid = nd->u.scr.shop->item[i].nameid; nd->u.scr.shop->item[cursor].value = nd->u.scr.shop->item[i].value; + nd->u.scr.shop->item[cursor].value2 = nd->u.scr.shop->item[i].value2; nd->u.scr.shop->item[cursor].qty = nd->u.scr.shop->item[i].qty; } @@ -23834,7 +24103,8 @@ static BUILDIN(tradertype) nd->u.scr.shop->item[i].value = 0; nd->u.scr.shop->item[i].qty = 0; } - npc->market_delfromsql(nd,USHRT_MAX); + npc->market_delfromsql(nd, INT_MAX); + npc->barter_delfromsql(nd, INT_MAX); } #if PACKETVER < 20131223 @@ -23843,6 +24113,12 @@ static BUILDIN(tradertype) script->reportsrc(st); } #endif +#if PACKETVER_ZERO_NUM < 20181226 + if (type == NST_BARTER) { + ShowWarning("buildin_tradertype: NST_BARTER is only available with PACKETVER_ZERO_NUM 20181226 or newer!\n"); + script->reportsrc(st); + } +#endif if( nd->u.scr.shop ) nd->u.scr.shop->type = type; @@ -23886,8 +24162,8 @@ static BUILDIN(shopcount) } else if ( !nd->u.scr.shop || !nd->u.scr.shop->items ) { ShowWarning("buildin_shopcount(%d): trying to use without any items!\n",id); return false; - } else if ( nd->u.scr.shop->type != NST_MARKET ) { - ShowWarning("buildin_shopcount(%d): trying to use on a non-NST_MARKET shop!\n",id); + } else if (nd->u.scr.shop->type != NST_MARKET && nd->u.scr.shop->type != NST_BARTER) { + ShowWarning("buildin_shopcount(%d): trying to use on a non-NST_MARKET and non-NST_BARTER shop!\n",id); return false; } @@ -24544,6 +24820,174 @@ static BUILDIN(openstylist) return true; } +static BUILDIN(msgtable) +{ + struct map_session_data *sd = script_rid2sd(st); + if (sd == NULL) + return false; + + const enum clif_messages msgId = script_getnum(st, 2); + if (script_hasdata(st, 3)) { + clif->msgtable_color(sd, msgId, script_getnum(st, 3)); + } else { + clif->msgtable(sd, msgId); + } + + return true; +} + +static BUILDIN(msgtable2) +{ + struct map_session_data *sd = script_rid2sd(st); + if (sd == NULL) + return false; + + const enum clif_messages msgId = script_getnum(st, 2); + if (script_isstringtype(st, 3)) { + const char *value = script_getstr(st, 3); + if (script_hasdata(st, 4)) { + clif->msgtable_str_color(sd, msgId, value, script_getnum(st, 4)); + } else { + clif->msgtable_str(sd, msgId, value); + } + } else { + const int value = script_getnum(st, 3); + clif->msgtable_num(sd, msgId, value); + } + + return true; +} + +// show/hide camera info +static BUILDIN(camerainfo) +{ + struct map_session_data *sd = script_rid2sd(st); + if (sd == NULL) + return false; + + clif->camera_showWindow(sd); + return true; +} + +// allow change some camera parameters +static BUILDIN(changecamera) +{ + struct map_session_data *sd = script_rid2sd(st); + if (sd == NULL) + return false; + + enum send_target target = SELF; + if (script_hasdata(st, 5)) { + target = script_getnum(st, 5); + } + clif->camera_change(sd, (float)script_getnum(st, 2), (float)script_getnum(st, 3), (float)script_getnum(st, 4), target); + return true; +} + +// update preview window to given item +static BUILDIN(itempreview) +{ + struct map_session_data *sd = script_rid2sd(st); + if (sd == NULL) + return false; + clif->item_preview(sd, script_getnum(st, 2)); + return true; +} + +// insert or remove card into equipped item +static BUILDIN(enchantitem) +{ + struct map_session_data *sd = script_rid2sd(st); + if (sd == NULL) + return false; + const int pos = script_getnum(st, 2); + if ((pos < EQI_ACC_L || pos > EQI_HAND_R) && pos != EQI_AMMO) { + ShowError("Wrong equip position: %d\n", pos); + script->reportfunc(st); + script->reportsrc(st); + script_pushint(st, false); + return true; + } + const int cardId = script_getnum(st, 4); + struct item_data *it = itemdb->exists(cardId); + if (it == NULL || it->type != IT_CARD) { + ShowError("Item id is not card or not exists: %d\n", cardId); + script->reportfunc(st); + script->reportsrc(st); + script_pushint(st, false); + return true; + } + const int n = sd->equip_index[pos]; + if (n < 0) { + ShowError("Item in equipment slot %d is not equipped\n", pos); + script->reportfunc(st); + script->reportsrc(st); + script_pushint(st, false); + return true; + } + const int cardSlot = script_getnum(st, 3); + if (cardSlot < 0 || cardSlot >= MAX_SLOTS) { + ShowError("Wrong card slot %d. Must be in range 0-3.\n", cardSlot); + script->reportfunc(st); + script->reportsrc(st); + script_pushint(st, false); + return true; + } + const bool res = clif->enchant_equipment(sd, pc->equip_pos[pos], cardSlot, cardId); + if (res) { + logs->pick_pc(sd, LOG_TYPE_CARD, -1, &sd->status.inventory[n],sd->inventory_data[n]); + sd->status.inventory[n].card[cardSlot] = cardId; + logs->pick_pc(sd, LOG_TYPE_CARD, 1, &sd->status.inventory[n],sd->inventory_data[n]); + status_calc_pc(sd, SCO_NONE); + } + script_pushint(st, res); + return true; +} + +// send ack to inventory expand request +static BUILDIN(expandInventoryAck) +{ + struct map_session_data *sd = script_rid2sd(st); + if (sd == NULL) + return false; + int itemId = 0; + if (script_hasdata(st, 3)) { + itemId = script_getnum(st, 3); + } + clif->inventoryExpandAck(sd, script_getnum(st, 2), itemId); + return true; +} + +// send final ack to inventory expand request +static BUILDIN(expandInventoryResult) +{ + struct map_session_data *sd = script_rid2sd(st); + if (sd == NULL) + return false; + clif->inventoryExpandResult(sd, script_getnum(st, 2)); + return true; +} + +// adjust player inventory size to given value positive or negative +static BUILDIN(expandInventory) +{ + struct map_session_data *sd = script_rid2sd(st); + if (sd == NULL) + return false; + script_pushint(st, pc->expandInventory(sd, script_getnum(st, 2))); + return true; +} + +// return current player inventory size +static BUILDIN(getInventorySize) +{ + struct map_session_data *sd = script_rid2sd(st); + if (sd == NULL) + return false; + script_pushint(st, sd->status.inventorySize); + return true; +} + /** * Adds a built-in script function. * @@ -24705,14 +25149,14 @@ static void script_parse_builtin(void) BUILDIN_DEF(__setr,"rv?"), // NPC interaction - BUILDIN_DEF(mes,"s"), - BUILDIN_DEF(mesf,"s*"), + BUILDIN_DEF(mes, "?"), + BUILDIN_DEF(mesf, "s*"), BUILDIN_DEF(next,""), BUILDIN_DEF(close,""), BUILDIN_DEF(close2,""), BUILDIN_DEF(menu,"sl*"), BUILDIN_DEF(select,"s*"), //for future jA script compatibility - BUILDIN_DEF(prompt,"s*"), + BUILDIN_DEF2(select, "prompt", "s*"), // BUILDIN_DEF(goto,"l"), BUILDIN_DEF(callsub,"l*"), @@ -24761,7 +25205,7 @@ static void script_parse_builtin(void) BUILDIN_DEF(readparam,"i?"), BUILDIN_DEF(setparam,"ii?"), BUILDIN_DEF(getcharid,"i?"), - BUILDIN_DEF(getnpcid,"i?"), + BUILDIN_DEF(getnpcid, "?"), BUILDIN_DEF(getpartyname,"i"), BUILDIN_DEF(getpartymember,"i?"), BUILDIN_DEF(getpartyleader,"i?"), @@ -24789,6 +25233,7 @@ static void script_parse_builtin(void) BUILDIN_DEF(downrefitem,"i?"), BUILDIN_DEF(statusup,"i"), BUILDIN_DEF(statusup2,"ii"), + BUILDIN_DEF(needed_status_point,"ii?"), BUILDIN_DEF(bonus,"iv"), BUILDIN_DEF2(bonus,"bonus2","ivi"), BUILDIN_DEF2(bonus,"bonus3","ivii"), @@ -24864,7 +25309,7 @@ static void script_parse_builtin(void) BUILDIN_DEF(sc_end,"i?"), BUILDIN_DEF(getstatus, "i?"), BUILDIN_DEF(getscrate,"ii?"), - BUILDIN_DEF(debugmes,"v"), + BUILDIN_DEF(debugmes,"v*"), BUILDIN_DEF2(catchpet,"pet","i"), BUILDIN_DEF2(birthpet,"bpet",""), BUILDIN_DEF(resetlvl,"i"), @@ -24940,6 +25385,7 @@ static void script_parse_builtin(void) BUILDIN_DEF(skilleffect,"vi"), // skill effect [Celest] BUILDIN_DEF(npcskilleffect,"viii"), // npc skill effect [Valaris] BUILDIN_DEF(specialeffect,"i???"), // npc skill effect [Valaris] + BUILDIN_DEF(removespecialeffect,"i???"), BUILDIN_DEF_DEPRECATED(specialeffect2,"i??"), // skill effect on players[Valaris] BUILDIN_DEF(nude,""), // nude command [Valaris] BUILDIN_DEF(mapwarp,"ssii??"), // Added by RoVeRT @@ -24947,6 +25393,7 @@ static void script_parse_builtin(void) BUILDIN_DEF2(atcommand,"charcommand","s"), // [MouseJstr] BUILDIN_DEF(movenpc,"sii?"), // [MouseJstr] BUILDIN_DEF(message,"vs"), // [MouseJstr] + BUILDIN_DEF(servicemessage, "si?"), BUILDIN_DEF(npctalk,"s??"), // [Valaris][Murilo BiO] BUILDIN_DEF(mobcount,"ss"), BUILDIN_DEF(getlook,"i"), @@ -24992,6 +25439,8 @@ static void script_parse_builtin(void) BUILDIN_DEF(charat,"si"), BUILDIN_DEF(isstr,"v"), BUILDIN_DEF(getdatatype, "?"), + BUILDIN_DEF(data_to_string, "?"), + BUILDIN_DEF2(getd, "string_to_data", "?"), BUILDIN_DEF(chr,"i"), BUILDIN_DEF(ord,"s"), BUILDIN_DEF(setchar,"ssi"), @@ -25120,10 +25569,14 @@ static void script_parse_builtin(void) BUILDIN_DEF(buyingstore,"i"), BUILDIN_DEF(searchstores,"ii"), BUILDIN_DEF(showdigit,"i?"), + BUILDIN_DEF(msgtable, "i?"), + BUILDIN_DEF(msgtable2, "iv?"), // WoE SE BUILDIN_DEF(agitstart2,""), BUILDIN_DEF(agitend2,""), BUILDIN_DEF(agitcheck2,""), + // Achievements [Smokexyz/Hercules] + BUILDIN_DEF(achievement_progress, "iiii?"), // BattleGround BUILDIN_DEF(waitingroom2bg,"siiss?"), BUILDIN_DEF(waitingroom2bg_single,"isiis"), @@ -25197,7 +25650,7 @@ static void script_parse_builtin(void) //Quest Log System [Inkfish] BUILDIN_DEF(questinfo, "i?"), - BUILDIN_DEF(setquestinfo, "i??"), + BUILDIN_DEF(setquestinfo, "i???"), BUILDIN_DEF(setquest, "i?"), BUILDIN_DEF(erasequest, "i?"), BUILDIN_DEF(completequest, "i?"), @@ -25235,8 +25688,8 @@ static void script_parse_builtin(void) /* New Shop Support */ BUILDIN_DEF(openshop,"?"), - BUILDIN_DEF(sellitem,"i??"), - BUILDIN_DEF(stopselling,"i"), + BUILDIN_DEF(sellitem,"i???"), + BUILDIN_DEF(stopselling,"i??"), BUILDIN_DEF(setcurrency,"i?"), BUILDIN_DEF(tradertype,"i"), BUILDIN_DEF(purchaseok,""), @@ -25269,6 +25722,17 @@ static void script_parse_builtin(void) // -- HatEffect BUILDIN_DEF(hateffect, "ii"), + + // camera + BUILDIN_DEF(camerainfo, ""), + BUILDIN_DEF(changecamera, "iii?"), + + BUILDIN_DEF(itempreview, "i"), + BUILDIN_DEF(enchantitem, "iii"), + BUILDIN_DEF(expandInventoryAck, "i?"), + BUILDIN_DEF(expandInventoryResult, "i"), + BUILDIN_DEF(expandInventory, "i"), + BUILDIN_DEF(getInventorySize, ""), }; int i, len = ARRAYLENGTH(BUILDIN); RECREATE(script->buildin, char *, script->buildin_count + len); // Pre-alloc to speed up @@ -25308,13 +25772,16 @@ static void script_hardcoded_constants(void) script->set_constant("MAX_LEVEL",MAX_LEVEL,false, false); script->set_constant("MAX_STORAGE",MAX_STORAGE,false, false); script->set_constant("MAX_GUILD_STORAGE",MAX_GUILD_STORAGE,false, false); - script->set_constant("MAX_CART",MAX_INVENTORY,false, false); + script->set_constant("MAX_CART", MAX_CART, false, false); script->set_constant("MAX_INVENTORY",MAX_INVENTORY,false, false); + script->set_constant("FIXED_INVENTORY_SIZE", FIXED_INVENTORY_SIZE, false, false); script->set_constant("MAX_ZENY",MAX_ZENY,false, false); script->set_constant("MAX_BANK_ZENY", MAX_BANK_ZENY, false, false); script->set_constant("MAX_BG_MEMBERS",MAX_BG_MEMBERS,false, false); script->set_constant("MAX_CHAT_USERS",MAX_CHAT_USERS,false, false); script->set_constant("MAX_REFINE",MAX_REFINE,false, false); + script->set_constant("MAX_MENU_OPTIONS", MAX_MENU_OPTIONS, false, false); + script->set_constant("MAX_MENU_LENGTH", MAX_MENU_LENGTH, false, false); script->constdb_comment("status options"); script->set_constant("Option_Nothing",OPTION_NOTHING,false, false); @@ -25584,6 +26051,7 @@ static void script_hardcoded_constants(void) script->set_constant("ITEMINFO_VIEWID", ITEMINFO_VIEWID, false, false); script->set_constant("ITEMINFO_MATK", ITEMINFO_MATK, false, false); script->set_constant("ITEMINFO_VIEWSPRITE", ITEMINFO_VIEWSPRITE, false, false); + script->set_constant("ITEMINFO_TRADE", ITEMINFO_TRADE, false, false); script->constdb_comment("monster skill states"); script->set_constant("MSS_ANY", MSS_ANY, false, false); @@ -25667,6 +26135,47 @@ static void script_hardcoded_constants(void) script->set_constant("QINFO_HOMUN_LEVEL", QINFO_HOMUN_LEVEL, false, false); script->set_constant("QINFO_HOMUN_TYPE", QINFO_HOMUN_TYPE, false, false); script->set_constant("QINFO_QUEST", QINFO_QUEST, false, false); + script->set_constant("QINFO_MERCENARY_CLASS", QINFO_MERCENARY_CLASS, false, false); + + script->constdb_comment("function types"); + script->set_constant("FUNCTION_IS_COMMAND", FUNCTION_IS_COMMAND, false, false); + 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->constdb_comment("item trade restrictions"); + script->set_constant("ITR_NONE", ITR_NONE, false, false); + script->set_constant("ITR_NODROP", ITR_NODROP, false, false); + script->set_constant("ITR_NOTRADE", ITR_NOTRADE, false, false); + script->set_constant("ITR_PARTNEROVERRIDE", ITR_PARTNEROVERRIDE, false, false); + script->set_constant("ITR_NOSELLTONPC", ITR_NOSELLTONPC, false, false); + script->set_constant("ITR_NOCART", ITR_NOCART, false, false); + script->set_constant("ITR_NOSTORAGE", ITR_NOSTORAGE, false, false); + script->set_constant("ITR_NOGSTORAGE", ITR_NOGSTORAGE, false, false); + script->set_constant("ITR_NOMAIL", ITR_NOMAIL, false, false); + script->set_constant("ITR_NOAUCTION", ITR_NOAUCTION, false, false); + script->set_constant("ITR_ALL", ITR_ALL, false, false); + + script->constdb_comment("inventory expand ack responds"); + script->set_constant("EXPAND_INV_ASK_CONFIRMATION", EXPAND_INVENTORY_ASK_CONFIRMATION, false, false); + script->set_constant("EXPAND_INV_FAILED", EXPAND_INVENTORY_FAILED, false, false); + script->set_constant("EXPAND_INV_OTHER_WORK", EXPAND_INVENTORY_OTHER_WORK, false, false); + script->set_constant("EXPAND_INV_MISSING_ITEM", EXPAND_INVENTORY_MISSING_ITEM, false, false); + script->set_constant("EXPAND_INV_MAX_SIZE", EXPAND_INVENTORY_MAX_SIZE, false, false); + + script->constdb_comment("inventory expand final responds"); + script->set_constant("EXPAND_INV_RESULT_SUCCESS", EXPAND_INVENTORY_RESULT_SUCCESS, false, false); + script->set_constant("EXPAND_INV_RESULT_FAILED", EXPAND_INVENTORY_RESULT_FAILED, false, false); + script->set_constant("EXPAND_INV_RESULT_OTHER_WORK", EXPAND_INVENTORY_RESULT_OTHER_WORK, false, false); + script->set_constant("EXPAND_INV_RESULT_MISSING_ITEM", EXPAND_INVENTORY_RESULT_MISSING_ITEM, false, false); + script->set_constant("EXPAND_INV_RESULT_MAX_SIZE", EXPAND_INVENTORY_RESULT_MAX_SIZE, false, false); + + script->constdb_comment("trader type"); + script->set_constant("NST_ZENY", NST_ZENY, false, false); + script->set_constant("NST_CASH", NST_CASH, false, false); + script->set_constant("NST_MARKET", NST_MARKET, false, false); + script->set_constant("NST_CUSTOM", NST_CUSTOM, false, false); + script->set_constant("NST_BARTER", NST_BARTER, false, false); script->constdb_comment("Renewal"); #ifdef RENEWAL @@ -25849,6 +26358,7 @@ void script_defaults(void) script->setarray_pc = script_setarray_pc; script->config_read = script_config_read; script->add_str = script_add_str; + script->add_variable = script_add_variable; script->get_str = script_get_str; script->search_str = script_search_str; script->setd_sub = setd_sub; |