summaryrefslogtreecommitdiff
path: root/src/map/script.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/map/script.c')
-rw-r--r--src/map/script.c1286
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;