diff options
Diffstat (limited to 'src/map/script.c')
-rw-r--r-- | src/map/script.c | 12419 |
1 files changed, 9720 insertions, 2699 deletions
diff --git a/src/map/script.c b/src/map/script.c index 007c6e0e1..45c954dc0 100644 --- a/src/map/script.c +++ b/src/map/script.c @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2015 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -29,7 +29,9 @@ #include "map/channel.h" #include "map/chat.h" #include "map/chrif.h" +#include "map/clan.h" #include "map/clif.h" +#include "map/date.h" #include "map/elemental.h" #include "map/guild.h" #include "map/homunculus.h" @@ -41,6 +43,7 @@ #include "map/map.h" #include "map/mapreg.h" #include "map/mercenary.h" +#include "map/messages.h" #include "map/mob.h" #include "map/npc.h" #include "map/party.h" @@ -49,11 +52,13 @@ #include "map/pet.h" #include "map/pet.h" #include "map/quest.h" +#include "map/refine.h" #include "map/skill.h" #include "map/status.h" #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" @@ -80,42 +85,28 @@ #include <sys/time.h> #endif -struct script_interface script_s; +static struct script_interface script_s; struct script_interface *script; -static inline int GETVALUE(const unsigned char* buf, int i) { - return (int)MakeDWord(MakeWord(buf[i], buf[i+1]), MakeWord(buf[i+2], 0)); -} -static inline void SETVALUE(unsigned char* buf, int i, int n) { - buf[i] = GetByte(n, 0); - buf[i+1] = GetByte(n, 1); - buf[i+2] = GetByte(n, 2); -} - -static inline void script_string_buf_ensure(struct script_string_buf *buf, size_t ensure) { - if( buf->pos+ensure >= buf->size ) { - do { - buf->size += 512; - } while ( buf->pos+ensure >= buf->size ); - RECREATE(buf->ptr, char, buf->size); - } -} - -static inline void script_string_buf_addb(struct script_string_buf *buf,uint8 b) { - if( buf->pos+1 >= buf->size ) { - buf->size += 512; - RECREATE(buf->ptr, char, buf->size); - } - buf->ptr[buf->pos++] = b; +static inline int GETVALUE(const struct script_buf *buf, int i) __attribute__((nonnull (1))); +static inline int GETVALUE(const struct script_buf *buf, int i) +{ + Assert_ret(VECTOR_LENGTH(*buf) > i + 2); + return (int)MakeDWord(MakeWord(VECTOR_INDEX(*buf, i), VECTOR_INDEX(*buf, i+1)), + MakeWord(VECTOR_INDEX(*buf, i+2), 0)); } -static inline void script_string_buf_destroy(struct script_string_buf *buf) { - if( buf->ptr ) - aFree(buf->ptr); - memset(buf,0,sizeof(struct script_string_buf)); +static inline void SETVALUE(struct script_buf *buf, int i, int n) __attribute__((nonnull (1))); +static inline void SETVALUE(struct script_buf *buf, int i, int n) +{ + Assert_retv(VECTOR_LENGTH(*buf) > i + 2); + VECTOR_INDEX(*buf, i) = GetByte(n, 0); + VECTOR_INDEX(*buf, i+1) = GetByte(n, 1); + VECTOR_INDEX(*buf, i+2) = GetByte(n, 2); } -const char* script_op2name(int op) { +static const char *script_op2name(int op) +{ #define RETURN_OP_NAME(type) case type: return #type switch( op ) { RETURN_OP_NAME(C_NOP); @@ -151,6 +142,7 @@ const char* script_op2name(int op) { RETURN_OP_NAME(C_ADD); RETURN_OP_NAME(C_SUB); RETURN_OP_NAME(C_MUL); + RETURN_OP_NAME(C_POW); RETURN_OP_NAME(C_DIV); RETURN_OP_NAME(C_MOD); RETURN_OP_NAME(C_NEG); @@ -173,9 +165,10 @@ const char* script_op2name(int op) { } #ifdef SCRIPT_DEBUG_DUMP_STACK -static void script_dump_stack(struct script_state* st) +static void script_dump_stack(struct script_state *st) { int i; + nullpo_retv(st); ShowMessage("\tstart = %d\n", st->start); ShowMessage("\tend = %d\n", st->end); ShowMessage("\tdefsp = %d\n", st->stack->defsp); @@ -215,9 +208,11 @@ static void script_dump_stack(struct script_state* st) #endif /// Reports on the console the src of a script error. -void script_reportsrc(struct script_state *st) { +static void script_reportsrc(struct script_state *st) +{ struct block_list* bl; + nullpo_retv(st); if( st->oid == 0 ) return; //Can't report source. @@ -245,7 +240,7 @@ void script_reportsrc(struct script_state *st) { } /// Reports on the console information about the script data. -void script_reportdata(struct script_data* data) +static void script_reportdata(struct script_data *data) { if( data == NULL ) return; @@ -287,7 +282,7 @@ void script_reportdata(struct script_data* data) } /// Reports on the console information about the current built-in function. -void script_reportfunc(struct script_state* st) +static void script_reportfunc(struct script_state *st) { int params, id; struct script_data* data; @@ -322,8 +317,9 @@ void script_reportfunc(struct script_state* st) /*========================================== * Output error message *------------------------------------------*/ -static void disp_error_message2(const char *mes,const char *pos,int report) analyzer_noreturn; -static void disp_error_message2(const char *mes,const char *pos,int report) { +static void disp_error_message2(const char *mes, const char *pos, int report) __attribute__((nonnull (1))) analyzer_noreturn; +static void disp_error_message2(const char *mes, const char *pos, int report) +{ script->error_msg = aStrdup(mes); script->error_pos = pos; script->error_report = report; @@ -331,12 +327,13 @@ static void disp_error_message2(const char *mes,const char *pos,int report) { } #define disp_error_message(mes,pos) (disp_error_message2((mes),(pos),1)) -void disp_warning_message(const char *mes, const char *pos) { +static void disp_warning_message(const char *mes, const char *pos) +{ script->warning(script->parser_current_src,script->parser_current_file,script->parser_current_line,mes,pos); } /// Checks event parameter validity -void check_event(struct script_state *st, const char *evt) +static void check_event(struct script_state *st, const char *evt) { if( evt && evt[0] && !stristr(evt, "::On") ) { @@ -348,9 +345,11 @@ void check_event(struct script_state *st, const char *evt) /*========================================== * Hashes the input string *------------------------------------------*/ -unsigned int calc_hash(const char* p) { +static unsigned int calc_hash(const char *p) +{ unsigned int h; + nullpo_ret(p); #if defined(SCRIPT_HASH_DJB2) h = 5381; while( *p ) // hash*33 + c @@ -382,10 +381,12 @@ unsigned int calc_hash(const char* p) { /*========================================== * Hashes the input string in a case insensitive way *------------------------------------------*/ -unsigned int calc_hash_ci(const char* p) { +static unsigned int calc_hash_ci(const char *p) +{ unsigned int h = 0; #ifdef ENABLE_CASE_CHECK + nullpo_ret(p); #if defined(SCRIPT_HASH_DJB2) h = 5381; while( *p ) // hash*33 + c @@ -420,14 +421,14 @@ unsigned int calc_hash_ci(const char* p) { *------------------------------------------*/ /// Looks up string using the provided id. -const char* script_get_str(int id) +static const char *script_get_str(int id) { Assert_retr(NULL, id >= LABEL_START && id < script->str_size); return script->str_buf+script->str_data[id].str; } /// Returns the uid of the string, or -1. -int script_search_str(const char* p) +static int script_search_str(const char *p) { int i; @@ -440,8 +441,10 @@ int script_search_str(const char* p) return -1; } -void script_casecheck_clear_sub(struct casecheck_data *ccd) { +static void script_casecheck_clear_sub(struct casecheck_data *ccd) +{ #ifdef ENABLE_CASE_CHECK + nullpo_retv(ccd); if (ccd->str_data) { aFree(ccd->str_data); ccd->str_data = NULL; @@ -458,19 +461,22 @@ void script_casecheck_clear_sub(struct casecheck_data *ccd) { #endif // ENABLE_CASE_CHECK } -void script_global_casecheck_clear(void) { +static void script_global_casecheck_clear(void) +{ script_casecheck_clear_sub(&script->global_casecheck); } -void script_local_casecheck_clear(void) { +static void script_local_casecheck_clear(void) +{ script_casecheck_clear_sub(&script->local_casecheck); } -const char *script_casecheck_add_str_sub(struct casecheck_data *ccd, const char *p) +static const char *script_casecheck_add_str_sub(struct casecheck_data *ccd, const char *p) { #ifdef ENABLE_CASE_CHECK int len; int h = script->calc_hash_ci(p); + nullpo_retr(NULL, ccd); if (ccd->str_hash[h] == 0) { //empty bucket, add new node here ccd->str_hash[h] = ccd->str_num; @@ -522,17 +528,19 @@ const char *script_casecheck_add_str_sub(struct casecheck_data *ccd, const char return NULL; } -const char *script_global_casecheck_add_str(const char *p) { +static const char *script_global_casecheck_add_str(const char *p) +{ return script_casecheck_add_str_sub(&script->global_casecheck, p); } -const char *script_local_casecheck_add_str(const char *p) { +static const char *script_local_casecheck_add_str(const char *p) +{ return script_casecheck_add_str_sub(&script->local_casecheck, p); } /// Stores a copy of the string and returns its id. /// If an identical string is already present, returns its id instead. -int script_add_str(const char* p) +static int script_add_str(const char *p) { int len, h = script->calc_hash(p); #ifdef ENABLE_CASE_CHECK @@ -606,22 +614,39 @@ int script_add_str(const char* p) return script->str_num++; } -/// Appends 1 byte to the script buffer. -void add_scriptb(int a) +static int script_add_variable(const char *varname) { - if( script->pos+1 >= script->size ) - { - script->size += SCRIPT_BLOCK_SIZE; - RECREATE(script->buf,unsigned char,script->size); + int key = script->search_str(varname); + + if (key < 0) { + key = script->add_str(varname); + script->str_data[key].type = C_NAME; } - script->buf[script->pos++] = (uint8)(a); + + return key; } -/// Appends a c_op value to the script buffer. -/// The value is variable-length encoded into 8-bit blocks. -/// The encoding scheme is ( 01?????? )* 00??????, LSB first. -/// All blocks but the last hold 7 bits of data, topmost bit is always 1 (carries). -void add_scriptc(int a) +/** + * Appends 1 byte to the script buffer. + * + * @param a The byte to append. + */ +static void add_scriptb(int a) +{ + VECTOR_ENSURE(script->buf, 1, SCRIPT_BLOCK_SIZE); + VECTOR_PUSH(script->buf, (uint8)a); +} + +/** + * Appends a c_op value to the script buffer. + * + * The value is variable-length encoded into 8-bit blocks. + * The encoding scheme is ( 01?????? )* 00??????, LSB first. + * All blocks but the last hold 7 bits of data, topmost bit is always 1 (carries). + * + * @param a The value to append. + */ +static void add_scriptc(int a) { while( a >= 0x40 ) { @@ -632,11 +657,16 @@ void add_scriptc(int a) script->addb(a); } -/// Appends an integer value to the script buffer. -/// The value is variable-length encoded into 8-bit blocks. -/// The encoding scheme is ( 11?????? )* 10??????, LSB first. -/// All blocks but the last hold 7 bits of data, topmost bit is always 1 (carries). -void add_scripti(int a) +/** + * Appends an integer value to the script buffer. + * + * The value is variable-length encoded into 8-bit blocks. + * The encoding scheme is ( 11?????? )* 10??????, LSB first. + * All blocks but the last hold 7 bits of data, topmost bit is always 1 (carries). + * + * @param a The value to append. + */ +static void add_scripti(int a) { while( a >= 0x40 ) { @@ -646,12 +676,12 @@ void add_scripti(int a) script->addb(a|0x80); } -/// Appends a script->str_data object (label/function/variable/integer) to the script buffer. - -/// -/// @param l The id of the script->str_data entry -// Maximum up to 16M -void add_scriptl(int l) +/** + * Appends a script->str_data object (label/function/variable/integer) to the script buffer. + * + * @param l The id of the script->str_data entry (Maximum up to 16M) + */ +static void add_scriptl(int l) { int backpatch = script->str_data[l].backpatch; @@ -667,7 +697,7 @@ void add_scriptl(int l) case C_USERFUNC: // Embedded data backpatch there is a possibility of label script->addc(C_NAME); - script->str_data[l].backpatch = script->pos; + script->str_data[l].backpatch = VECTOR_LENGTH(script->buf); script->addb(backpatch); script->addb(backpatch>>8); script->addb(backpatch>>16); @@ -689,7 +719,7 @@ void add_scriptl(int l) /*========================================== * Resolve the label *------------------------------------------*/ -void set_label(int l,int pos, const char* script_pos) +static void set_label(int l, int pos, const char *script_pos) { int i; @@ -705,15 +735,15 @@ void set_label(int l,int pos, const char* script_pos) script->str_data[l].type=(script->str_data[l].type == C_USERFUNC ? C_USERFUNC_POS : C_POS); script->str_data[l].label=pos; for (i = script->str_data[l].backpatch; i >= 0 && i != 0x00ffffff; ) { - int next = GETVALUE(script->buf,i); - script->buf[i-1]=(script->str_data[l].type == C_USERFUNC ? C_USERFUNC_POS : C_POS); - SETVALUE(script->buf,i,pos); + int next = GETVALUE(&script->buf, i); + VECTOR_INDEX(script->buf, i-1) = (script->str_data[l].type == C_USERFUNC ? C_USERFUNC_POS : C_POS); + SETVALUE(&script->buf, i, pos); i = next; } } /// Skips spaces and/or comments. -const char* script_skip_space(const char* p) +static const char *script_skip_space(const char *p) { if( p == NULL ) return NULL; @@ -752,7 +782,9 @@ const char* script_skip_space(const char* p) /// Skips a word. /// A word consists of undercores and/or alphanumeric characters, /// and valid variable prefixes/postfixes. -const char* skip_word(const char* p) { +static const char *skip_word(const char *p) +{ + nullpo_retr(NULL, p); // prefix switch( *p ) { case '@':// temporary char variable @@ -767,7 +799,7 @@ const char* skip_word(const char* p) { p += ( p[1] == '@' ? 2 : 1 ); break; } - while( ISALNUM(*p) || *p == '_' || *p == '\'' ) + while (ISALNUM(*p) || *p == '_') ++p; // postfix @@ -779,10 +811,12 @@ const char* skip_word(const char* p) { /// Adds a word to script->str_data. /// @see skip_word /// @see script->add_str -int add_word(const char* p) { +static int add_word(const char *p) +{ size_t len; int i; + nullpo_retr(0, p); // Check for a word len = script->skip_word(p) - p; if( len == 0 ) @@ -804,33 +838,32 @@ int add_word(const char* p) { /// Parses a function call. /// The argument list can have parenthesis or not. /// The number of arguments is checked. -static -const char* parse_callfunc(const char* p, int require_paren, int is_custom) +static const char *parse_callfunc(const char *p, int require_paren, int is_custom) { const char *p2; char *arg = NULL; char null_arg = '\0'; int func; - bool nested_call = false, macro = false; + bool macro = false; + nullpo_retr(NULL, p); // is need add check for arg null pointer below? func = script->add_word(p); - if( script->str_data[func].type == C_FUNC ) { - /** only when unset (-1), valid values are >= 0 **/ - if( script->syntax.last_func == -1 ) - script->syntax.last_func = script->str_data[func].val; - else { //Nested function call - script->syntax.nested_call++; - nested_call = true; - - if( script->str_data[func].val == script->buildin_lang_macro_offset ) { + if (script->str_data[func].type == C_FUNC) { + script->syntax.nested_call++; + if (script->syntax.last_func != -1) { + if (script->str_data[func].val == script->buildin_lang_macro_offset) { script->syntax.lang_macro_active = true; macro = true; + } else if (script->str_data[func].val == script->buildin_lang_macro_fmtstring_offset) { + script->syntax.lang_macro_fmtstring_active = true; + macro = true; } } if( !macro ) { // buildin function + script->syntax.last_func = script->str_data[func].val; script->addl(func); script->addc(C_ARG); } @@ -919,30 +952,29 @@ const char* parse_callfunc(const char* p, int require_paren, int is_custom) disp_error_message("parse_callfunc: expected ')' to close argument list",p); ++p; - if( script->str_data[func].val == script->buildin_lang_macro_offset ) + if (script->str_data[func].val == script->buildin_lang_macro_offset) script->syntax.lang_macro_active = false; + else if (script->str_data[func].val == script->buildin_lang_macro_fmtstring_offset) + script->syntax.lang_macro_fmtstring_active = false; } - if( nested_call ) - script->syntax.nested_call--; - - if( !script->syntax.nested_call ) - script->syntax.last_func = -1; - - if( !macro ) + if (!macro) { + if (0 == --script->syntax.nested_call) + script->syntax.last_func = -1; script->addc(C_FUNC); + } return p; } /// Processes end of logical script line. /// @param first When true, only fix up scheduling data is initialized /// @param p Script position for error reporting in set_label -void parse_nextline(bool first, const char* p) +static void parse_nextline(bool first, const char *p) { if( !first ) { script->addc(C_EOL); // mark end of line for stack cleanup - script->set_label(LABEL_NEXTLINE, script->pos, p); // fix up '-' labels + script->set_label(LABEL_NEXTLINE, VECTOR_LENGTH(script->buf), p); // fix up '-' labels } // initialize data for new '-' label fix up scheduling @@ -955,7 +987,7 @@ void parse_nextline(bool first, const char* p) * Pushes a variable into stack, processing its array index if needed. * @see parse_variable */ -void parse_variable_sub_push(int word, const char *p2) +static void parse_variable_sub_push(int word, const char *p2) { if( p2 ) { const char* p3 = NULL; @@ -986,13 +1018,14 @@ void parse_variable_sub_push(int word, const char *p2) /// Parse a variable assignment using the direct equals operator /// @param p script position where the function should run from /// @return NULL if not a variable assignment, the new position otherwise -const char* parse_variable(const char* p) +static const char *parse_variable(const char *p) { int word; c_op type = C_NOP; const char *p2 = NULL; const char *var = p; + nullpo_retr(NULL, p); if( ( p[0] == '+' && p[1] == '+' && (type = C_ADD_PRE, true) ) // pre ++ || ( p[0] == '-' && p[1] == '-' && (type = C_SUB_PRE, true) ) // pre -- ) { @@ -1030,6 +1063,7 @@ const char* parse_variable(const char* p) || ( p[0] == '|' && p[1] == '=' && (type = C_OR, true) ) // |= || ( p[0] == '&' && p[1] == '=' && (type = C_AND, true) ) // &= || ( p[0] == '*' && p[1] == '=' && (type = C_MUL, true) ) // *= + || ( p[0] == '*' && p[1] == '*' && p[2] == '=' && (type = C_POW, true) ) // **= || ( p[0] == '/' && p[1] == '=' && (type = C_DIV, true) ) // /= || ( p[0] == '%' && p[1] == '=' && (type = C_MOD, true) ) // %= || ( p[0] == '+' && p[1] == '+' && (type = C_ADD_POST, true) ) // post ++ @@ -1053,6 +1087,7 @@ const char* parse_variable(const char* p) case C_L_SHIFT: // <<= case C_R_SHIFT: // >>= + case C_POW: // **= p = script->skip_space( &p[3] ); break; @@ -1066,6 +1101,8 @@ const char* parse_variable(const char* p) } // push the set function onto the stack + script->syntax.nested_call++; + script->syntax.last_func = script->str_data[script->buildin_set_ref].val; script->addl(script->buildin_set_ref); script->addc(C_ARG); @@ -1117,6 +1154,8 @@ const char* parse_variable(const char* p) // close the script by appending the function operator script->addc(C_FUNC); + if (--script->syntax.nested_call == 0) + script->syntax.last_func = -1; // push the buffer from the method return p; @@ -1135,7 +1174,8 @@ const char* parse_variable(const char* p) * @param p Pointer to the string to check * @return Whether the string is a number literal */ -bool is_number(const char *p) { +static bool is_number(const char *p) +{ const char *np; if (!p) return false; @@ -1159,13 +1199,22 @@ bool is_number(const char *p) { } /** + * Duplicates a script string into the script string list. * - **/ -int script_string_dup(char *str) { - size_t len = strlen(str); + * Grows the script string list as needed. + * + * @param str The string to insert. + * @return the string position in the script string list. + */ +static int script_string_dup(char *str) +{ + int len; int pos = script->string_list_pos; - while( pos+len+1 >= script->string_list_size ) { + nullpo_retr(pos, str); + len = (int)strlen(str); + + while (pos+len+1 >= script->string_list_size) { script->string_list_size += (1024*1024)/2; RECREATE(script->string_list,char,script->string_list_size); } @@ -1179,240 +1228,214 @@ int script_string_dup(char *str) { /*========================================== * Analysis section *------------------------------------------*/ -const char* parse_simpleexpr(const char *p) +static const char *parse_simpleexpr(const char *p) { p=script->skip_space(p); - if(*p==';' || *p==',') + nullpo_retr(NULL, p); + if (*p == ';' || *p == ',') disp_error_message("parse_simpleexpr: unexpected end of expression",p); - if(*p=='(') { - int i = script->syntax.curly_count-1; - if (i >= 0 && script->syntax.curly[i].type == TYPE_ARGLIST) - ++script->syntax.curly[i].count; - p=script->parse_subexpr(p+1,-1); - p=script->skip_space(p); - if( (i=script->syntax.curly_count-1) >= 0 && script->syntax.curly[i].type == TYPE_ARGLIST - && script->syntax.curly[i].flag == ARGLIST_UNDEFINED && --script->syntax.curly[i].count == 0 - ) { - if( *p == ',' ) { - script->syntax.curly[i].flag = ARGLIST_PAREN; - return p; - } else { - script->syntax.curly[i].flag = ARGLIST_NO_PAREN; - } - } - if( *p != ')' ) - disp_error_message("parse_simpleexpr: unmatched ')'",p); - ++p; - } else if(is_number(p)) { - char *np; - long long lli; - while(*p == '0' && ISDIGIT(p[1])) p++; // Skip leading zeros, we don't support octal literals - lli=strtoll(p,&np,0); - if( lli < INT_MIN ) { - lli = INT_MIN; - script->disp_warning_message("parse_simpleexpr: underflow detected, capping value to INT_MIN",p); - } else if( lli > INT_MAX ) { - lli = INT_MAX; - script->disp_warning_message("parse_simpleexpr: overflow detected, capping value to INT_MAX",p); - } - script->addi((int)lli); // Cast is safe, as it's already been checked for overflows - p=np; - } else if(*p=='"') { - struct string_translation *st = NULL; - const char *start_point = p; - bool duplicate = true; - struct script_string_buf *sbuf = &script->parse_simpleexpr_str; - - do { - p++; - while( *p && *p != '"' ) { - if( (unsigned char)p[-1] <= 0x7e && *p == '\\' ) { - char buf[8]; - size_t len = sv->skip_escaped_c(p) - p; - size_t n = sv->unescape_c(buf, p, len); - if( n != 1 ) - ShowDebug("parse_simpleexpr: unexpected length %d after unescape (\"%.*s\" -> %.*s)\n", (int)n, (int)len, p, (int)n, buf); - p += len; - script_string_buf_addb(sbuf, *buf); - continue; - } else if( *p == '\n' ) { - disp_error_message("parse_simpleexpr: unexpected newline @ string",p); - } - script_string_buf_addb(sbuf, *p++); - } - if(!*p) - disp_error_message("parse_simpleexpr: unexpected end of file @ string",p); - p++; //'"' - p = script->skip_space(p); - } while( *p && *p == '"' ); - - script_string_buf_addb(sbuf, 0); - - if (!(script->syntax.translation_db && (st = strdb_get(script->syntax.translation_db, sbuf->ptr)) != NULL)) { - script->addc(C_STR); - - if( script->pos+sbuf->pos >= script->size ) { - do { - script->size += SCRIPT_BLOCK_SIZE; - } while( script->pos+sbuf->pos >= script->size ); - RECREATE(script->buf,unsigned char,script->size); - } + if (*p == '(') { + return script->parse_simpleexpr_paren(p); + } else if (is_number(p)) { + return script->parse_simpleexpr_number(p); + } else if(*p == '"') { + return script->parse_simpleexpr_string(p); + } else { + return script->parse_simpleexpr_name(p); + } +} - memcpy(script->buf+script->pos, sbuf->ptr, sbuf->pos); - script->pos += sbuf->pos; +static const char *parse_simpleexpr_paren(const char *p) +{ + int i = script->syntax.curly_count - 1; + nullpo_retr(NULL, p); + if (i >= 0 && script->syntax.curly[i].type == TYPE_ARGLIST) + ++script->syntax.curly[i].count; + p = script->parse_subexpr(p + 1, -1); + p = script->skip_space(p); + if ((i = script->syntax.curly_count - 1) >= 0 + && script->syntax.curly[i].type == TYPE_ARGLIST + && script->syntax.curly[i].flag == ARGLIST_UNDEFINED + && --script->syntax.curly[i].count == 0 + ) { + if (*p == ',') { + script->syntax.curly[i].flag = ARGLIST_PAREN; + return p; } else { - int expand = sizeof(int) + sizeof(uint8); - unsigned char j; - unsigned int st_cursor = 0; + script->syntax.curly[i].flag = ARGLIST_NO_PAREN; + } + } + if (*p != ')') + disp_error_message("parse_simpleexpr: unmatched ')'", p); - script->addc(C_LSTR); + return p + 1; +} - expand += (sizeof(char*) + sizeof(uint8)) * st->translations; +static const char *parse_simpleexpr_number(const char *p) +{ + char *np = NULL; + long long lli; - while( script->pos+expand >= script->size ) { - script->size += SCRIPT_BLOCK_SIZE; - RECREATE(script->buf,unsigned char,script->size); - } + nullpo_retr(NULL, p); + while (*p == '0' && ISDIGIT(p[1])) + p++; // Skip leading zeros, we don't support octal literals - *((int *)(&script->buf[script->pos])) = st->string_id; - *((uint8 *)(&script->buf[script->pos + sizeof(int)])) = st->translations; + lli = strtoll(p, &np, 0); + if (lli < INT_MIN) { + lli = INT_MIN; + script->disp_warning_message("parse_simpleexpr: underflow detected, capping value to INT_MIN", p); + } else if (lli > INT_MAX) { + lli = INT_MAX; + script->disp_warning_message("parse_simpleexpr: overflow detected, capping value to INT_MAX", p); + } + script->addi((int)lli); // Cast is safe, as it's already been checked for overflows - script->pos += sizeof(int) + sizeof(uint8); + return np; +} - for(j = 0; j < st->translations; j++) { - *((uint8 *)(&script->buf[script->pos])) = RBUFB(st->buf, st_cursor); - *((char **)(&script->buf[script->pos+sizeof(uint8)])) = &st->buf[st_cursor + sizeof(uint8)]; - script->pos += sizeof(char*) + sizeof(uint8); - st_cursor += sizeof(uint8); - while(st->buf[st_cursor++]); - st_cursor += sizeof(uint8); - } - } +static const char *parse_simpleexpr_string(const char *p) +{ + const char *start_point = p; - /* When exporting we don't know what is a translation and what isn't */ - if( script->lang_export_fp && sbuf->pos > 1 ) {//sbuf->pos will always be at least 1 because of the '\0' - if( !script->syntax.strings ) { - script->syntax.strings = strdb_alloc(DB_OPT_DUP_KEY|DB_OPT_ALLOW_NULL_DATA, 0); + nullpo_retr(NULL, p); + do { + p++; + while (*p != '\0' && *p != '"') { + if ((unsigned char)p[-1] <= 0x7e && *p == '\\') { + char buf[8]; + size_t len = sv->skip_escaped_c(p) - p; + size_t n = sv->unescape_c(buf, p, len); + if (n != 1) + ShowDebug("parse_simpleexpr: unexpected length %d after unescape (\"%.*s\" -> %.*s)\n", (int)n, (int)len, p, (int)n, buf); + p += len; + VECTOR_ENSURE(script->parse_simpleexpr_strbuf, 1, 512); + VECTOR_PUSH(script->parse_simpleexpr_strbuf, buf[0]); + continue; } - - if( !strdb_exists(script->syntax.strings,sbuf->ptr) ) { - strdb_put(script->syntax.strings, sbuf->ptr, NULL); - duplicate = false; + if (*p == '\n') { + disp_error_message("parse_simpleexpr: unexpected newline @ string", p); } + VECTOR_ENSURE(script->parse_simpleexpr_strbuf, 1, 512); + VECTOR_PUSH(script->parse_simpleexpr_strbuf, *p++); } + if (*p == '\0') + disp_error_message("parse_simpleexpr: unexpected end of file @ string", p); + p++; //'"' + p = script->skip_space(p); + } while (*p != '\0' && *p == '"'); - if( script->lang_export_fp && !duplicate && - ( ( ( script->syntax.last_func == script->buildin_mes_offset || - script->syntax.last_func == script->buildin_select_offset ) && !script->syntax.nested_call - ) || script->syntax.lang_macro_active ) ) { - const char *line_start = start_point; - const char *line_end = start_point; - struct script_string_buf *lbuf = &script->lang_export_line_buf; - struct script_string_buf *ubuf = &script->lang_export_unescaped_buf; - size_t line_length, cursor; + VECTOR_ENSURE(script->parse_simpleexpr_strbuf, 1, 512); + VECTOR_PUSH(script->parse_simpleexpr_strbuf, '\0'); - while( line_start > script->parser_current_src ) { - if( *line_start != '\n' ) - line_start--; - else - break; - } + script->add_translatable_string(&script->parse_simpleexpr_strbuf, start_point); - while( *line_end != '\n' && *line_end != '\0' ) - line_end++; + VECTOR_TRUNCATE(script->parse_simpleexpr_strbuf); - line_length = (size_t)(line_end - line_start); - if( line_length > 0 ) { - script_string_buf_ensure(lbuf,line_length + 1); + return p; +} - memcpy(lbuf->ptr, line_start, line_length); - lbuf->pos = line_length; - script_string_buf_addb(lbuf, 0); +static const char *parse_simpleexpr_name(const char *p) +{ + int l; + const char *pv = NULL; - normalize_name(lbuf->ptr, "\r\n\t "); - } + // label , register , function etc + if (script->skip_word(p) == p) + disp_error_message("parse_simpleexpr: unexpected character", p); - for(cursor = 0; cursor < sbuf->pos; cursor++) { - if( sbuf->ptr[cursor] == '"' ) - script_string_buf_addb(ubuf, '\\'); - script_string_buf_addb(ubuf, sbuf->ptr[cursor]); - } - script_string_buf_addb(ubuf, 0); - - fprintf(script->lang_export_fp, "#: %s\n" - "# %s\n" - "msgctxt \"%s\"\n" - "msgid \"%s\"\n" - "msgstr \"\"\n", - script->parser_current_file ? script->parser_current_file : "Unknown File", - lbuf->ptr, - script->parser_current_npc_name ? script->parser_current_npc_name : "Unknown NPC", - ubuf->ptr - ); - lbuf->pos = 0; - ubuf->pos = 0; + l = script->add_word(p); + if (script->str_data[l].type == C_FUNC || script->str_data[l].type == C_USERFUNC || script->str_data[l].type == C_USERFUNC_POS) { + return script->parse_callfunc(p,1,0); +#ifdef SCRIPT_CALLFUNC_CHECK + } else { + const char *name = script->get_str(l); + if (strdb_get(script->userfunc_db,name) != NULL) { + return script->parse_callfunc(p, 1, 1); } - sbuf->pos = 0; +#endif + } + + if ((pv = script->parse_variable(p)) != NULL) { + // successfully processed a variable assignment + return pv; + } + + if (script->str_data[l].type == C_INT && script->str_data[l].deprecated) { + disp_warning_message("This constant is deprecated and it will be removed in a future version. Please see the script documentation and constants.conf for an alternative.\n", p); + } + + p = script->skip_word(p); + if (*p == '[') { + // array(name[i] => getelementofarray(name,i) ) + script->addl(script->buildin_getelementofarray_ref); + script->addc(C_ARG); + script->addl(l); + + p = script->parse_subexpr(p + 1, -1); + p = script->skip_space(p); + if (*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 { - int l; - const char* pv; + script->addl(l); + } - // label , register , function etc - if(script->skip_word(p)==p) - disp_error_message("parse_simpleexpr: unexpected character",p); + return p; +} - l=script->add_word(p); - if( script->str_data[l].type == C_FUNC || script->str_data[l].type == C_USERFUNC || script->str_data[l].type == C_USERFUNC_POS) { - return script->parse_callfunc(p,1,0); -#ifdef SCRIPT_CALLFUNC_CHECK - } else { - const char* name = script->get_str(l); - if( strdb_get(script->userfunc_db,name) != NULL ) { - return script->parse_callfunc(p,1,1); - } -#endif - } +static void script_add_translatable_string(const struct script_string_buf *string, const char *start_point) +{ + struct string_translation *st = NULL; - if( (pv = script->parse_variable(p)) ) { - // successfully processed a variable assignment - return pv; - } + nullpo_retv(string); + if (script->syntax.translation_db == NULL + || (st = strdb_get(script->syntax.translation_db, VECTOR_DATA(*string))) == NULL) { + script->addc(C_STR); - if (script->str_data[l].type == C_INT && script->str_data[l].deprecated) { - disp_warning_message("This constant is deprecated and it will be removed in a future version. Please see the script documentation and constants.conf for an alternative.\n", p); - } + VECTOR_ENSURE(script->buf, VECTOR_LENGTH(*string), SCRIPT_BLOCK_SIZE); - p=script->skip_word(p); - if( *p == '[' ) { - // array(name[i] => getelementofarray(name,i) ) - script->addl(script->buildin_getelementofarray_ref); - script->addc(C_ARG); - script->addl(l); + VECTOR_PUSHARRAY(script->buf, VECTOR_DATA(*string), VECTOR_LENGTH(*string)); + } else { + unsigned char u; + int st_cursor = 0; - p=script->parse_subexpr(p+1,-1); - p=script->skip_space(p); - if( *p != ']' ) - disp_error_message("parse_simpleexpr: unmatched ']'",p); - ++p; - script->addc(C_FUNC); - } else { - script->addl(l); - } + script->addc(C_LSTR); - } + VECTOR_ENSURE(script->buf, (int)(sizeof(st->string_id) + sizeof(st->translations)), SCRIPT_BLOCK_SIZE); + VECTOR_PUSHARRAY(script->buf, (void *)&st->string_id, sizeof(st->string_id)); + VECTOR_PUSHARRAY(script->buf, (void *)&st->translations, sizeof(st->translations)); - return p; + for (u = 0; u != st->translations; u++) { + struct string_translation_entry *entry = (void *)(st->buf+st_cursor); + char *stringptr = &entry->string[0]; + st_cursor += sizeof(*entry); + VECTOR_ENSURE(script->buf, (int)(sizeof(entry->lang_id) + sizeof(char *)), SCRIPT_BLOCK_SIZE); + VECTOR_PUSHARRAY(script->buf, (void *)&entry->lang_id, sizeof(entry->lang_id)); + VECTOR_PUSHARRAY(script->buf, (void *)&stringptr, sizeof(stringptr)); + st_cursor += sizeof(uint8); // FIXME: What are we skipping here? + while (st->buf[st_cursor++] != 0) + (void)0; // Skip string + st_cursor += sizeof(uint8); // FIXME: What are we skipping here? + } + } } /*========================================== * Analysis of the expression *------------------------------------------*/ -const char* script_parse_subexpr(const char* p,int limit) +static const char *script_parse_subexpr(const char *p, int limit) { int op,opl,len; + nullpo_retr(NULL, p); p=script->skip_space(p); if( *p == '-' ) { @@ -1435,8 +1458,9 @@ const char* script_parse_subexpr(const char* p,int limit) p=script->skip_space(p); while(( (op=C_OP3, opl=0, len=1,*p=='?') // ?: - || (op=C_ADD, opl=9, len=1,*p=='+') // + - || (op=C_SUB, opl=9, len=1,*p=='-') // - + || (op=C_ADD, opl=9, len=1,*p=='+' && p[1]!='+') // + + || (op=C_SUB, opl=9, len=1,*p=='-' && p[1]!='-') // - + || (op=C_POW, opl=11,len=2,*p=='*' && p[1]=='*') // ** || (op=C_MUL, opl=10,len=1,*p=='*') // * || (op=C_DIV, opl=10,len=1,*p=='/') // / || (op=C_MOD, opl=10,len=1,*p=='%') // % @@ -1476,8 +1500,9 @@ const char* script_parse_subexpr(const char* p,int limit) /*========================================== * Evaluation of the expression *------------------------------------------*/ -const char* parse_expr(const char *p) +static const char *parse_expr(const char *p) { + nullpo_retr(NULL, p); switch(*p) { case ')': case ';': case ':': case '[': case ']': case '}': @@ -1490,10 +1515,11 @@ const char* parse_expr(const char *p) /*========================================== * Analysis of the line *------------------------------------------*/ -const char* parse_line(const char* p) +static const char *parse_line(const char *p) { const char* p2; + nullpo_retr(NULL, p); p=script->skip_space(p); if(*p==';') { //Close decision for if(); for(); while(); @@ -1552,8 +1578,9 @@ const char* parse_line(const char* p) } // { ... } Closing process -const char* parse_curly_close(const char* p) +static const char *parse_curly_close(const char *p) { + nullpo_retr(NULL, p); if(script->syntax.curly_count <= 0) { disp_error_message("parse_curly_close: unexpected string",p); return p + 1; @@ -1582,7 +1609,7 @@ const char* parse_curly_close(const char* p) // You are here labeled sprintf(label,"__SW%x_%x", (unsigned int)script->syntax.curly[pos].index, (unsigned int)script->syntax.curly[pos].count); l=script->add_str(label); - script->set_label(l,script->pos, p); + script->set_label(l, VECTOR_LENGTH(script->buf), p); if(script->syntax.curly[pos].flag) { //Exists default @@ -1595,7 +1622,7 @@ const char* parse_curly_close(const char* p) // Label end sprintf(label,"__SW%x_FIN", (unsigned int)script->syntax.curly[pos].index); l=script->add_str(label); - script->set_label(l,script->pos, p); + script->set_label(l, VECTOR_LENGTH(script->buf), p); linkdb_final(&script->syntax.curly[pos].case_label); // free the list of case label script->syntax.curly_count--; //Closing decision if, for , while @@ -1610,10 +1637,11 @@ const char* parse_curly_close(const char* p) // Syntax-related processing // break, case, continue, default, do, for, function, // if, switch, while ? will handle this internally. -const char* parse_syntax(const char* p) +static const char *parse_syntax(const char *p) { const char *p2 = script->skip_word(p); + nullpo_retr(NULL, p); switch(*p) { case 'B': case 'b': @@ -1674,7 +1702,7 @@ const char* parse_syntax(const char* p) // You are here labeled sprintf(label,"__SW%x_%x", (unsigned int)script->syntax.curly[pos].index, (unsigned int)script->syntax.curly[pos].count); l=script->add_str(label); - script->set_label(l,script->pos, p); + script->set_label(l, VECTOR_LENGTH(script->buf), p); } //Decision statement switch p = script->skip_space(p2); @@ -1714,7 +1742,7 @@ const char* parse_syntax(const char* p) // Label after the completion of FALLTHRU sprintf(label, "__SW%x_%xJ", (unsigned int)script->syntax.curly[pos].index, (unsigned int)script->syntax.curly[pos].count); l=script->add_str(label); - script->set_label(l,script->pos,p); + script->set_label(l, VECTOR_LENGTH(script->buf), p); } // check duplication of case label [Rayce] if(linkdb_search(&script->syntax.curly[pos].case_label, (void*)h64BPTRSIZE(v)) != NULL) @@ -1781,7 +1809,7 @@ const char* parse_syntax(const char* p) } sprintf(label, "__SW%x_%x", (unsigned int)script->syntax.curly[pos].index, (unsigned int)script->syntax.curly[pos].count); l=script->add_str(label); - script->set_label(l,script->pos,p); + script->set_label(l, VECTOR_LENGTH(script->buf), p); // Skip to the next link w/o condition sprintf(label, "goto __SW%x_%x;", (unsigned int)script->syntax.curly[pos].index, (unsigned int)script->syntax.curly[pos].count + 1); @@ -1792,7 +1820,7 @@ const char* parse_syntax(const char* p) // The default label sprintf(label, "__SW%x_DEF", (unsigned int)script->syntax.curly[pos].index); l=script->add_str(label); - script->set_label(l,script->pos,p); + script->set_label(l, VECTOR_LENGTH(script->buf), p); script->syntax.curly[script->syntax.curly_count - 1].flag = 1; script->syntax.curly[pos].count++; @@ -1810,7 +1838,7 @@ const char* parse_syntax(const char* p) // Label of the (do) form here sprintf(label, "__DO%x_BGN", (unsigned int)script->syntax.curly[script->syntax.curly_count].index); l=script->add_str(label); - script->set_label(l,script->pos,p); + script->set_label(l, VECTOR_LENGTH(script->buf), p); script->syntax.curly_count++; return p; } @@ -1841,7 +1869,7 @@ const char* parse_syntax(const char* p) // Form the start of label decision sprintf(label, "__FR%x_J", (unsigned int)script->syntax.curly[pos].index); l=script->add_str(label); - script->set_label(l,script->pos,p); + script->set_label(l, VECTOR_LENGTH(script->buf), p); p=script->skip_space(p); if(*p == ';') { @@ -1870,7 +1898,7 @@ const char* parse_syntax(const char* p) // Labels to form the next loop sprintf(label, "__FR%x_NXT", (unsigned int)script->syntax.curly[pos].index); l=script->add_str(label); - script->set_label(l,script->pos,p); + script->set_label(l, VECTOR_LENGTH(script->buf), p); // Process the next time you enter the loop // A ')' last for; flag to be treated as' @@ -1889,7 +1917,7 @@ const char* parse_syntax(const char* p) // Loop start labeling sprintf(label, "__FR%x_BGN", (unsigned int)script->syntax.curly[pos].index); l=script->add_str(label); - script->set_label(l,script->pos,p); + script->set_label(l, VECTOR_LENGTH(script->buf), p); return p; } else if( p2 - p == 8 && strncmp(p, "function", 8) == 0 ) { // internal script function @@ -1939,9 +1967,9 @@ const char* parse_syntax(const char* p) if( script->str_data[l].type == C_NOP || script->str_data[l].type == C_USERFUNC )// register only, if the name was not used by something else { script->str_data[l].type = C_USERFUNC; - script->set_label(l, script->pos, p); + script->set_label(l, VECTOR_LENGTH(script->buf), p); if( script->parse_options&SCRIPT_USE_LABEL_DB ) - script->label_add(l,script->pos); + script->label_add(l, VECTOR_LENGTH(script->buf)); } else disp_error_message("parse_syntax:function: function name is invalid", func_name); @@ -2021,7 +2049,7 @@ const char* parse_syntax(const char* p) // Form the start of label decision sprintf(label, "__WL%x_NXT", (unsigned int)script->syntax.curly[script->syntax.curly_count].index); l=script->add_str(label); - script->set_label(l,script->pos,p); + script->set_label(l, VECTOR_LENGTH(script->buf), p); // Skip to the end point if the condition is false sprintf(label, "__WL%x_FIN", (unsigned int)script->syntax.curly[script->syntax.curly_count].index); @@ -2039,10 +2067,12 @@ const char* parse_syntax(const char* p) return NULL; } -const char* parse_syntax_close(const char *p) { +static const char *parse_syntax_close(const char *p) +{ // If (...) for (...) hoge (); as to make sure closed closed once again int flag; + nullpo_retr(NULL, p); do { p = script->parse_syntax_close_sub(p,&flag); } while(flag); @@ -2052,7 +2082,7 @@ const char* parse_syntax_close(const char *p) { // Close judgment if, for, while, of do // flag == 1 : closed // flag == 0 : not closed -const char* parse_syntax_close_sub(const char* p,int* flag) +static const char *parse_syntax_close_sub(const char *p, int *flag) { char label[256]; int pos = script->syntax.curly_count - 1; @@ -2078,7 +2108,7 @@ const char* parse_syntax_close_sub(const char* p,int* flag) // Put the label of the location sprintf(label, "__IF%x_%x", (unsigned int)script->syntax.curly[pos].index, (unsigned int)script->syntax.curly[pos].count); l=script->add_str(label); - script->set_label(l,script->pos,p); + script->set_label(l, VECTOR_LENGTH(script->buf), p); script->syntax.curly[pos].count++; p = script->skip_space(p); @@ -2116,7 +2146,7 @@ const char* parse_syntax_close_sub(const char* p,int* flag) // Put the label of the final location sprintf(label, "__IF%x_FIN", (unsigned int)script->syntax.curly[pos].index); l=script->add_str(label); - script->set_label(l,script->pos,p); + script->set_label(l, VECTOR_LENGTH(script->buf), p); if(script->syntax.curly[pos].flag == 1) { // Because the position of the pointer is the same if not else for this return bp; @@ -2129,7 +2159,7 @@ const char* parse_syntax_close_sub(const char* p,int* flag) // (Come here continue) to form the label here sprintf(label, "__DO%x_NXT", (unsigned int)script->syntax.curly[pos].index); l=script->add_str(label); - script->set_label(l,script->pos,p); + script->set_label(l, VECTOR_LENGTH(script->buf), p); } // Skip to the end point if the condition is false @@ -2164,7 +2194,7 @@ const char* parse_syntax_close_sub(const char* p,int* flag) // Form label of the end point conditions sprintf(label, "__DO%x_FIN", (unsigned int)script->syntax.curly[pos].index); l=script->add_str(label); - script->set_label(l,script->pos,p); + script->set_label(l, VECTOR_LENGTH(script->buf), p); p = script->skip_space(p); if(*p != ';') { disp_error_message("parse_syntax: need ';'",p); @@ -2186,7 +2216,7 @@ const char* parse_syntax_close_sub(const char* p,int* flag) // End for labeling sprintf(label, "__FR%x_FIN", (unsigned int)script->syntax.curly[pos].index); l=script->add_str(label); - script->set_label(l,script->pos,p); + script->set_label(l, VECTOR_LENGTH(script->buf), p); script->syntax.curly_count--; return p; } else if(script->syntax.curly[pos].type == TYPE_WHILE) { @@ -2202,7 +2232,7 @@ const char* parse_syntax_close_sub(const char* p,int* flag) // End while labeling sprintf(label, "__WL%x_FIN", (unsigned int)script->syntax.curly[pos].index); l=script->add_str(label); - script->set_label(l,script->pos,p); + script->set_label(l, VECTOR_LENGTH(script->buf), p); script->syntax.curly_count--; return p; } else if(script->syntax.curly[pos].type == TYPE_USERFUNC) { @@ -2215,7 +2245,7 @@ const char* parse_syntax_close_sub(const char* p,int* flag) // Put the label of the location sprintf(label, "__FN%x_FIN", (unsigned int)script->syntax.curly[pos].index); l=script->add_str(label); - script->set_label(l,script->pos,p); + script->set_label(l, VECTOR_LENGTH(script->buf), p); script->syntax.curly_count--; return p; } else { @@ -2225,10 +2255,11 @@ const char* parse_syntax_close_sub(const char* p,int* flag) } /// Retrieves the value of a constant. -bool script_get_constant(const char* name, int* value) +static bool script_get_constant(const char *name, int *value) { int n = script->search_str(name); + nullpo_retr(false, value); if( n == -1 || script->str_data[n].type != C_INT ) {// not found or not a constant return false; @@ -2242,11 +2273,11 @@ bool script_get_constant(const char* name, int* value) } /// Creates new constant or parameter with given value. -void script_set_constant(const char *name, int value, bool is_parameter, bool is_deprecated) +static void script_set_constant(const char *name, int value, bool is_parameter, bool is_deprecated) { int n = script->add_str(name); - if( script->str_data[n].type == C_NOP ) {// new + if (script->str_data[n].type == C_NOP) { script->str_data[n].type = is_parameter ? C_PARAM : C_INT; script->str_data[n].val = value; script->str_data[n].deprecated = is_deprecated ? 1 : 0; @@ -2257,7 +2288,7 @@ void script_set_constant(const char *name, int value, bool is_parameter, bool is } } /* adds data to a existent constant in the database, inserted normally via parse */ -void script_set_constant2(const char *name, int value, bool is_parameter, bool is_deprecated) +static void script_set_constant2(const char *name, int value, bool is_parameter, bool is_deprecated) { int n = script->add_str(name); @@ -2290,7 +2321,7 @@ void script_set_constant2(const char *name, int value, bool is_parameter, bool i /** * Loads the constants database from constants.conf */ -void read_constdb(void) +static void read_constdb(bool reload) { struct config_t constants_conf; char filepath[256]; @@ -2298,7 +2329,7 @@ void read_constdb(void) struct config_setting_t *t; int i = 0; - sprintf(filepath, "%s/constants.conf", map->db_path); + safesnprintf(filepath, 256, "%s/constants.conf", map->db_path); if (!libconfig->load_file(&constants_conf, filepath)) return; @@ -2309,7 +2340,6 @@ void read_constdb(void) } while ((t = libconfig->setting_get_elem(cdb, i++))) { - bool is_parameter = false; bool is_deprecated = false; int value = 0; const char *name = config_setting_name(t); @@ -2340,10 +2370,6 @@ void read_constdb(void) continue; } value = i32; - if (libconfig->setting_lookup_bool(t, "Parameter", &i32)) { - if (i32 != 0) - is_parameter = true; - } if (libconfig->setting_lookup_bool(t, "Deprecated", &i32)) { if (i32 != 0) is_deprecated = true; @@ -2351,9 +2377,13 @@ void read_constdb(void) } else { value = libconfig->setting_get_int(t); } - if (is_parameter) - ShowWarning("read_constdb: Defining parameters in the constants configuration is deprecated and will no longer be possible in a future version. Parameters should be defined in source. (parameter = '%s')\n", name); - script->set_constant(name, value, is_parameter, is_deprecated); + + if (reload) { + int n = script->add_str(name); + script->str_data[n].type = C_NOP; // ensures it will be overwritten + } + + script->set_constant(name, value, false, is_deprecated); } script->constdb_comment(NULL); libconfig->destroy(&constants_conf); @@ -2366,12 +2396,12 @@ void read_constdb(void) * * @param comment The comment to set (NULL to unset) */ -void script_constdb_comment(const char *comment) +static void script_constdb_comment(const char *comment) { (void)comment; } -void script_load_parameters(void) +static void script_load_parameters(void) { int i = 0; struct { @@ -2391,6 +2421,7 @@ void script_load_parameters(void) {"SkillPoint", SP_SKILLPOINT}, {"Class", SP_CLASS}, {"Zeny", SP_ZENY}, + {"BankVault", SP_BANKVAULT}, {"Sex", SP_SEX}, {"NextBaseExp", SP_NEXTBASEEXP}, {"NextJobExp", SP_NEXTJOBEXP}, @@ -2425,7 +2456,7 @@ void script_load_parameters(void) /*========================================== * Display emplacement line of script *------------------------------------------*/ -const char* script_print_line(StringBuf* buf, const char* p, const char* mark, int line) +static const char *script_print_line(StringBuf *buf, const char *p, const char *mark, int line) { int i, mark_pos = 0, tabstop = TAB_SIZE; if( p == NULL || !p[0] ) return NULL; @@ -2464,7 +2495,8 @@ const char* script_print_line(StringBuf* buf, const char* p, const char* mark, i #undef update_tabstop #define CONTEXTLINES 3 -void script_errorwarning_sub(StringBuf *buf, const char* src, const char* file, int start_line, const char* error_msg, const char* error_pos) { +static void script_errorwarning_sub(StringBuf *buf, const char *src, const char *file, int start_line, const char *error_msg, const char *error_pos) +{ // Find the line where the error occurred int j; int line = start_line; @@ -2500,7 +2532,8 @@ void script_errorwarning_sub(StringBuf *buf, const char* src, const char* file, } #undef CONTEXTLINES -void script_error(const char* src, const char* file, int start_line, const char* error_msg, const char* error_pos) { +static void script_error(const char *src, const char *file, int start_line, const char *error_msg, const char *error_pos) +{ StringBuf buf; StrBuf->Init(&buf); @@ -2512,7 +2545,8 @@ void script_error(const char* src, const char* file, int start_line, const char* StrBuf->Destroy(&buf); } -void script_warning(const char* src, const char* file, int start_line, const char* error_msg, const char* error_pos) { +static void script_warning(const char *src, const char *file, int start_line, const char *error_msg, const char *error_pos) +{ StringBuf buf; StrBuf->Init(&buf); @@ -2526,7 +2560,8 @@ void script_warning(const char* src, const char* file, int start_line, const cha /*========================================== * Analysis of the script *------------------------------------------*/ -struct script_code* parse_script(const char *src,const char *file,int line,int options, int *retval) { +static struct script_code *parse_script(const char *src, const char *file, int line, int options, int *retval) +{ const char *p,*tmpp; int i; struct script_code* code = NULL; @@ -2544,9 +2579,6 @@ struct script_code* parse_script(const char *src,const char *file,int line,int o script->parse_cleanup_timer_id = timer->add(timer->gettick() + 10, script->parse_cleanup_timer, 0, 0); } - if( script->syntax.strings ) /* used only when generating translation file */ - db_destroy(script->syntax.strings); - memset(&script->syntax,0,sizeof(script->syntax)); script->syntax.last_func = -1;/* as valid values are >= 0 */ if( script->parser_current_npc_name ) { @@ -2556,11 +2588,7 @@ struct script_code* parse_script(const char *src,const char *file,int line,int o script->syntax.translation_db = strdb_get(script->translation_db, script->parser_current_npc_name); } - if( !script->buf ) { - script->buf = (unsigned char *)aMalloc(SCRIPT_BLOCK_SIZE*sizeof(unsigned char)); - script->size = SCRIPT_BLOCK_SIZE; - } - script->pos=0; + VECTOR_TRUNCATE(script->buf); script->parse_nextline(true, NULL); // who called parse_script is responsible for clearing the database after using it, but just in case... lets clear it here @@ -2574,7 +2602,7 @@ struct script_code* parse_script(const char *src,const char *file,int line,int o if( script->error_report ) script->error(src,file,line,script->error_msg,script->error_pos); aFree( script->error_msg ); - script->pos = 0; + VECTOR_TRUNCATE(script->buf); for(i=LABEL_START;i<script->str_num;i++) if(script->str_data[i].type == C_NOP) script->str_data[i].type = C_NAME; for(i=0; i<size; i++) @@ -2594,9 +2622,9 @@ struct script_code* parse_script(const char *src,const char *file,int line,int o p=script->skip_space(p); if( options&SCRIPT_IGNORE_EXTERNAL_BRACKETS ) {// does not require brackets around the script - if( *p == '\0' && !(options&SCRIPT_RETURN_EMPTY_SCRIPT) ) - {// empty script and can return NULL - script->pos = 0; + if (*p == '\0' && !(options&SCRIPT_RETURN_EMPTY_SCRIPT)) { + // empty script and can return NULL + VECTOR_TRUNCATE(script->buf); #ifdef ENABLE_CASE_CHECK script->local_casecheck.clear(); script->parser_current_src = NULL; @@ -2614,9 +2642,9 @@ struct script_code* parse_script(const char *src,const char *file,int line,int o if (retval) *retval = EXIT_FAILURE; } p = script->skip_space(p+1); - if( *p == '}' && !(options&SCRIPT_RETURN_EMPTY_SCRIPT) ) - {// empty script and can return NULL - script->pos = 0; + if (*p == '}' && !(options&SCRIPT_RETURN_EMPTY_SCRIPT)) { + // empty script and can return NULL + VECTOR_TRUNCATE(script->buf); #ifdef ENABLE_CASE_CHECK script->local_casecheck.clear(); script->parser_current_src = NULL; @@ -2648,9 +2676,9 @@ struct script_code* parse_script(const char *src,const char *file,int line,int o tmpp=script->skip_space(script->skip_word(p)); if(*tmpp==':' && !(strncmp(p,"default:",8) == 0 && p + 7 == tmpp)) { i=script->add_word(p); - script->set_label(i,script->pos,p); + script->set_label(i, VECTOR_LENGTH(script->buf), p); if( script->parse_options&SCRIPT_USE_LABEL_DB ) - script->label_add(i,script->pos); + script->label_add(i, VECTOR_LENGTH(script->buf)); p=tmpp+1; p=script->skip_space(p); continue; @@ -2672,8 +2700,8 @@ struct script_code* parse_script(const char *src,const char *file,int line,int o script->str_data[i].type=C_NAME; script->str_data[i].label=i; for (j = script->str_data[i].backpatch; j >= 0 && j != 0x00ffffff; ) { - int next = GETVALUE(script->buf,j); - SETVALUE(script->buf,j,i); + int next = GETVALUE(&script->buf, j); + SETVALUE(&script->buf, j, i); j = next; } } else if(script->str_data[i].type == C_USERFUNC) { @@ -2690,37 +2718,39 @@ struct script_code* parse_script(const char *src,const char *file,int line,int o } #ifdef SCRIPT_DEBUG_DISP - for(i=0;i<script->pos;i++) { - if((i&15)==0) ShowMessage("%04x : ",i); - ShowMessage("%02x ",script->buf[i]); - if((i&15)==15) ShowMessage("\n"); + for (i = 0; i < VECTOR_LENGTH(script->buf); i++) { + if ((i&15) == 0) + ShowMessage("%04x : ",i); + ShowMessage("%02x ", VECTOR_INDEX(script->buf, i)); + if ((i&15) == 15) + ShowMessage("\n"); } ShowMessage("\n"); #endif #ifdef SCRIPT_DEBUG_DISASM i = 0; - while(i < script->pos) { - int j = i; - c_op op = script->get_com(script->buf,&i); + while (i < VECTOR_LENGTH(script->buf)) { + c_op op = script->get_com(&script->buf, &i); + int j = i; // Note: i is modified in the line above. ShowMessage("%06x %s", i, script->op2name(op)); - j = i; - switch(op) { + + switch (op) { case C_INT: - ShowMessage(" %d", script->get_num(script->buf,&i)); + ShowMessage(" %d", script->get_num(&script->buf, &i)); break; case C_POS: - ShowMessage(" 0x%06x", *(int*)(script->buf+i)&0xffffff); + ShowMessage(" 0x%06x", *(int*)(&VECTOR_INDEX(script->buf, i))&0xffffff); i += 3; break; case C_NAME: - j = (*(int*)(script->buf+i)&0xffffff); + j = (*(int*)(&VECTOR_INDEX(script->buf, i))&0xffffff); ShowMessage(" %s", ( j == 0xffffff ) ? "?? unknown ??" : script->get_str(j)); i += 3; break; case C_STR: - j = (int)strlen((char*)script->buf + i); - ShowMessage(" %s", script->buf + i); + j = (int)strlen((char*)&VECTOR_INDEX(script->buf, i)); + ShowMessage(" %s", &VECTOR_INDEX(script->buf, i)); i += j+1; break; } @@ -2729,9 +2759,9 @@ struct script_code* parse_script(const char *src,const char *file,int line,int o #endif CREATE(code,struct script_code,1); - code->script_buf = (unsigned char *)aMalloc(script->pos*sizeof(unsigned char)); - memcpy(code->script_buf, script->buf, script->pos); - code->script_size = script->pos; + VECTOR_INIT(code->script_buf); + VECTOR_ENSURE(code->script_buf, VECTOR_LENGTH(script->buf), 1); + VECTOR_PUSHARRAY(code->script_buf, VECTOR_DATA(script->buf), VECTOR_LENGTH(script->buf)); code->local.vars = NULL; code->local.arrays = NULL; #ifdef ENABLE_CASE_CHECK @@ -2745,9 +2775,10 @@ struct script_code* parse_script(const char *src,const char *file,int line,int o /// Returns the player attached to this script, identified by the rid. /// If there is no player attached, the script is terminated. -struct map_session_data *script_rid2sd(struct script_state *st) +static struct map_session_data *script_rid2sd(struct script_state *st) { struct map_session_data *sd; + nullpo_retr(NULL, st); if( !( sd = map->id2sd(st->rid) ) ) { ShowError("script_rid2sd: fatal error ! player not attached!\n"); script->reportfunc(st); @@ -2757,7 +2788,7 @@ struct map_session_data *script_rid2sd(struct script_state *st) return sd; } -struct map_session_data *script_id2sd(struct script_state *st, int account_id) +static struct map_session_data *script_id2sd(struct script_state *st, int account_id) { struct map_session_data *sd; if ((sd = map->id2sd(account_id)) == NULL) { @@ -2768,7 +2799,7 @@ struct map_session_data *script_id2sd(struct script_state *st, int account_id) return sd; } -struct map_session_data *script_charid2sd(struct script_state *st, int char_id) +static struct map_session_data *script_charid2sd(struct script_state *st, int char_id) { struct map_session_data *sd; if ((sd = map->charid2sd(char_id)) == NULL) { @@ -2779,10 +2810,10 @@ struct map_session_data *script_charid2sd(struct script_state *st, int char_id) return sd; } -struct map_session_data *script_nick2sd(struct script_state *st, const char *name) +static struct map_session_data *script_nick2sd(struct script_state *st, const char *name) { struct map_session_data *sd; - if ((sd = map->nick2sd(name)) == NULL) { + if ((sd = map->nick2sd(name, false)) == NULL) { ShowWarning("script_nick2sd: Player name '%s' not found!\n", name); script->reportfunc(st); script->reportsrc(st); @@ -2790,14 +2821,26 @@ struct map_session_data *script_nick2sd(struct script_state *st, const char *nam return sd; } -char *get_val_npcscope_str(struct script_state* st, struct reg_db *n, struct script_data* data) { +static char *get_val_npcscope_str(struct script_state *st, struct reg_db *n, struct script_data *data) +{ if (n) return (char*)i64db_get(n->vars, reference_getuid(data)); else return NULL; } -char *get_val_instance_str(struct script_state* st, const char* name, struct script_data* data) { +static char *get_val_pc_ref_str(struct script_state *st, struct reg_db *n, struct script_data *data) +{ + struct script_reg_str *p = NULL; + nullpo_retr(NULL, n); + + p = i64db_get(n->vars, reference_getuid(data)); + return p ? p->value : NULL; +} + +static char *get_val_instance_str(struct script_state *st, const char *name, struct script_data *data) +{ + nullpo_retr(NULL, st); if (st->instance_id >= 0) { return (char*)i64db_get(instance->list[st->instance_id].regs.vars, reference_getuid(data)); } else { @@ -2806,14 +2849,25 @@ char *get_val_instance_str(struct script_state* st, const char* name, struct scr } } -int get_val_npcscope_num(struct script_state* st, struct reg_db *n, struct script_data* data) { +static int get_val_npcscope_num(struct script_state *st, struct reg_db *n, struct script_data *data) +{ if (n) return (int)i64db_iget(n->vars, reference_getuid(data)); else return 0; } -int get_val_instance_num(struct script_state* st, const char* name, struct script_data* data) { +static int get_val_pc_ref_num(struct script_state *st, struct reg_db *n, struct script_data *data) +{ + struct script_reg_num *p = NULL; + nullpo_retr(0, n); + + p = i64db_get(n->vars, reference_getuid(data)); + return p ? p->value : 0; +} + +static int get_val_instance_num(struct script_state *st, const char *name, struct script_data *data) +{ if (st->instance_id >= 0) return (int)i64db_iget(instance->list[st->instance_id].regs.vars, reference_getuid(data)); else { @@ -2829,13 +2883,14 @@ int get_val_instance_num(struct script_state* st, const char* name, struct scrip * @param data[in,out] variable/constant. * @return pointer to data, for convenience. */ -struct script_data *get_val(struct script_state* st, struct script_data* data) { +static struct script_data *get_val(struct script_state *st, struct script_data *data) +{ const char* name; char prefix; char postfix; struct map_session_data *sd = NULL; - if( !data_isreference(data) ) + if (!data_isreference(data)) return data;// not a variable/constant name = reference_getname(data); @@ -2849,11 +2904,10 @@ struct script_data *get_val(struct script_state* st, struct script_data* data) { return data; } - //##TODO use reference_tovariable(data) when it's confirmed that it works [FlavioJS] - if( !reference_toconstant(data) && not_server_variable(prefix) ) { + 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 + if (sd == NULL) {// needs player attached + if (postfix == '$') {// string variable ShowWarning("script_get_val: cannot access player variable '%s', defaulting to \"\"\n", name); data->type = C_CONSTSTR; data->u.str = ""; @@ -2871,32 +2925,44 @@ struct script_data *get_val(struct script_state* st, struct script_data* data) { const char *str = NULL; switch (prefix) { - case '@': + case '@': + if (data->ref) { + str = script->get_val_ref_str(st, data->ref, data); + } else { str = pc->readregstr(sd, data->u.num); - break; - case '$': - str = mapreg->readregstr(data->u.num); - break; - case '#': - if (name[1] == '#') - str = pc_readaccountreg2str(sd, data->u.num);// global - else - str = pc_readaccountregstr(sd, data->u.num);// local - break; - case '.': - if (data->ref) - str = script->get_val_ref_str(st, data->ref, data); - else if (name[1] == '@') - str = script->get_val_scope_str(st, &st->stack->scope, data); - else - str = script->get_val_npc_str(st, &st->script->local, data); - break; - case '\'': - str = script->get_val_instance_str(st, name, data); - break; - default: + } + break; + case '$': + str = mapreg->readregstr(data->u.num); + break; + case '#': + if (data->ref) { + str = script->get_val_pc_ref_str(st, data->ref, data); + } else if (name[1] == '#') { + str = pc_readaccountreg2str(sd, data->u.num);// global + } else { + str = pc_readaccountregstr(sd, data->u.num);// local + } + break; + case '.': + if (data->ref) { + str = script->get_val_ref_str(st, data->ref, data); + } else if (name[1] == '@') { + str = script->get_val_scope_str(st, &st->stack->scope, data); + } else { + str = script->get_val_npc_str(st, &st->script->local, data); + } + break; + case '\'': + str = script->get_val_instance_str(st, name, data); + break; + default: + if (data->ref) { + str = script->get_val_pc_ref_str(st, data->ref, data); + } else { str = pc_readglobalreg_str(sd, data->u.num); - break; + } + break; } if (str == NULL || str[0] == '\0') { @@ -2916,36 +2982,48 @@ struct script_data *get_val(struct script_state* st, struct script_data* data) { data->u.num = reference_getconstant(data); } else if( reference_toparam(data) ) { data->u.num = pc->readparam(sd, reference_getparamtype(data)); - } else - switch( prefix ) { - case '@': + } else { + switch (prefix) { + case '@': + if (data->ref) { + data->u.num = script->get_val_ref_num(st, data->ref, data); + } else { data->u.num = pc->readreg(sd, data->u.num); - break; - case '$': - data->u.num = mapreg->readreg(data->u.num); - break; - case '#': - if( name[1] == '#' ) - data->u.num = pc_readaccountreg2(sd, data->u.num);// global - else - data->u.num = pc_readaccountreg(sd, data->u.num);// local - break; - case '.': - if (data->ref) - data->u.num = script->get_val_ref_num(st, data->ref, data); - else if (name[1] == '@') - data->u.num = script->get_val_scope_num(st, &st->stack->scope, data); - else - data->u.num = script->get_val_npc_num(st, &st->script->local, data); - break; - case '\'': - data->u.num = script->get_val_instance_num(st, name, data); - break; - default: + } + break; + case '$': + data->u.num = mapreg->readreg(data->u.num); + break; + case '#': + if (data->ref) { + data->u.num = script->get_val_pc_ref_num(st, data->ref, data); + } else if (name[1] == '#') { + data->u.num = pc_readaccountreg2(sd, data->u.num);// global + } else { + data->u.num = pc_readaccountreg(sd, data->u.num);// local + } + break; + case '.': + if (data->ref) { + data->u.num = script->get_val_ref_num(st, data->ref, data); + } else if (name[1] == '@') { + data->u.num = script->get_val_scope_num(st, &st->stack->scope, data); + } else { + data->u.num = script->get_val_npc_num(st, &st->script->local, data); + } + break; + case '\'': + data->u.num = script->get_val_instance_num(st, name, data); + break; + default: + if (data->ref) { + data->u.num = script->get_val_pc_ref_num(st, data->ref, data); + } else { data->u.num = pc_readglobalreg(sd, data->u.num); - break; + } + break; } - + } } data->ref = NULL; @@ -2962,14 +3040,19 @@ struct script_data *get_val(struct script_state* st, struct script_data* data) { * @param ref[in] the container to look up the reference into. * @return the retrieved value of the reference. */ -const void *get_val2(struct script_state *st, int64 uid, struct reg_db *ref) +static const void *get_val2(struct script_state *st, int64 uid, struct reg_db *ref) { struct script_data* data; + nullpo_retr(NULL, st); script->push_val(st->stack, C_NAME, uid, ref); data = script_getdatatop(st, -1); 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); } @@ -2977,16 +3060,21 @@ const void *get_val2(struct script_state *st, int64 uid, struct reg_db *ref) * Because, currently, array members with key 0 are indifferenciable from normal variables, we should ensure its actually in * Will be gone as soon as undefined var feature is implemented **/ -void script_array_ensure_zero(struct script_state *st, struct map_session_data *sd, int64 uid, struct reg_db *ref) { +static void script_array_ensure_zero(struct script_state *st, struct map_session_data *sd, int64 uid, struct reg_db *ref) +{ const char *name = script->get_str(script_getvarid(uid)); - // is here st can be null pointer and st->rid is wrong? - struct reg_db *src = script->array_src(st, sd ? sd : st->rid ? map->id2sd(st->rid) : NULL, name, ref); + struct reg_db *src = NULL; bool insert = false; - if (sd && !st) { - /* when sd comes, st isn't available */ + if (st == NULL) { + // Special case with no st available, only sd + nullpo_retv(sd); + src = script->array_src(NULL, sd, name, ref); insert = true; } else { + if (sd == NULL && st->rid != 0) + sd = map->id2sd(st->rid); // Retrieve the missing sd + src = script->array_src(st, sd, name, ref); if( is_string_variable(name) ) { const char *str = script->get_val2(st, uid, ref); if (str != NULL && *str != '\0') @@ -3020,7 +3108,8 @@ void script_array_ensure_zero(struct script_state *st, struct map_session_data * /** * Returns array size by ID **/ -unsigned int script_array_size(struct script_state *st, struct map_session_data *sd, const char *name, struct reg_db *ref) { +static unsigned int script_array_size(struct script_state *st, struct map_session_data *sd, const char *name, struct reg_db *ref) +{ struct script_array *sa = NULL; struct reg_db *src = script->array_src(st, sd, name, ref); @@ -3032,7 +3121,8 @@ unsigned int script_array_size(struct script_state *st, struct map_session_data /** * Returns array's highest key (for that awful getarraysize implementation that doesn't really gets the array size) **/ -unsigned int script_array_highest_key(struct script_state *st, struct map_session_data *sd, const char *name, struct reg_db *ref) { +static unsigned int script_array_highest_key(struct script_state *st, struct map_session_data *sd, const char *name, struct reg_db *ref) +{ struct script_array *sa = NULL; struct reg_db *src = script->array_src(st, sd, name, ref); @@ -3053,7 +3143,7 @@ unsigned int script_array_highest_key(struct script_state *st, struct map_sessio } return 0; } -int script_free_array_db(union DBKey key, struct DBData *data, va_list ap) +static int script_free_array_db(union DBKey key, struct DBData *data, va_list ap) { struct script_array *sa = DB->data2ptr(data); aFree(sa->members); @@ -3063,7 +3153,10 @@ int script_free_array_db(union DBKey key, struct DBData *data, va_list ap) /** * Clears script_array and removes it from script->array_db **/ -void script_array_delete(struct reg_db *src, struct script_array *sa) { +static void script_array_delete(struct reg_db *src, struct script_array *sa) +{ + nullpo_retv(src); + nullpo_retv(sa); aFree(sa->members); idb_remove(src->arrays, sa->id); ers_free(script->array_ers, sa); @@ -3073,9 +3166,11 @@ void script_array_delete(struct reg_db *src, struct script_array *sa) { * * @param idx the index of the member in script_array struct list, not of the actual array member **/ -void script_array_remove_member(struct reg_db *src, struct script_array *sa, unsigned int idx) { +static void script_array_remove_member(struct reg_db *src, struct script_array *sa, unsigned int idx) +{ unsigned int i, cursor; + nullpo_retv(sa); /* its the only member left, no need to do anything other than delete the array data */ if( sa->size == 1 ) { script->array_delete(src,sa); @@ -3099,44 +3194,56 @@ void script_array_remove_member(struct reg_db *src, struct script_array *sa, uns * * @param idx the index of the array member being inserted **/ -void script_array_add_member(struct script_array *sa, unsigned int idx) { +static void script_array_add_member(struct script_array *sa, unsigned int idx) +{ + nullpo_retv(sa); RECREATE(sa->members, unsigned int, ++sa->size); - sa->members[sa->size - 1] = idx; } /** * Obtains the source of the array database for this type and scenario * Initializes such database when not yet initialized. **/ -struct reg_db *script_array_src(struct script_state *st, struct map_session_data *sd, const char *name, struct reg_db *ref) { +static struct reg_db *script_array_src(struct script_state *st, struct map_session_data *sd, const char *name, struct reg_db *ref) +{ struct reg_db *src = NULL; + nullpo_retr(NULL, name); - switch( name[0] ) { + switch (name[0]) { /* from player */ - default: /* char reg */ - case '@':/* temp char reg */ - case '#':/* account reg */ + default: /* char reg */ + case '@':/* temp char reg */ + case '#':/* account reg */ + if (ref != NULL) { + src = ref; + } else { + nullpo_retr(NULL, sd); src = &sd->regs; - break; - case '$':/* map reg */ - src = &mapreg->regs; - break; - case '.':/* npc/script */ - if( ref ) - src = ref; - else - src = (name[1] == '@') ? &st->stack->scope : &st->script->local; - break; - case '\'':/* instance */ - if( st->instance_id >= 0 ) { - src = &instance->list[st->instance_id].regs; - } - break; + } + break; + case '$':/* map reg */ + src = &mapreg->regs; + break; + case '.':/* npc/script */ + if (ref != NULL) { + src = ref; + } else { + nullpo_retr(NULL, st); + src = (name[1] == '@') ? &st->stack->scope : &st->script->local; + } + break; + case '\'':/* instance */ + nullpo_retr(NULL, st); + if (st->instance_id >= 0) { + src = &instance->list[st->instance_id].regs; + } + break; } - if( src ) { - if( !src->arrays ) + if (src) { + if (!src->arrays) { src->arrays = idb_alloc(DB_OPT_BASE); + } return src; } return NULL; @@ -3149,11 +3256,13 @@ struct reg_db *script_array_src(struct script_state *st, struct map_session_data * @param num[in] Variable ID * @param empty[in] Whether the modified member is empty (needs to be removed) **/ -void script_array_update(struct reg_db *src, int64 num, bool empty) { +static void script_array_update(struct reg_db *src, int64 num, bool empty) +{ struct script_array *sa = NULL; int id = script_getvarid(num); unsigned int index = script_getvaridx(num); + nullpo_retv(src); if (!src->arrays) { src->arrays = idb_alloc(DB_OPT_BASE); } else { @@ -3189,10 +3298,11 @@ void script_array_update(struct reg_db *src, int64 num, bool empty) { } } -void set_reg_npcscope_str(struct script_state* st, struct reg_db *n, int64 num, const char* name, const char *str) +static void set_reg_npcscope_str(struct script_state *st, struct reg_db *n, int64 num, const char *name, const char *str) { if (n) { + nullpo_retv(str); if (str[0]) { i64db_put(n->vars, num, aStrdup(str)); if (script_getvaridx(num)) @@ -3205,7 +3315,33 @@ void set_reg_npcscope_str(struct script_state* st, struct reg_db *n, int64 num, } } -void set_reg_npcscope_num(struct script_state* st, struct reg_db *n, int64 num, const char* name, int val) +static void set_reg_pc_ref_str(struct script_state *st, struct reg_db *n, int64 num, const char *name, const char *str) +{ + struct DBIterator *iter = db_iterator(map->pc_db); + + 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 DBIterator *iter = db_iterator(map->pc_db); + + 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) { if (n) { if (val != 0) { @@ -3220,8 +3356,9 @@ void set_reg_npcscope_num(struct script_state* st, struct reg_db *n, int64 num, } } -void set_reg_instance_str(struct script_state* st, int64 num, const char* name, const char *str) +static void set_reg_instance_str(struct script_state *st, int64 num, const char *name, const char *str) { + nullpo_retv(st); if (st->instance_id >= 0) { if (str[0]) { i64db_put(instance->list[st->instance_id].regs.vars, num, aStrdup(str)); @@ -3238,8 +3375,9 @@ void set_reg_instance_str(struct script_state* st, int64 num, const char* name, } } -void set_reg_instance_num(struct script_state* st, int64 num, const char* name, int val) +static void set_reg_instance_num(struct script_state *st, int64 num, const char *name, int val) { + nullpo_retv(st); if (st->instance_id >= 0) { if (val != 0) { i64db_iput(instance->list[st->instance_id].regs.vars, num, val); @@ -3270,59 +3408,93 @@ void set_reg_instance_num(struct script_state* st, int64 num, const char* name, * * TODO: return values are screwed up, have been for some time (reaad: years), e.g. some functions return 1 failure and success. *------------------------------------------*/ -int set_reg(struct script_state *st, struct map_session_data *sd, int64 num, const char *name, const void *value, struct reg_db *ref) +static int set_reg(struct script_state *st, struct map_session_data *sd, int64 num, const char *name, const void *value, struct reg_db *ref) { - char prefix = name[0]; + char prefix; + 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); - script->reportsrc(st); - st->state = END; + if (st) { + script->reportsrc(st); + st->state = END; + } return 0; } - if( is_string_variable(name) ) {// string variable + if (is_string_variable(name)) {// string variable const char *str = (const char*)value; switch (prefix) { - case '@': + case '@': + if (ref) { + script->set_reg_ref_str(st, ref, num, name, str); + } else { pc->setregstr(sd, num, str); - return 1; - case '$': - return mapreg->setregstr(num, str); - case '#': - return (name[1] == '#') ? - pc_setaccountreg2str(sd, num, str) : - pc_setaccountregstr(sd, num, str); - case '.': - if (ref) - script->set_reg_ref_str(st, ref, num, name, str); - else if (name[1] == '@') - script->set_reg_scope_str(st, &st->stack->scope, num, name, str); - else - script->set_reg_npc_str(st, &st->script->local, num, name, str); - return 1; - case '\'': - set_reg_instance_str(st, num, name, str); - return 1; - default: - return pc_setglobalreg_str(sd, num, str); + } + return 1; + case '$': + mapreg->setregstr(num, str); + return 1; + case '#': + if (ref) { + script->set_reg_pc_ref_str(st, ref, num, name, str); + } else if (name[1] == '#') { + pc_setaccountreg2str(sd, num, str); + } else { + pc_setaccountregstr(sd, num, str); + } + return 1; + case '.': + if (ref) { + script->set_reg_ref_str(st, ref, num, name, str); + } else if (name[1] == '@') { + script->set_reg_scope_str(st, &st->stack->scope, num, name, str); + } else { + script->set_reg_npc_str(st, &st->script->local, num, name, str); + } + return 1; + case '\'': + set_reg_instance_str(st, num, name, str); + return 1; + default: + if (ref) { + script->set_reg_pc_ref_str(st, ref, num, name, str); + } else { + pc_setglobalreg_str(sd, num, str); + } + return 1; } } else {// integer variable // FIXME: This isn't safe, in 32bits systems we're converting a 64bit pointer // to a 32bit int, this will lead to overflows! [Panikon] int val = (int)h64BPTRSIZE(value); - if(script->str_data[script_getvarid(num)].type == C_PARAM) { - if( pc->setparam(sd, script->str_data[script_getvarid(num)].val, val) == 0 ) { - if( st != NULL ) { + if (script->str_data[script_getvarid(num)].type == C_PARAM) { + if (pc->setparam(sd, script->str_data[script_getvarid(num)].val, val) == 0) { + if (st != NULL) { ShowError("script:set_reg: failed to set param '%s' to %d.\n", name, val); script->reportsrc(st); // Instead of just stop the script execution we let the character close // the window if it was open. st->state = (sd->state.dialog) ? CLOSE : END; - if( st->state == CLOSE ) + if(st->state == CLOSE) { clif->scriptclose(sd, st->oid); + } } return 0; } @@ -3330,44 +3502,74 @@ int set_reg(struct script_state *st, struct map_session_data *sd, int64 num, con } switch (prefix) { - case '@': + case '@': + if (ref) { + script->set_reg_ref_num(st, ref, num, name, val); + } else { pc->setreg(sd, num, val); - return 1; - case '$': - return mapreg->setreg(num, val); - case '#': - return (name[1] == '#') ? - pc_setaccountreg2(sd, num, val) : - pc_setaccountreg(sd, num, val); - case '.': - if (ref) - script->set_reg_ref_num(st, ref, num, name, val); - else if (name[1] == '@') - script->set_reg_scope_num(st, &st->stack->scope, num, name, val); - else - script->set_reg_npc_num(st, &st->script->local, num, name, val); - return 1; - case '\'': - set_reg_instance_num(st, num, name, val); - return 1; - default: - return pc_setglobalreg(sd, num, val); + } + return 1; + case '$': + mapreg->setreg(num, val); + return 1; + case '#': + if (ref) { + script->set_reg_pc_ref_num(st, ref, num, name, val); + } else if (name[1] == '#') { + pc_setaccountreg2(sd, num, val); + } else { + pc_setaccountreg(sd, num, val); + } + return 1; + case '.': + if (ref) { + script->set_reg_ref_num(st, ref, num, name, val); + } else if (name[1] == '@') { + script->set_reg_scope_num(st, &st->stack->scope, num, name, val); + } else { + script->set_reg_npc_num(st, &st->script->local, num, name, val); + } + return 1; + case '\'': + set_reg_instance_num(st, num, name, val); + return 1; + default: + if (ref) { + script->set_reg_pc_ref_num(st, ref, num, name, val); + } else { + pc_setglobalreg(sd, num, val); + } + return 1; } } } -int set_var(struct map_session_data *sd, char *name, void *val) +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); } -void setd_sub(struct script_state *st, struct map_session_data *sd, const char *varname, int elem, const void *value, struct reg_db *ref) +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 -const char *conv_str(struct script_state *st, struct script_data* data) +static const char *conv_str(struct script_state *st, struct script_data *data) { script->get_val(st, data); if (data_isstring(data)) { @@ -3401,7 +3603,7 @@ const char *conv_str(struct script_state *st, struct script_data* data) } /// Converts the data to an int -int conv_num(struct script_state *st, struct script_data *data) +static int conv_num(struct script_state *st, struct script_data *data) { long num; @@ -3459,7 +3661,9 @@ int conv_num(struct script_state *st, struct script_data *data) // /// Increases the size of the stack -void stack_expand(struct script_stack* stack) { +static void stack_expand(struct script_stack *stack) +{ + nullpo_retv(stack); stack->sp_max += 64; stack->stack_data = (struct script_data*)aRealloc(stack->stack_data, stack->sp_max * sizeof(stack->stack_data[0]) ); @@ -3468,7 +3672,9 @@ void stack_expand(struct script_stack* stack) { } /// Pushes a value into the stack (with reference) -struct script_data* push_val(struct script_stack* stack, enum c_op type, int64 val, struct reg_db *ref) { +static struct script_data *push_val(struct script_stack *stack, enum c_op type, int64 val, struct reg_db *ref) +{ + nullpo_retr(NULL, stack); if( stack->sp >= stack->sp_max ) script->stack_expand(stack); stack->stack_data[stack->sp].type = type; @@ -3479,8 +3685,9 @@ struct script_data* push_val(struct script_stack* stack, enum c_op type, int64 v } /// Pushes a string into the stack -struct script_data *push_str(struct script_stack *stack, char *str) +static struct script_data *push_str(struct script_stack *stack, char *str) { + nullpo_retr(NULL, stack); if( stack->sp >= stack->sp_max ) script->stack_expand(stack); stack->stack_data[stack->sp].type = C_STR; @@ -3491,8 +3698,9 @@ struct script_data *push_str(struct script_stack *stack, char *str) } /// Pushes a constant string into the stack -struct script_data *push_conststr(struct script_stack *stack, const char *str) +static struct script_data *push_conststr(struct script_stack *stack, const char *str) { + nullpo_retr(NULL, stack); if( stack->sp >= stack->sp_max ) script->stack_expand(stack); stack->stack_data[stack->sp].type = C_CONSTSTR; @@ -3503,7 +3711,9 @@ struct script_data *push_conststr(struct script_stack *stack, const char *str) } /// Pushes a retinfo into the stack -struct script_data* push_retinfo(struct script_stack* stack, struct script_retinfo* ri, struct reg_db *ref) { +static struct script_data *push_retinfo(struct script_stack *stack, struct script_retinfo *ri, struct reg_db *ref) +{ + nullpo_retr(NULL, stack); if( stack->sp >= stack->sp_max ) script->stack_expand(stack); stack->stack_data[stack->sp].type = C_RETINFO; @@ -3514,7 +3724,9 @@ struct script_data* push_retinfo(struct script_stack* stack, struct script_retin } /// Pushes a copy of the target position into the stack -struct script_data* push_copy(struct script_stack* stack, int pos) { +static struct script_data *push_copy(struct script_stack *stack, int pos) +{ + nullpo_retr(NULL, stack); switch( stack->stack_data[pos].type ) { case C_CONSTSTR: return script->push_conststr(stack, stack->stack_data[pos].u.str); @@ -3538,11 +3750,15 @@ struct script_data* push_copy(struct script_stack* stack, int pos) { /// Removes the values in indexes [start,end[ from the stack. /// Adjusts all stack pointers. -void pop_stack(struct script_state* st, int start, int end) { - struct script_stack* stack = st->stack; +static void pop_stack(struct script_state *st, int start, int end) +{ + struct script_stack* stack; struct script_data* data; int i; + nullpo_retv(st); + stack = st->stack; + if( start < 0 ) start = 0; if( end > stack->sp ) @@ -3607,7 +3823,7 @@ void pop_stack(struct script_state* st, int start, int end) { /*========================================== * Release script dependent variable, dependent variable of function *------------------------------------------*/ -void script_free_vars(struct DBMap *var_storage) +static void script_free_vars(struct DBMap *var_storage) { if( var_storage ) { // destroy the storage construct containing the variables @@ -3615,7 +3831,7 @@ void script_free_vars(struct DBMap *var_storage) } } -void script_free_code(struct script_code* code) +static void script_free_code(struct script_code *code) { nullpo_retv(code); @@ -3624,7 +3840,7 @@ void script_free_code(struct script_code* code) script->free_vars(code->local.vars); if (code->local.arrays) code->local.arrays->destroy(code->local.arrays,script->array_free_db); - aFree(code->script_buf); + VECTOR_CLEAR(code->script_buf); aFree(code); } @@ -3635,7 +3851,8 @@ void script_free_code(struct script_code* code) /// @param rid Who is running the script (attached player) /// @param oid Where the code is being run (npc 'object') /// @return Script state -struct script_state* script_alloc_state(struct script_code* rootscript, int pos, int rid, int oid) { +static struct script_state *script_alloc_state(struct script_code *rootscript, int pos, int rid, int oid) +{ struct script_state* st; st = ers_alloc(script->st_ers, struct script_state); @@ -3677,7 +3894,9 @@ struct script_state* script_alloc_state(struct script_code* rootscript, int pos, /// Frees a script state. /// /// @param st Script state -void script_free_state(struct script_state* st) { +static void script_free_state(struct script_state *st) +{ + nullpo_retv(st); if( idb_exists(script->st_db,st->id) ) { struct map_session_data *sd = st->rid ? map->id2sd(st->rid) : NULL; @@ -3738,7 +3957,9 @@ void script_free_state(struct script_state* st) { * @param st[in] Script state. * @param ref[in] Reference to be added. */ -void script_add_pending_ref(struct script_state *st, struct reg_db *ref) { +static void script_add_pending_ref(struct script_state *st, struct reg_db *ref) +{ + nullpo_retv(st); RECREATE(st->pending_refs, struct reg_db*, ++st->pending_ref_count); st->pending_refs[st->pending_ref_count-1] = ref; } @@ -3749,37 +3970,37 @@ void script_add_pending_ref(struct script_state *st, struct reg_db *ref) { /*========================================== * Read command *------------------------------------------*/ -c_op get_com(unsigned char *scriptbuf,int *pos) +static c_op get_com(const struct script_buf *scriptbuf, int *pos) { int i = 0, j = 0; - if(scriptbuf[*pos]>=0x80) { + if (VECTOR_INDEX(*scriptbuf, *pos) >= 0x80) { return C_INT; } - while(scriptbuf[*pos]>=0x40) { - i=scriptbuf[(*pos)++]<<j; + while (VECTOR_INDEX(*scriptbuf, *pos) >= 0x40) { + i = VECTOR_INDEX(*scriptbuf, (*pos)++) << j; j+=6; } - return (c_op)(i+(scriptbuf[(*pos)++]<<j)); + return (c_op)(i+(VECTOR_INDEX(*scriptbuf, (*pos)++)<<j)); } /*========================================== * Income figures *------------------------------------------*/ -int get_num(unsigned char *scriptbuf,int *pos) +static int get_num(const struct script_buf *scriptbuf, int *pos) { int i,j; i=0; j=0; - while(scriptbuf[*pos]>=0xc0) { - i+=(scriptbuf[(*pos)++]&0x7f)<<j; + while (VECTOR_INDEX(*scriptbuf, *pos) >= 0xc0) { + i+= (VECTOR_INDEX(*scriptbuf, (*pos)++)&0x7f)<<j; j+=6; } - return i+((scriptbuf[(*pos)++]&0x7f)<<j); + return i+((VECTOR_INDEX(*scriptbuf, (*pos)++)&0x7f)<<j); } /// Ternary operators /// test ? if_true : if_false -void op_3(struct script_state* st, int op) +static void op_3(struct script_state *st, int op) { struct script_data* data; int flag = 0; @@ -3816,7 +4037,7 @@ void op_3(struct script_state* st, int op) /// s1 RE_EQ s2 -> i /// s1 RE_NE s2 -> i /// s1 ADD s2 -> s -void op_2str(struct script_state* st, int op, const char* s1, const char* s2) +static void op_2str(struct script_state *st, int op, const char *s1, const char *s2) { int a = 0; @@ -3834,7 +4055,7 @@ void op_2str(struct script_state* st, int op, const char* s1, const char* s2) pcre *compiled_regex; pcre_extra *extra_regex; const char *pcre_error, *pcre_match; - int pcre_erroroffset, offsetcount, i; + int pcre_erroroffset, offsetcount; int offsets[256*3]; // (max_capturing_groups+1)*3 compiled_regex = libpcre->compile(s2, 0, &pcre_error, &pcre_erroroffset, NULL); @@ -3875,13 +4096,14 @@ void op_2str(struct script_state* st, int op, const char* s1, const char* s2) return; } - if( op == C_RE_EQ ) { - for( i = 0; i < offsetcount; i++ ) { + if (op == C_RE_EQ) { + 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); @@ -3912,7 +4134,7 @@ void op_2str(struct script_state* st, int op, const char* s1, const char* s2) /// Binary number operators /// i OP i -> i -void op_2num(struct script_state* st, int op, int i1, int i2) +static void op_2num(struct script_state *st, int op, int i1, int i2) { int ret; int64 ret64; @@ -3951,6 +4173,7 @@ void op_2num(struct script_state* st, int op, int i1, int i2) case C_ADD: ret = i1 + i2; ret64 = (int64)i1 + i2; break; case C_SUB: ret = i1 - i2; ret64 = (int64)i1 - i2; break; case C_MUL: ret = i1 * i2; ret64 = (int64)i1 * i2; break; + case C_POW: ret = (int)pow((double)i1, (double)i2); ret64 = (int64)pow((double)i1, (double)i2); break; default: ShowError("script:op_2num: unexpected number operator %s i1=%d i2=%d\n", script->op2name(op), i1, i2); script->reportsrc(st); @@ -3971,7 +4194,7 @@ void op_2num(struct script_state* st, int op, int i1, int i2) } /// Binary operators -void op_2(struct script_state *st, int op) +static void op_2(struct script_state *st, int op) { struct script_data* left, leftref; struct script_data* right; @@ -4045,7 +4268,7 @@ void op_2(struct script_state *st, int op) /// NEG i -> i /// NOT i -> i /// LNOT i -> i -void op_1(struct script_state* st, int op) +static void op_1(struct script_state *st, int op) { struct script_data* data; int i1; @@ -4083,10 +4306,17 @@ void op_1(struct script_state* st, int op) /// /// @param st Script state whose stack arguments should be inspected. /// @param func Built-in function for which the arguments are intended. -void script_check_buildin_argtype(struct script_state* st, int func) +static bool script_check_buildin_argtype(struct script_state *st, int func) { int idx, invalid = 0; - char* sf = script->buildin[script->str_data[func].val]; + char* sf; + if (script->str_data[func].val < 0 || script->str_data[func].val >= script->buildin_count) { + ShowDebug("Function: %s\n", script->get_str(func)); + ShowError("Script data corruption detected!\n"); + script->reportsrc(st); + return false; + } + sf = script->buildin[script->str_data[func].val]; for (idx = 2; script_hasdata(st, idx); idx++) { struct script_data* data = script_getdata(st, idx); @@ -4157,15 +4387,17 @@ void script_check_buildin_argtype(struct script_state* st, int func) ShowDebug("Function: %s\n", script->get_str(func)); script->reportsrc(st); } + return true; } /// Executes a buildin command. /// Stack: C_NAME(<command>) C_ARG <arg0> <arg1> ... <argN> -int run_func(struct script_state *st) +static int run_func(struct script_state *st) { struct script_data* data; int i,start_sp,end_sp,func; + nullpo_retr(1, st); end_sp = st->stack->sp;// position after the last argument for( i = end_sp-1; i > 0 ; --i ) if( st->stack->stack_data[i].type == C_ARG ) @@ -4194,7 +4426,11 @@ int run_func(struct script_state *st) } if( script->config.warn_func_mismatch_argtypes ) { - script->check_buildin_argtype(st, func); + if (script->check_buildin_argtype(st, func) == false) + { + st->state = END; + return 1; + } } if(script->str_data[func].func) { @@ -4249,7 +4485,8 @@ int run_func(struct script_state *st) /*========================================== * script execution *------------------------------------------*/ -void run_script(struct script_code *rootscript, int pos, int rid, int oid) { +static void run_script(struct script_code *rootscript, int pos, int rid, int oid) +{ struct script_state *st; if( rootscript == NULL || pos < 0 ) @@ -4263,7 +4500,7 @@ void run_script(struct script_code *rootscript, int pos, int rid, int oid) { script->run_main(st); } -void script_stop_instances(struct script_code *code) +static void script_stop_instances(struct script_code *code) { struct DBIterator *iter; struct script_state* st; @@ -4285,7 +4522,8 @@ void script_stop_instances(struct script_code *code) /*========================================== * Timer function for sleep *------------------------------------------*/ -int run_script_timer(int tid, int64 tick, int id, intptr_t data) { +static int run_script_timer(int tid, int64 tick, int id, intptr_t data) +{ struct script_state *st = idb_get(script->st_db,(int)data); if( st ) { struct map_session_data *sd = map->id2sd(st->rid); @@ -4306,9 +4544,11 @@ int run_script_timer(int tid, int64 tick, int id, intptr_t data) { /// /// @param st Script state to detach. /// @param dequeue_event Whether to schedule any queued events, when there was no previous script. -void script_detach_state(struct script_state* st, bool dequeue_event) { +static void script_detach_state(struct script_state *st, bool dequeue_event) +{ struct map_session_data* sd; + nullpo_retv(st); if(st->rid && (sd = map->id2sd(st->rid))!=NULL) { sd->st = st->bk_st; sd->npc_id = st->bk_npcid; @@ -4339,9 +4579,11 @@ void script_detach_state(struct script_state* st, bool dequeue_event) { /// Attaches script state to possibly attached character and backups it's previous script, if any. /// /// @param st Script state to attach. -void script_attach_state(struct script_state* st) { +static void script_attach_state(struct script_state *st) +{ struct map_session_data* sd; + nullpo_retv(st); if(st->rid && (sd = map->id2sd(st->rid))!=NULL) { if(st!=sd->st) @@ -4370,14 +4612,19 @@ void script_attach_state(struct script_state* st) { /*========================================== * The main part of the script execution *------------------------------------------*/ -void run_script_main(struct script_state *st) { +static void run_script_main(struct script_state *st) +{ int cmdcount = script->config.check_cmdcount; int gotocount = script->config.check_gotocount; struct map_session_data *sd; struct script_stack *stack = st->stack; struct npc_data *nd; + nullpo_retv(st); script->attach_state(st); + if (st->state != END && Assert_chk(st->state == RUN || st->state == STOP || st->state == RERUNLINE)) { + st->state = END; + } nd = map->id2nd(st->oid); if( nd && nd->bl.m >= 0 ) @@ -4393,7 +4640,7 @@ void run_script_main(struct script_state *st) { st->state = RUN; while( st->state == RUN ) { - enum c_op c = script->get_com(st->script->script_buf,&st->pos); + enum c_op c = script->get_com(&st->script->script_buf, &st->pos); switch(c) { case C_EOL: if( stack->defsp > stack->sp ) @@ -4402,27 +4649,29 @@ void run_script_main(struct script_state *st) { script->pop_stack(st, stack->defsp, stack->sp);// pop unused stack data. (unused return value) break; case C_INT: - script->push_val(stack,C_INT,script->get_num(st->script->script_buf,&st->pos),NULL); + script->push_val(stack,C_INT,script->get_num(&st->script->script_buf, &st->pos), NULL); break; case C_POS: case C_NAME: - script->push_val(stack,c,GETVALUE(st->script->script_buf,st->pos),NULL); + script->push_val(stack,c,GETVALUE(&st->script->script_buf, st->pos), NULL); st->pos+=3; break; case C_ARG: script->push_val(stack,c,0,NULL); break; case C_STR: - script->push_conststr(stack, (const char *)(st->script->script_buf+st->pos)); - while(st->script->script_buf[st->pos++]); + script->push_conststr(stack, (const char *)&VECTOR_INDEX(st->script->script_buf, st->pos)); + while (VECTOR_INDEX(st->script->script_buf, st->pos++) != 0) + (void)0; // Skip string break; case C_LSTR: { - int string_id = *((int *)(&st->script->script_buf[st->pos])); - uint8 translations = *((uint8 *)(&st->script->script_buf[st->pos+sizeof(int)])); struct map_session_data *lsd = NULL; - - st->pos += sizeof(int) + sizeof(uint8); + uint8 translations = 0; + int string_id = *((int *)(&VECTOR_INDEX(st->script->script_buf, st->pos))); + st->pos += sizeof(string_id); + translations = *((uint8 *)(&VECTOR_INDEX(st->script->script_buf, st->pos))); + st->pos += sizeof(translations); if( (!st->rid || !(lsd = map->id2sd(st->rid)) || !lsd->lang_id) && !map->default_lang_id ) script->push_conststr(stack, script->string_list+string_id); @@ -4431,7 +4680,7 @@ void run_script_main(struct script_state *st) { int offset = st->pos; for(k = 0; k < translations; k++) { - uint8 lang_id = *(uint8 *)(&st->script->script_buf[offset]); + uint8 lang_id = *(uint8 *)(&VECTOR_INDEX(st->script->script_buf, offset)); offset += sizeof(uint8); if( lang_id == wlang_id ) break; @@ -4440,7 +4689,7 @@ void run_script_main(struct script_state *st) { if (k == translations) script->push_conststr(stack, script->string_list+string_id); else - script->push_conststr(stack, *(const char**)(&st->script->script_buf[offset]) ); + script->push_conststr(stack, *(const char**)(&VECTOR_INDEX(st->script->script_buf, offset))); } st->pos += ( ( sizeof(char*) + sizeof(uint8) ) * translations ); } @@ -4470,6 +4719,7 @@ void run_script_main(struct script_state *st) { case C_ADD: case C_SUB: case C_MUL: + case C_POW: case C_DIV: case C_MOD: case C_EQ: @@ -4548,58 +4798,62 @@ void run_script_main(struct script_state *st) { } } -int script_config_read(char *cfgName) { - int i; - char line[1024],w1[1024],w2[1024]; - FILE *fp; +/** + * Reads 'script_configuration' and initializes required variables. + * + * @param filename Path to configuration file. + * @param imported Whether the current config is imported from another file. + * + * @retval false in case of error. + */ +static bool script_config_read(const char *filename, bool imported) +{ + struct config_t config; + struct config_setting_t * setting = NULL; + const char *import = NULL; + bool retval = true; - if( !( fp = fopen(cfgName,"r") ) ) { - ShowError("File not found: %s\n", cfgName); - return 1; + nullpo_retr(false, filename); + + if (!libconfig->load_file(&config, filename)) + return false; + + if ((setting = libconfig->lookup(&config, "script_configuration")) == NULL) { + libconfig->destroy(&config); + if (imported) + return true; + ShowError("script_config_read: script_configuration was not found in %s!\n", filename); + return false; } - while (fgets(line, sizeof(line), fp)) { - if (line[0] == '/' && line[1] == '/') - continue; - i = sscanf(line,"%1023[^:]: %1023[^\r\n]", w1, w2); - if(i!=2) - continue; - if(strcmpi(w1,"warn_func_mismatch_paramnum")==0) { - script->config.warn_func_mismatch_paramnum = config_switch(w2); - } - else if(strcmpi(w1,"check_cmdcount")==0) { - script->config.check_cmdcount = config_switch(w2); - } - else if(strcmpi(w1,"check_gotocount")==0) { - script->config.check_gotocount = config_switch(w2); - } - else if(strcmpi(w1,"input_min_value")==0) { - script->config.input_min_value = config_switch(w2); - } - else if(strcmpi(w1,"input_max_value")==0) { - script->config.input_max_value = config_switch(w2); - } - else if(strcmpi(w1,"warn_func_mismatch_argtypes")==0) { - script->config.warn_func_mismatch_argtypes = config_switch(w2); - } - else if(strcmpi(w1,"import")==0) { - script->config_read(w2); - } - else if(HPM->parseConf(w1, w2, HPCT_SCRIPT)) { - ; // handled by plugin + libconfig->setting_lookup_bool_real(setting, "warn_func_mismatch_paramnum", &script->config.warn_func_mismatch_paramnum); + libconfig->setting_lookup_bool_real(setting, "warn_func_mismatch_argtypes", &script->config.warn_func_mismatch_argtypes); + libconfig->setting_lookup_int(setting, "check_cmdcount", &script->config.check_cmdcount); + libconfig->setting_lookup_int(setting, "check_gotocount", &script->config.check_gotocount); + libconfig->setting_lookup_int(setting, "input_min_value", &script->config.input_min_value); + libconfig->setting_lookup_int(setting, "input_max_value", &script->config.input_max_value); + + if (!HPM->parse_conf(&config, filename, HPCT_SCRIPT, imported)) + retval = false; + + // import should overwrite any previous configuration, so it should be called last + if (libconfig->lookup_string(&config, "import", &import) == CONFIG_TRUE) { + if (strcmp(import, filename) == 0 || strcmp(import, map->SCRIPT_CONF_NAME) == 0) { + ShowWarning("script_config_read: Loop detected! Skipping 'import'...\n"); } else { - ShowWarning("Unknown setting '%s' in file %s\n", w1, cfgName); + if (!script->config_read(import, true)) + retval = false; } } - fclose(fp); - return 0; + libconfig->destroy(&config); + return retval; } /** * @see DBApply */ -int db_script_free_code_sub(union DBKey key, struct DBData *data, va_list ap) +static int db_script_free_code_sub(union DBKey key, struct DBData *data, va_list ap) { struct script_code *code = DB->data2ptr(data); if (code) @@ -4607,7 +4861,7 @@ int db_script_free_code_sub(union DBKey key, struct DBData *data, va_list ap) return 0; } -void script_run_autobonus(const char *autobonus, int id, int pos) +static void script_run_autobonus(const char *autobonus, int id, int pos) { struct script_code *scriptroot = (struct script_code *)strdb_get(script->autobonus_db, autobonus); @@ -4617,7 +4871,7 @@ void script_run_autobonus(const char *autobonus, int id, int pos) } } -void script_add_autobonus(const char *autobonus) +static void script_add_autobonus(const char *autobonus) { if( strdb_get(script->autobonus_db, autobonus) == NULL ) { struct script_code *scriptroot = script->parse(autobonus, "autobonus", 0, 0, NULL); @@ -4628,13 +4882,19 @@ void script_add_autobonus(const char *autobonus) } /// resets a temporary character array variable to given value -void script_cleararray_pc(struct map_session_data* sd, const char* varname, void* value) { +static void script_cleararray_pc(struct map_session_data *sd, const char *varname, void *value) +{ struct script_array *sa = NULL; struct reg_db *src = NULL; 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; @@ -4655,15 +4915,21 @@ void script_cleararray_pc(struct map_session_data* sd, const char* varname, void /// sets a temporary character array variable element idx to given value /// @param refcache Pointer to an int variable, which keeps a copy of the reference to varname and must be initialized to 0. Can be NULL if only one element is set. -void script_setarray_pc(struct map_session_data* sd, const char* varname, uint32 idx, void* value, int* refcache) { +static void script_setarray_pc(struct map_session_data *sd, const char *varname, uint32 idx, void *value, int *refcache) +{ 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); @@ -4675,10 +4941,11 @@ void script_setarray_pc(struct map_session_data* sd, const char* varname, uint32 /** * Clears persistent variables from memory **/ -int script_reg_destroy(union DBKey key, struct DBData *data, va_list ap) +static int script_reg_destroy(union DBKey key, struct DBData *data, va_list ap) { struct script_reg_state *src; + nullpo_ret(data); if( data->type != DB_DATA_PTR )/* got no need for those! */ return 0; @@ -4699,7 +4966,10 @@ int script_reg_destroy(union DBKey key, struct DBData *data, va_list ap) /** * Clears a single persistent variable **/ -void script_reg_destroy_single(struct map_session_data *sd, int64 reg, struct script_reg_state *data) { +static void script_reg_destroy_single(struct map_session_data *sd, int64 reg, struct script_reg_state *data) +{ + nullpo_retv(sd); + nullpo_retv(data); i64db_remove(sd->regs.vars, reg); if( data->type ) { @@ -4713,20 +4983,23 @@ void script_reg_destroy_single(struct map_session_data *sd, int64 reg, struct sc ers_free(pc->num_reg_ers,(struct script_reg_num*)data); } } -unsigned int *script_array_cpy_list(struct script_array *sa) { +static unsigned int *script_array_cpy_list(struct script_array *sa) +{ + nullpo_retr(NULL, sa); if( sa->size > script->generic_ui_array_size ) script->generic_ui_array_expand(sa->size); memcpy(script->generic_ui_array, sa->members, sizeof(unsigned int)*sa->size); return script->generic_ui_array; } -void script_generic_ui_array_expand (unsigned int plus) { +static void script_generic_ui_array_expand(unsigned int plus) +{ script->generic_ui_array_size += plus + 100; RECREATE(script->generic_ui_array, unsigned int, script->generic_ui_array_size); } /*========================================== * Destructor *------------------------------------------*/ -void do_final_script(void) +static void do_final_script(void) { int i; struct DBIterator *iter; @@ -4806,6 +5079,8 @@ void do_final_script(void) aFree(script->str_buf); for( i = 0; i < atcommand->binding_count; i++ ) { + aFree(atcommand->binding[i]->at_groups); + aFree(atcommand->binding[i]->char_groups); aFree(atcommand->binding[i]); } @@ -4854,16 +5129,15 @@ void do_final_script(void) script->clear_translations(false); script->parser_clean_leftovers(); - - if( script->lang_export_file ) - aFree(script->lang_export_file); } /** * **/ -uint8 script_add_language(const char *name) { +static uint8 script_add_language(const char *name) +{ uint8 lang_id = script->max_lang_id; + nullpo_ret(name); RECREATE(script->languages, char *, ++script->max_lang_id); script->languages[lang_id] = aStrdup(name); @@ -4873,12 +5147,14 @@ uint8 script_add_language(const char *name) { /** * Goes thru db/translations.conf file **/ -void script_load_translations(void) { +static void script_load_translations(void) +{ struct config_t translations_conf; - const char *config_filename = "db/translations.conf"; // FIXME hardcoded name + char config_filename[256]; + libconfig->format_db_path("translations.conf", config_filename, sizeof(config_filename)); struct config_setting_t *translations = NULL; int i, size; - uint32 total = 0; + int total = 0; uint8 lang_id = 0, k; if (map->minimal) // No translations in minimal mode @@ -4914,25 +5190,23 @@ void script_load_translations(void) { size = libconfig->setting_length(translations); for(i = 0; i < size; i++) { - const char *translation_file = libconfig->setting_get_string_elem(translations, i); - script->load_translation(translation_file, ++lang_id, &total); + const char *translation_dir = libconfig->setting_get_string_elem(translations, i); + total += script->load_translation(translation_dir, ++lang_id); } libconfig->destroy(&translations_conf); - if( total ) { - struct DBIterator *main_iter, *sub_iter; + if (total != 0) { + struct DBIterator *main_iter; struct DBMap *string_db; struct string_translation *st = NULL; - uint32 j = 0; - CREATE(script->translation_buf, char *, total); - script->translation_buf_size = total; + VECTOR_ENSURE(script->translation_buf, total, 1); main_iter = db_iterator(script->translation_db); - for( string_db = dbi_first(main_iter); dbi_exists(main_iter); string_db = dbi_next(main_iter) ) { - sub_iter = db_iterator(string_db); - for( st = dbi_first(sub_iter); dbi_exists(sub_iter); st = dbi_next(sub_iter) ) { - script->translation_buf[j++] = st->buf; + for (string_db = dbi_first(main_iter); dbi_exists(main_iter); string_db = dbi_next(main_iter)) { + struct DBIterator *sub_iter = db_iterator(string_db); + for (st = dbi_first(sub_iter); dbi_exists(sub_iter); st = dbi_next(sub_iter)) { + VECTOR_PUSH(script->translation_buf, st->buf); } dbi_destroy(sub_iter); } @@ -4954,149 +5228,319 @@ void script_load_translations(void) { } /** + * Generates a language name from a translation directory name. * - **/ -const char * script_get_translation_file_name(const char *file) { - static char file_name[200]; - int i, len = (int)strlen(file), last_bar = -1, last_dot = -1; + * @param directory The directory name. + * @return The corresponding translation name. + */ +static const char *script_get_translation_dir_name(const char *directory) +{ + const char *basename = NULL, *last_dot = NULL; + + nullpo_retr("Unknown", directory); - for(i = 0; i < len; i++) { - if( file[i] == '/' || file[i] == '\\' ) - last_bar = i; - else if ( file[i] == '.' ) - last_dot = i; + basename = strrchr(directory, '/'); +#ifdef WIN32 + { + const char *basename_windows = strrchr(directory, '\\'); + if (basename_windows > basename) + basename = basename_windows; } +#endif // WIN32 + if (basename == NULL) + basename = directory; + else + basename++; // Skip slash + Assert_retr("Unknown", *basename != '\0'); + + last_dot = strrchr(basename, '.'); + if (last_dot != NULL) { + static char dir_name[200]; + if (last_dot == basename) + return basename + 1; - if( last_bar != -1 || last_dot != -1 ) { - if( last_bar != -1 && last_dot < last_bar ) - last_dot = -1; - safestrncpy(file_name, file+(last_bar >= 0 ? last_bar+1 : 0), ( last_dot >= 0 ? ( last_bar >= 0 ? last_dot - last_bar : last_dot ) : sizeof(file_name) )); - return file_name; + safestrncpy(dir_name, basename, last_dot - basename + 1); + return dir_name; } - return file; + return basename; } /** - * Parses a individual translation file - **/ -void script_load_translation(const char *file, uint8 lang_id, uint32 *total) { - uint32 translations = 0; + * Parses and adds a translated string to the translations database. + * + * @param file Translations file being parsed (for error messages). + * @param lang_id Language ID being parsed. + * @param msgctxt Message context (i.e. NPC name) + * @param msgid Message ID (source string) + * @param msgstr Translated message + * @return success state + * @retval true if a new string was added. + */ +static bool script_load_translation_addstring(const char *file, uint8 lang_id, const char *msgctxt, const struct script_string_buf *msgid, const struct script_string_buf *msgstr) +{ + nullpo_retr(false, file); + nullpo_retr(false, msgctxt); + nullpo_retr(false, msgid); + nullpo_retr(false, msgstr); + + if (VECTOR_LENGTH(*msgid) <= 1) { + // Empty ID (i.e. header) to be ignored + return false; + } + + if (VECTOR_LENGTH(*msgstr) <= 1) { + // Empty (untranslated) string to be ignored + return false; + } + + if (msgctxt[0] == '\0') { + // Missing context + ShowWarning("script_load_translation: Missing context for msgid '%s' in '%s'. Skipping.\n", + VECTOR_DATA(*msgid), file); + return false; + } + + if (strcasecmp(msgctxt, "messages.conf") == 0) { + int i; + for (i = 0; i < MAX_MSG; i++) { + if (atcommand->msg_table[0][i] != NULL && strcmpi(atcommand->msg_table[0][i], VECTOR_DATA(*msgid)) == 0) { + if (atcommand->msg_table[lang_id][i] != NULL) + aFree(atcommand->msg_table[lang_id][i]); + atcommand->msg_table[lang_id][i] = aStrdup(VECTOR_DATA(*msgstr)); + break; + } + } + } else { + int msgstr_len = VECTOR_LENGTH(*msgstr); + int inner_len = 1 + msgstr_len + 1; //uint8 lang_id + msgstr_len + '\0' + struct string_translation *st = NULL; + struct DBMap *string_db; + + if ((string_db = strdb_get(script->translation_db, msgctxt)) == NULL) { + string_db = strdb_alloc(DB_OPT_DUP_KEY, 0); + strdb_put(script->translation_db, msgctxt, string_db); + } + + if ((st = strdb_get(string_db, VECTOR_DATA(*msgid))) == NULL) { + CREATE(st, struct string_translation, 1); + st->string_id = script->string_dup(VECTOR_DATA(*msgid)); + strdb_put(string_db, VECTOR_DATA(*msgid), st); + } + RECREATE(st->buf, uint8, st->len + inner_len); + + WBUFB(st->buf, st->len) = lang_id; + safestrncpy(WBUFP(st->buf, st->len + 1), VECTOR_DATA(*msgstr), msgstr_len + 1); + + st->translations++; + st->len += inner_len; + } + return true; +} + +/** + * Parses an individual translation file. + * + * @param directory The directory structure to read. + * @param lang_id The language identifier. + * @return The amount of strings loaded. + */ +static int script_load_translation_file(const char *file, uint8 lang_id) +{ char line[1024]; - char msgctxt[NAME_LENGTH*2+1] = { 0 }; - struct DBMap *string_db; - size_t i; + char msgctxt[NAME_LENGTH*2+1] = ""; + struct script_string_buf msgid, msgstr; + struct script_string_buf *msg_ptr; + int translations = 0; + int lineno = 0; FILE *fp; - struct script_string_buf msgid = { 0 }, msgstr = { 0 }; - if( !(fp = fopen(file,"rb")) ) { + nullpo_ret(file); + + if ((fp = fopen(file,"rb")) == NULL) { ShowError("load_translation: failed to open '%s' for reading\n",file); - return; + return 0; } - script->add_language(script->get_translation_file_name(file)); - if( lang_id >= atcommand->max_message_table ) - atcommand->expand_message_table(); + VECTOR_INIT(msgid); + VECTOR_INIT(msgstr); + + while (fgets(line, sizeof(line), fp) != NULL) { + int len = (int)strlen(line); + int i; + lineno++; - while(fgets(line, sizeof(line), fp)) { - size_t len = strlen(line), cursor = 0; + if (len <= 1) { + if (VECTOR_LENGTH(msgid) > 0 && VECTOR_LENGTH(msgstr) > 0) { + // Add string + if (script->load_translation_addstring(file, lang_id, msgctxt, &msgid, &msgstr)) + translations++; - if( len <= 1 ) + msgctxt[0] = '\0'; + VECTOR_TRUNCATE(msgid); + VECTOR_TRUNCATE(msgstr); + } continue; + } - if( line[0] == '#' ) + if (line[0] == '#') continue; - if( strncasecmp(line,"msgctxt \"", 9) == 0 ) { + if (VECTOR_LENGTH(msgid) > 0) { + if (VECTOR_LENGTH(msgstr) > 0) { + msg_ptr = &msgstr; + } else { + msg_ptr = &msgid; + } + if (line[0] == '"') { + // Continuation line + (void)VECTOR_POP(*msg_ptr); // Pop final '\0' + for (i = 1; i < len - 2; i++) { + VECTOR_ENSURE(*msg_ptr, 1, 512); + if (line[i] == '\\' && line[i+1] == '"') { + VECTOR_PUSH(*msg_ptr, '"'); + i++; + } else if (line[i] == '\\' && line[i+1] == 'r') { + VECTOR_PUSH(*msg_ptr, '\r'); + i++; + } else { + VECTOR_PUSH(*msg_ptr, line[i]); + } + } + VECTOR_ENSURE(*msg_ptr, 1, 512); + VECTOR_PUSH(*msg_ptr, '\0'); + continue; + } + + } + + if (strncasecmp(line,"msgctxt \"", 9) == 0) { + int cursor = 0; msgctxt[0] = '\0'; - for(i = 9; i < len - 2; i++) { - if( line[i] == '\\' && line[i+1] == '"' ) { + for (i = 9; i < len - 2; i++) { + if (line[i] == '\\' && line[i+1] == '"') { msgctxt[cursor] = '"'; i++; - } else + } else if (line[i] == '\\' && line[i+1] == 'r') { + msgctxt[cursor] = '\r'; + i++; + } else { msgctxt[cursor] = line[i]; - if( ++cursor >= sizeof(msgctxt) - 1 ) + } + if (++cursor >= (int)sizeof(msgctxt) - 1) break; } msgctxt[cursor] = '\0'; - } else if ( strncasecmp(line, "msgid \"", 7) == 0 ) { - msgid.pos = 0; - for(i = 7; i < len - 2; i++) { - if( line[i] == '\\' && line[i+1] == '"' ) { - script_string_buf_addb(&msgid, '"'); + + // New context, reset everything + VECTOR_TRUNCATE(msgid); + VECTOR_TRUNCATE(msgstr); + continue; + } + + if (strncasecmp(line, "msgid \"", 7) == 0) { + VECTOR_TRUNCATE(msgid); + for (i = 7; i < len - 2; i++) { + VECTOR_ENSURE(msgid, 1, 512); + if (line[i] == '\\' && line[i+1] == '"') { + VECTOR_PUSH(msgid, '"'); + i++; + } else if (line[i] == '\\' && line[i+1] == 'r') { + VECTOR_PUSH(msgid, '\r'); i++; - } else - script_string_buf_addb(&msgid, line[i]); + } else { + VECTOR_PUSH(msgid, line[i]); + } } - script_string_buf_addb(&msgid,0); - } else if ( len > 9 && line[9] != '"' && strncasecmp(line, "msgstr \"",8) == 0 ) { - msgstr.pos = 0; - for(i = 8; i < len - 2; i++) { - if( line[i] == '\\' && line[i+1] == '"' ) { - script_string_buf_addb(&msgstr, '"'); + VECTOR_ENSURE(msgid, 1, 512); + VECTOR_PUSH(msgid, '\0'); + + // New id, reset string if any + VECTOR_TRUNCATE(msgstr); + continue; + } + + if (VECTOR_LENGTH(msgid) > 0 && strncasecmp(line, "msgstr \"", 8) == 0) { + VECTOR_TRUNCATE(msgstr); + for (i = 8; i < len - 2; i++) { + VECTOR_ENSURE(msgstr, 1, 512); + if (line[i] == '\\' && line[i+1] == '"') { + VECTOR_PUSH(msgstr, '"'); i++; - } else - script_string_buf_addb(&msgstr, line[i]); + } else if (line[i] == '\\' && line[i+1] == 'r') { + VECTOR_PUSH(msgstr, '\r'); + i++; + } else { + VECTOR_PUSH(msgstr, line[i]); + } } - script_string_buf_addb(&msgstr,0); + VECTOR_ENSURE(msgstr, 1, 512); + VECTOR_PUSH(msgstr, '\0'); + + continue; } - if( msgctxt[0] && msgid.pos > 1 && msgstr.pos > 1 ) { - size_t msgstr_len = msgstr.pos; - unsigned int inner_len = 1 + (uint32)msgstr_len + 1; //uint8 lang_id + msgstr_len + '\0' + ShowWarning("script_load_translation: Unexpected input at '%s' in file '%s' line %d. Skipping.\n", + line, file, lineno); + } - if( strcasecmp(msgctxt, "messages.conf") == 0 ) { - int k; + // Add last string + if (VECTOR_LENGTH(msgid) > 0 && VECTOR_LENGTH(msgstr) > 0) { + if (script->load_translation_addstring(file, lang_id, msgctxt, &msgid, &msgstr)) + translations++; + } - for(k = 0; k < MAX_MSG; k++) { - if( atcommand->msg_table[0][k] && strcmpi(atcommand->msg_table[0][k],msgid.ptr) == 0 ) { - if( atcommand->msg_table[lang_id][k] ) - aFree(atcommand->msg_table[lang_id][k]); - atcommand->msg_table[lang_id][k] = aStrdup(msgstr.ptr); - break; - } - } - } else { - struct string_translation *st = NULL; + fclose(fp); - if( !( string_db = strdb_get(script->translation_db, msgctxt) ) ) { - string_db = strdb_alloc(DB_OPT_DUP_KEY, 0); - strdb_put(script->translation_db, msgctxt, string_db); - } + VECTOR_CLEAR(msgid); + VECTOR_CLEAR(msgstr); - if( !(st = strdb_get(string_db, msgid.ptr) ) ) { - CREATE(st, struct string_translation, 1); - st->string_id = script->string_dup(msgid.ptr); - strdb_put(string_db, msgid.ptr, st); - } - RECREATE(st->buf, char, st->len + inner_len); + return translations; +} - WBUFB(st->buf, st->len) = lang_id; - safestrncpy(WBUFP(st->buf, st->len + 1), msgstr.ptr, msgstr_len + 1); +struct load_translation_data { + uint8 lang_id; + int translation_count; +}; - st->translations++; - st->len += inner_len; - } - msgctxt[0] = '\0'; - msgid.pos = msgstr.pos = 0; - translations++; - } - } +static void script_load_translation_sub(const char *filename, void *context) +{ + nullpo_retv(context); - *total += translations; + struct load_translation_data *data = context; - fclose(fp); + data->translation_count += script->load_translation_file(filename, data->lang_id); +} - script_string_buf_destroy(&msgid); - script_string_buf_destroy(&msgstr); +/** + * Loads a translations directory + * + * @param directory The directory structure to read. + * @param lang_id The language identifier. + * @return The amount of strings loaded. + */ +static int script_load_translation(const char *directory, uint8 lang_id) +{ + struct load_translation_data data = { 0 }; + data.lang_id = lang_id; + + nullpo_ret(directory); + + script->add_language(script->get_translation_dir_name(directory)); + if (lang_id >= atcommand->max_message_table) + atcommand->expand_message_table(); - ShowStatus("Done reading '"CL_WHITE"%u"CL_RESET"' translations in '"CL_WHITE"%s"CL_RESET"'.\n", translations, file); + findfile(directory, ".po", script_load_translation_sub, &data); + + ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' translations in '"CL_WHITE"%s"CL_RESET"'.\n", data.translation_count, directory); + return data.translation_count; } /** * **/ -void script_clear_translations(bool reload) { +static void script_clear_translations(bool reload) +{ uint32 i; if( script->string_list ) @@ -5106,15 +5550,10 @@ void script_clear_translations(bool reload) { script->string_list_pos = 0; script->string_list_size = 0; - if( script->translation_buf ) { - for(i = 0; i < script->translation_buf_size; i++) { - aFree(script->translation_buf[i]); - } - aFree(script->translation_buf); + while (VECTOR_LENGTH(script->translation_buf) > 0) { + aFree(VECTOR_POP(script->translation_buf)); } - - script->translation_buf = NULL; - script->translation_buf_size = 0; + VECTOR_CLEAR(script->translation_buf); if( script->languages ) { for(i = 0; i < script->max_lang_id; i++) @@ -5135,7 +5574,7 @@ void script_clear_translations(bool reload) { /** * **/ -int script_translation_db_destroyer(union DBKey key, struct DBData *data, va_list ap) +static int script_translation_db_destroyer(union DBKey key, struct DBData *data, va_list ap) { struct DBMap *string_db = DB->data2ptr(data); @@ -5156,32 +5595,23 @@ int script_translation_db_destroyer(union DBKey key, struct DBData *data, va_lis /** * **/ -void script_parser_clean_leftovers(void) { - if( script->buf ) - aFree(script->buf); - - script->buf = NULL; - script->size = 0; +static void script_parser_clean_leftovers(void) +{ + VECTOR_CLEAR(script->buf); if( script->translation_db ) { script->translation_db->destroy(script->translation_db,script->translation_db_destroyer); script->translation_db = NULL; } - if( script->syntax.strings ) { /* used only when generating translation file */ - db_destroy(script->syntax.strings); - script->syntax.strings = NULL; - } - - script_string_buf_destroy(&script->parse_simpleexpr_str); - script_string_buf_destroy(&script->lang_export_line_buf); - script_string_buf_destroy(&script->lang_export_unescaped_buf); + VECTOR_CLEAR(script->parse_simpleexpr_strbuf); } /** * Performs cleanup after all parsing is processed **/ -int script_parse_cleanup_timer(int tid, int64 tick, int id, intptr_t data) { +static int script_parse_cleanup_timer(int tid, int64 tick, int id, intptr_t data) +{ script->parser_clean_leftovers(); script->parse_cleanup_timer_id = INVALID_TIMER; @@ -5192,8 +5622,10 @@ int script_parse_cleanup_timer(int tid, int64 tick, int id, intptr_t data) { /*========================================== * Initialization *------------------------------------------*/ -void do_init_script(bool minimal) { +static void do_init_script(bool minimal) +{ script->parse_cleanup_timer_id = INVALID_TIMER; + VECTOR_INIT(script->parse_simpleexpr_strbuf); script->st_db = idb_alloc(DB_OPT_BASE); script->userfunc_db = strdb_alloc(DB_OPT_DUP_KEY,0); @@ -5210,7 +5642,7 @@ void do_init_script(bool minimal) { VECTOR_INIT(script->hqi); script->parse_builtin(); - script->read_constdb(); + script->read_constdb(false); script->load_parameters(); script->hardcoded_constants(); @@ -5221,7 +5653,7 @@ void do_init_script(bool minimal) { script->load_translations(); } -int script_reload(void) +static int script_reload(void) { int i; struct DBIterator *iter; @@ -5243,6 +5675,8 @@ int script_reload(void) script->label_count = 0; for( i = 0; i < atcommand->binding_count; i++ ) { + aFree(atcommand->binding[i]->at_groups); + aFree(atcommand->binding[i]->char_groups); aFree(atcommand->binding[i]); } @@ -5260,18 +5694,21 @@ int script_reload(void) script->parse_cleanup_timer_id = INVALID_TIMER; } - mapreg->reload(); - + script->read_constdb(true); itemdb->name_constants(); + clan->set_constants(); + mapreg->reload(); sysinfo->vcsrevision_reload(); return 0; } /* returns name of current function being run, from within the stack [Ind/Hercules] */ -const char *script_getfuncname(struct script_state *st) { +static const char *script_getfuncname(struct script_state *st) +{ struct script_data *data; + nullpo_retr(NULL, st); data = &st->stack->stack_data[st->start]; if( data->type == C_NAME && script->str_data[data->u.num].type == C_FUNC ) @@ -5280,6 +5717,231 @@ const char *script_getfuncname(struct script_state *st) { return NULL; } +/** + * Writes a string to a StringBuf by combining a format string and a set of + * arguments taken from the current script state (caller script function + * arguments). + * + * @param[in] st Script state (must have at least a string at index + * 'start'). + * @param[in] start Index of the format string argument. + * @param[out] out Output string buffer (managed by the caller, must be + * already initialized) + * @retval false if an error occurs. + */ +static bool script_sprintf_helper(struct script_state *st, int start, struct StringBuf *out) +{ + const char *format = NULL; + const char *p = NULL, *np = NULL; + char *buf = NULL; + int buf_len = 0; + int lastarg = start; + int argc = script_lastdata(st) + 1; + + nullpo_retr(-1, out); + Assert_retr(-1, start >= 2 && start <= argc); + Assert_retr(-1, script_hasdata(st, start)); + + p = format = script_getstr(st, start); + + /* + * format-string = "" / *(text / placeholder) + * placeholder = "%%" / "%n" / std-placeholder + * std-placeholder = "%" [pos-parameter] [flags] [width] [precision] [length] type + * pos-parameter = number "$" + * flags = *("-" / "+" / "0" / SP) + * width = number / ("*" [pos-parameter]) + * precision = "." (number / ("*" [pos-parameter])) + * length = "hh" / "h" / "l" / "ll" / "L" / "z" / "j" / "t" + * type = "d" / "i" / "u" / "f" / "F" / "e" / "E" / "g" / "G" / "x" / "X" / "o" / "s" / "c" / "p" / "a" / "A" + * number = digit-nonzero *DIGIT + * digit-nonzero = "1" / "2" / "3" / "4" / "5" / "6" / "7" / "8" / "9" + */ + + while ((np = strchr(p, '%')) != NULL) { + bool flag_plus = false, flag_minus = false, flag_zero = false, flag_space = false; + bool positional_arg = false; + int width = 0, nextarg = lastarg + 1, thisarg = nextarg; + + if (p != np) { + int len = (int)(np - p + 1); + if (buf_len < len) { + RECREATE(buf, char, len); + buf_len = len; + } + safestrncpy(buf, p, len); + StrBuf->AppendStr(out, buf); + } + np++; + + // placeholder = "%%" ; (special case) + if (*np == '%') { + StrBuf->AppendStr(out, "%"); + p = np + 1; + continue; + } + // placeholder = "%n" ; (ignored) + if (*np == 'n') { + ShowWarning("script_sprintf_helper: Format %%n not supported! Skipping...\n"); + script->reportsrc(st); + lastarg = nextarg; + p = np + 1; + continue; + } + + // std-placeholder = "%" [pos-parameter] [flags] [width] [precision] [length] type + + // pos-parameter = number "$" + if (ISDIGIT(*np) && *np != '0') { + const char *pp = np; + while (ISDIGIT(*pp)) + pp++; + if (*pp == '$') { + thisarg = atoi(np) + start; + positional_arg = true; + np = pp + 1; + } + } + + if (thisarg >= argc) { + ShowError("buildin_sprintf: Not enough arguments passed!\n"); + if (buf != NULL) + aFree(buf); + return false; + } + + // flags = *("-" / "+" / "0" / SP) + while (true) { + if (*np == '-') { + flag_minus = true; + } else if (*np == '+') { + flag_plus = true; + } else if (*np == ' ') { + flag_space = true; + } else if (*np == '0') { + flag_zero = true; + } else { + break; + } + np++; + } + + // width = number / ("*" [pos-parameter]) + if (ISDIGIT(*np)) { + width = atoi(np); + while (ISDIGIT(*np)) + np++; + } else if (*np == '*') { + bool positional_widtharg = false; + int width_arg; + np++; + // pos-parameter = number "$" + if (ISDIGIT(*np) && *np != '0') { + const char *pp = np; + while (ISDIGIT(*pp)) + pp++; + if (*pp == '$') { + width_arg = atoi(np) + start; + positional_widtharg = true; + np = pp + 1; + } + } + if (!positional_widtharg) { + width_arg = nextarg; + nextarg++; + if (!positional_arg) + thisarg++; + } + + if (width_arg >= argc || thisarg >= argc) { + ShowError("buildin_sprintf: Not enough arguments passed!\n"); + if (buf != NULL) + aFree(buf); + return false; + } + width = script_getnum(st, width_arg); + } + + // precision = "." (number / ("*" [pos-parameter])) ; (not needed/implemented) + + // length = "hh" / "h" / "l" / "ll" / "L" / "z" / "j" / "t" ; (not needed/implemented) + + // type = "d" / "i" / "u" / "f" / "F" / "e" / "E" / "g" / "G" / "x" / "X" / "o" / "s" / "c" / "p" / "a" / "A" + if (buf_len < 16) { + RECREATE(buf, char, 16); + buf_len = 16; + } + { + int i = 0; + memset(buf, '\0', buf_len); + buf[i++] = '%'; + if (flag_minus) + buf[i++] = '-'; + if (flag_plus) + buf[i++] = '+'; + else if (flag_space) // ignored if '+' is specified + buf[i++] = ' '; + if (flag_zero) + buf[i++] = '0'; + if (width > 0) + safesnprintf(buf + i, buf_len - i - 1, "%d", width); + } + buf[(int)strlen(buf)] = *np; + switch (*np) { + case 'd': + case 'i': + case 'u': + case 'x': + case 'X': + case 'o': + // Piggyback printf + StrBuf->Printf(out, buf, script_getnum(st, thisarg)); + break; + case 's': + // Piggyback printf + StrBuf->Printf(out, buf, script_getstr(st, thisarg)); + break; + case 'c': + { + const char *str = script_getstr(st, thisarg); + // Piggyback printf + StrBuf->Printf(out, buf, str[0]); + } + break; + case 'f': + case 'F': + case 'e': + case 'E': + case 'g': + case 'G': + case 'p': + case 'a': + case 'A': + ShowWarning("buildin_sprintf: Format %%%c not supported! Skipping...\n", *np); + script->reportsrc(st); + lastarg = nextarg; + p = np + 1; + continue; + default: + ShowError("buildin_sprintf: Invalid format string.\n"); + if (buf != NULL) + aFree(buf); + return false; + } + lastarg = nextarg; + p = np + 1; + } + + // Append the remaining part + if (p != NULL) + StrBuf->AppendStr(out, p); + + if (buf != NULL) + aFree(buf); + + return true; +} + //----------------------------------------------------------------------------- // buildin functions // @@ -5292,23 +5954,49 @@ const char *script_getfuncname(struct script_state *st) { /// If a dialog doesn't exist yet, one is created. /// /// mes "<message>"; -BUILDIN(mes) +static BUILDIN(mes) { struct map_session_data *sd = script->rid2sd(st); - if( sd == NULL ) + + if (sd == NULL) return true; - if( !script_hasdata(st, 3) ) {// only a single line detected in the script + if (script_hasdata(st, 2)) clif->scriptmes(sd, st->oid, script_getstr(st, 2)); - } else {// parse multiple lines as they exist - int i; + else + clif->scriptmes(sd, st->oid, ""); - for( i = 2; script_hasdata(st, i); i++ ) { - // send the message to the client - clif->scriptmes(sd, st->oid, script_getstr(st, i)); - } + return true; +} + +/** + * Appends a message to the npc dialog, applying format string conversions (see + * sprintf). + * + * If a dialog doesn't exist yet, one is created. + * + * @code + * mes "<message>"; + * @endcode + */ +static BUILDIN(mesf) +{ + struct map_session_data *sd = script->rid2sd(st); + struct StringBuf buf; + + if (sd == NULL) + return true; + + StrBuf->Init(&buf); + + if (!script->sprintf_helper(st, 2, &buf)) { + StrBuf->Destroy(&buf); + return false; } + clif->scriptmes(sd, st->oid, StrBuf->Value(&buf)); + StrBuf->Destroy(&buf); + return true; } @@ -5316,7 +6004,7 @@ BUILDIN(mes) /// The dialog text is cleared and the script continues when the button is pressed. /// /// next; -BUILDIN(next) +static BUILDIN(next) { struct map_session_data *sd = script->rid2sd(st); if (sd == NULL) @@ -5329,11 +6017,24 @@ BUILDIN(next) return true; } +/// Clears the NPC dialog and continues the script without press next button. +/// +/// mesclear(); +static BUILDIN(mesclear) +{ + struct map_session_data *sd = script->rid2sd(st); + + if (sd != NULL) + clif->scriptclear(sd, st->oid); + + return true; +} + /// Ends the script and displays the button 'close' on the npc dialog. /// The dialog is closed when the button is pressed. /// /// close; -BUILDIN(close) +static BUILDIN(close) { struct map_session_data *sd = script->rid2sd(st); if (sd == NULL) @@ -5348,7 +6049,7 @@ BUILDIN(close) /// The dialog is closed and the script continues when the button is pressed. /// /// close2; -BUILDIN(close2) +static BUILDIN(close2) { struct map_session_data *sd = script->rid2sd(st); if (sd == NULL) @@ -5368,11 +6069,12 @@ BUILDIN(close2) /// Counts the number of valid and total number of options in 'str' /// If max_count > 0 the counting stops when that valid option is reached /// total is incremented for each option (NULL is supported) -int menu_countoptions(const char* str, int max_count, int* total) +static int menu_countoptions(const char *str, int max_count, int *total) { int count = 0; int bogus_total; + nullpo_ret(str); if( total == NULL ) total = &bogus_total; ++(*total); @@ -5417,7 +6119,7 @@ int menu_countoptions(const char* str, int max_count, int* total) /// NOTE: the client closes the npc dialog when cancel is pressed /// /// menu "<option_text>",<target_label>{,"<option_text>",<target_label>,...}; -BUILDIN(menu) +static BUILDIN(menu) { int i; const char* text; @@ -5469,11 +6171,11 @@ 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); @@ -5482,13 +6184,13 @@ 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; @@ -5526,7 +6228,7 @@ 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; } @@ -5539,7 +6241,7 @@ BUILDIN(menu) /// select(<option_text>{,<option_text>,...}) -> <selected_option> /// /// @see menu -BUILDIN(select) +static BUILDIN(select) { int i; const char* text; @@ -5570,11 +6272,11 @@ 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); @@ -5582,107 +6284,31 @@ 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 + } else if(sd->npc_menu == MAX_MENU_OPTIONS) { // Cancel was pressed sd->state.menu_or_input = 0; - st->state = END; - } else {// return selected option - int menu = 0; - - 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 -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; } @@ -5696,7 +6322,7 @@ BUILDIN(prompt) /// Jumps to the target script label. /// /// goto <label>; -BUILDIN(goto) +static BUILDIN(goto) { if( !data_islabel(script_getdata(st,2)) ) { @@ -5714,7 +6340,7 @@ BUILDIN(goto) /*========================================== * user-defined function call *------------------------------------------*/ -BUILDIN(callfunc) +static BUILDIN(callfunc) { int i, j; struct script_retinfo* ri; @@ -5774,7 +6400,7 @@ BUILDIN(callfunc) /*========================================== * subroutine call *------------------------------------------*/ -BUILDIN(callsub) +static BUILDIN(callsub) { int i,j; struct script_retinfo* ri; @@ -5827,7 +6453,7 @@ BUILDIN(callsub) /// If the argument doesn't exist /// /// getarg(<index>{,<default_value>}) -> <value> -BUILDIN(getarg) +static BUILDIN(getarg) { struct script_retinfo* ri; int idx; @@ -5861,7 +6487,8 @@ BUILDIN(getarg) /// /// return; /// return <value>; -BUILDIN(return) { +static BUILDIN(return) +{ st->state = RETFUNC; if( st->stack->defsp < 1 || st->stack->stack_data[st->stack->defsp-1].type != C_RETINFO ) { @@ -5911,7 +6538,7 @@ BUILDIN(return) { /// If <min> is greater than <max>, their numbers are switched. /// rand(<range>) -> <int> /// rand(<min>,<max>) -> <int> -BUILDIN(rand) +static BUILDIN(rand) { int range; int min; @@ -5940,7 +6567,7 @@ BUILDIN(rand) /*========================================== * Warp sd to str,x,y or Random or SavePoint/Save *------------------------------------------*/ -BUILDIN(warp) +static BUILDIN(warp) { int ret; int x,y; @@ -5976,7 +6603,7 @@ BUILDIN(warp) /*========================================== * Warp a specified area *------------------------------------------*/ -int buildin_areawarp_sub(struct block_list *bl, va_list ap) +static int buildin_areawarp_sub(struct block_list *bl, va_list ap) { int x2,y2,x3,y3; unsigned int index; @@ -5996,6 +6623,9 @@ int buildin_areawarp_sub(struct block_list *bl, va_list ap) pc->randomwarp(sd, CLR_TELEPORT); } else if (x3 != 0 && y3 != 0) { int max, tx, ty, j = 0; + int16 m; + + m = map->mapindex2mapid(index); // choose a suitable max number of attempts if( (max = (y3-y2+1)*(x3-x2+1)*3) > 1000 ) @@ -6006,7 +6636,7 @@ int buildin_areawarp_sub(struct block_list *bl, va_list ap) tx = rnd()%(x3-x2+1)+x2; ty = rnd()%(y3-y2+1)+y2; j++; - } while (map->getcell(index, bl, tx, ty, CELL_CHKNOPASS) && j < max); + } while (map->getcell(m, bl, tx, ty, CELL_CHKNOPASS) && j < max); pc->setpos(sd, index, tx, ty, CLR_OUTSIGHT); } else { @@ -6014,7 +6644,7 @@ int buildin_areawarp_sub(struct block_list *bl, va_list ap) } return 0; } -BUILDIN(areawarp) +static BUILDIN(areawarp) { int16 m, x0,y0,x1,y1, x2,y2,x3=0,y3=0; unsigned int index; @@ -6056,7 +6686,7 @@ BUILDIN(areawarp) /*========================================== * areapercentheal <map>,<x1>,<y1>,<x2>,<y2>,<hp>,<sp> *------------------------------------------*/ -int buildin_areapercentheal_sub(struct block_list *bl, va_list ap) +static int buildin_areapercentheal_sub(struct block_list *bl, va_list ap) { int hp = va_arg(ap, int); int sp = va_arg(ap, int); @@ -6070,7 +6700,8 @@ int buildin_areapercentheal_sub(struct block_list *bl, va_list ap) return 0; } -BUILDIN(areapercentheal) { +static BUILDIN(areapercentheal) +{ int hp,sp,m; const char *mapname; int x0,y0,x1,y1; @@ -6096,7 +6727,8 @@ BUILDIN(areapercentheal) { * another player npc-session. * Using: warpchar "mapname",x,y,Char_ID; *------------------------------------------*/ -BUILDIN(warpchar) { +static BUILDIN(warpchar) +{ int x,y,a; const char *str; struct map_session_data *sd; @@ -6120,160 +6752,209 @@ BUILDIN(warpchar) { return true; } -/*========================================== - * Warpparty - [Fredzilla] [Paradox924X] - * Syntax: warpparty "to_mapname",x,y,Party_ID,{"from_mapname"}; - * If 'from_mapname' is specified, only the party members on that map will be warped - *------------------------------------------*/ -BUILDIN(warpparty) + +/** + * Warps a party to a specific/random map or save point. + * + * @code{.herc} + * warpparty("<to map name>", <x>, <y>, <party id>{{, <ignore mapflags>}, "<from map name>"{, <include leader>}}); + * @endcode + * + **/ +static BUILDIN(warpparty) { - struct map_session_data *sd = NULL; - struct map_session_data *pl_sd; - struct party_data* p; - int type; - int map_index; - int i; + const int p_id = script_getnum(st, 5); + struct party_data *p = party->search(p_id); - const char* str = script_getstr(st,2); - int x = script_getnum(st,3); - int y = script_getnum(st,4); - int p_id = script_getnum(st,5); - const char* str2 = NULL; - if ( script_hasdata(st,6) ) - str2 = script_getstr(st,6); + if (p == NULL) { + ShowError("script:%s: Party not found! (%d)\n", script->getfuncname(st), p_id); + script_pushint(st, 0); + return false; + } - p = party->search(p_id); - if(!p) - return true; + const char *m_name_to = script_getstr(st, 2); + const int type = (strcmp(m_name_to, "Random") == 0) ? 0 : + (strcmp(m_name_to, "SavePointAll") == 0) ? 1 : + (strcmp(m_name_to, "SavePoint") == 0) ? 2 : + (strcmp(m_name_to, "Leader") == 0) ? 3 : 4; + int map_index = 0; - type = ( strcmp(str,"Random")==0 ) ? 0 - : ( strcmp(str,"SavePointAll")==0 ) ? 1 - : ( strcmp(str,"SavePoint")==0 ) ? 2 - : ( strcmp(str,"Leader")==0 ) ? 3 - : 4; + if (type == 4 && (map_index = script->mapindexname2id(st, m_name_to)) == 0) { + ShowError("script:%s: Target map not found! (%s)\n", script->getfuncname(st), m_name_to); + script_pushint(st, 0); + return false; + } - switch (type) - { - case 3: - for(i = 0; i < MAX_PARTY && !p->party.member[i].leader; i++); - if (i == MAX_PARTY || !p->data[i].sd) //Leader not found / not online - return true; - pl_sd = p->data[i].sd; - map_index = pl_sd->mapindex; - x = pl_sd->bl.x; - y = pl_sd->bl.y; - break; - case 4: - map_index = script->mapindexname2id(st,str); - break; - case 2: - //"SavePoint" uses save point of the currently attached player - if (( sd = script->rid2sd(st) ) == NULL ) - return true; - /* Fall through */ - default: - map_index = 0; - break; + int x = script_getnum(st, 3); + int y = script_getnum(st, 4); + struct map_session_data *p_sd; + + if (type == 3) { + int idx; + + ARR_FIND(0, MAX_PARTY, idx, p->party.member[idx].leader == 1); + + if (idx == MAX_PARTY || (p_sd = p->data[idx].sd) == NULL) { + ShowError("script:%s: Party leader not found!\n", script->getfuncname(st)); + script_pushint(st, 0); + return false; + } + + map_index = p_sd->mapindex; + x = p_sd->bl.x; + y = p_sd->bl.y; + } else if (type == 2) { + struct map_session_data *sd = script->rid2sd(st); + + if (sd == NULL) { + ShowError("script:%s: No character attached for warp to save point!\n", + script->getfuncname(st)); + script_pushint(st, 0); + return false; + } + + map_index = sd->status.save_point.map; + x = sd->status.save_point.x; + y = sd->status.save_point.y; } - for (i = 0; i < MAX_PARTY; i++) { - if( !(pl_sd = p->data[i].sd) || pl_sd->status.party_id != p_id ) + const int offset = (script_hasdata(st, 6) && script_isinttype(st, 6)) ? 1 : 0; + const bool ignore_mapflags = (offset == 1 && script_getnum(st, 6) != 0); + const char *m_name_from = script_hasdata(st, 6 + offset) ? script_getstr(st, 6 + offset) : NULL; + const bool include_leader = script_hasdata(st, 7 + offset) ? script_getnum(st, 7 + offset) : true; + + if (m_name_from != NULL && script->mapindexname2id(st, m_name_from) == 0) { + ShowError("script:%s: Source map not found! (%s)\n", script->getfuncname(st), m_name_from); + script_pushint(st, 0); + return false; + } + + for (int i = 0; i < MAX_PARTY; i++) { + if ((p_sd = p->data[i].sd) == NULL || p_sd->status.party_id != p_id || pc_isdead(p_sd)) continue; - if( str2 && strcmp(str2, map->list[pl_sd->bl.m].name) != 0 ) + if (p->party.member[i].online == 0 || (!include_leader && p->party.member[i].leader == 1)) continue; - if( pc_isdead(pl_sd) ) + if (m_name_from != NULL && strcmp(m_name_from, map->list[p_sd->bl.m].name) != 0) continue; - switch( type ) - { - case 0: // Random - if(!map->list[pl_sd->bl.m].flag.nowarp) - pc->randomwarp(pl_sd,CLR_TELEPORT); - break; - case 1: // SavePointAll - if(!map->list[pl_sd->bl.m].flag.noreturn) - pc->setpos(pl_sd,pl_sd->status.save_point.map,pl_sd->status.save_point.x,pl_sd->status.save_point.y,CLR_TELEPORT); - break; - case 2: // SavePoint - if(!map->list[pl_sd->bl.m].flag.noreturn) - pc->setpos(pl_sd,sd->status.save_point.map,sd->status.save_point.x,sd->status.save_point.y,CLR_TELEPORT); - break; - case 3: // Leader - case 4: // m,x,y - if(!map->list[pl_sd->bl.m].flag.noreturn && !map->list[pl_sd->bl.m].flag.nowarp) - pc->setpos(pl_sd,map_index,x,y,CLR_TELEPORT); - break; + if (!ignore_mapflags) { + if (((type == 0 || type > 2) && map->list[p_sd->bl.m].flag.nowarp == 1) || + (type > 0 && map->list[p_sd->bl.m].flag.noreturn == 1)) + continue; + } + + if (type == 1) { + map_index = p_sd->status.save_point.map; + x = p_sd->status.save_point.x; + y = p_sd->status.save_point.y; } + + if (type > 0) + pc->setpos(p_sd, map_index, x, y, CLR_TELEPORT); + else + pc->randomwarp(p_sd, CLR_TELEPORT); } + script_pushint(st, 1); return true; } -/*========================================== - * Warpguild - [Fredzilla] - * Syntax: warpguild "mapname",x,y,Guild_ID; - *------------------------------------------*/ -BUILDIN(warpguild) + +/** + * Warps a guild to a specific/random map or save point. + * + * @code{.herc} + * warpguild("<to map name>", <x>, <y>, <guild id>{{, <ignore mapflags>}, "<from map name>"}); + * @endcode + * + **/ +static BUILDIN(warpguild) { - struct map_session_data *sd = NULL; - struct map_session_data *pl_sd; - struct guild* g; - struct s_mapiterator* iter; - int type; + const int g_id = script_getnum(st, 5); + struct guild *g = guild->search(g_id); - const char* str = script_getstr(st,2); - int x = script_getnum(st,3); - int y = script_getnum(st,4); - int gid = script_getnum(st,5); + if (g == NULL) { + ShowError("script:%s: Guild not found! (%d)\n", script->getfuncname(st), g_id); + script_pushint(st, 0); + return false; + } - g = guild->search(gid); - if( g == NULL ) - return true; + const char *m_name_to = script_getstr(st, 2); + const int type = (strcmp(m_name_to, "Random") == 0) ? 0 : + (strcmp(m_name_to, "SavePointAll") == 0) ? 1 : + (strcmp(m_name_to, "SavePoint") == 0) ? 2 : 3; + int map_index = 0; - type = ( strcmp(str,"Random")==0 ) ? 0 - : ( strcmp(str,"SavePointAll")==0 ) ? 1 - : ( strcmp(str,"SavePoint")==0 ) ? 2 - : 3; + if (type == 3 && (map_index = script->mapindexname2id(st, m_name_to)) == 0) { + ShowError("script:%s: Target map not found! (%s)\n", script->getfuncname(st), m_name_to); + script_pushint(st, 0); + return false; + } - if( type == 2 && ( sd = script->rid2sd(st) ) == NULL ) - {// "SavePoint" uses save point of the currently attached player - return true; + int x = script_getnum(st, 3); + int y = script_getnum(st, 4); + + if (type == 2) { + struct map_session_data *sd = script->rid2sd(st); + + if (sd == NULL) { + ShowError("script:%s: No character attached for warp to save point!\n", + script->getfuncname(st)); + script_pushint(st, 0); + return false; + } + + map_index = sd->status.save_point.map; + x = sd->status.save_point.x; + y = sd->status.save_point.y; } - iter = mapit_getallusers(); - for (pl_sd = BL_UCAST(BL_PC, mapit->first(iter)); mapit->exists(iter); pl_sd = BL_UCAST(BL_PC, mapit->next(iter))) { - if( pl_sd->status.guild_id != gid ) + const int offset = (script_hasdata(st, 6) && script_isinttype(st, 6)) ? 1 : 0; + const bool ignore_mapflags = (offset == 1 && script_getnum(st, 6) != 0); + const char *m_name_from = script_hasdata(st, 6 + offset) ? script_getstr(st, 6 + offset) : NULL; + + if (m_name_from != NULL && script->mapindexname2id(st, m_name_from) == 0) { + ShowError("script:%s: Source map not found! (%s)\n", script->getfuncname(st), m_name_from); + script_pushint(st, 0); + return false; + } + + for (int i = 0; i < MAX_GUILD; i++) { + struct map_session_data *g_sd = g->member[i].sd; + + if (g->member[i].online == 0 || g_sd == NULL || g_sd->status.guild_id != g_id || pc_isdead(g_sd)) continue; - switch( type ) - { - case 0: // Random - if(!map->list[pl_sd->bl.m].flag.nowarp) - pc->randomwarp(pl_sd,CLR_TELEPORT); - break; - case 1: // SavePointAll - if(!map->list[pl_sd->bl.m].flag.noreturn) - pc->setpos(pl_sd,pl_sd->status.save_point.map,pl_sd->status.save_point.x,pl_sd->status.save_point.y,CLR_TELEPORT); - break; - case 2: // SavePoint - if(!map->list[pl_sd->bl.m].flag.noreturn) - pc->setpos(pl_sd,sd->status.save_point.map,sd->status.save_point.x,sd->status.save_point.y,CLR_TELEPORT); - break; - case 3: // m,x,y - if(!map->list[pl_sd->bl.m].flag.noreturn && !map->list[pl_sd->bl.m].flag.nowarp) - pc->setpos(pl_sd,script->mapindexname2id(st,str),x,y,CLR_TELEPORT); - break; + if (m_name_from != NULL && strcmp(m_name_from, map->list[g_sd->bl.m].name) != 0) + continue; + + if (!ignore_mapflags) { + if (((type == 0 || type > 2) && map->list[g_sd->bl.m].flag.nowarp == 1) || + (type > 0 && map->list[g_sd->bl.m].flag.noreturn == 1)) + continue; + } + + if (type == 1) { + map_index = g_sd->status.save_point.map; + x = g_sd->status.save_point.x; + y = g_sd->status.save_point.y; } + + if (type > 0) + pc->setpos(g_sd, map_index, x, y, CLR_TELEPORT); + else + pc->randomwarp(g_sd, CLR_TELEPORT); } - mapit->free(iter); + script_pushint(st, 1); return true; } + /*========================================== * Force Heal a player (hp and sp) *------------------------------------------*/ -BUILDIN(heal) +static BUILDIN(heal) { int hp,sp; struct map_session_data *sd = script->rid2sd(st); @@ -6282,13 +6963,13 @@ 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; } /*========================================== * Heal a player by item (get vit bonus etc) *------------------------------------------*/ -BUILDIN(itemheal) +static BUILDIN(itemheal) { struct map_session_data *sd; int hp,sp; @@ -6303,14 +6984,15 @@ BUILDIN(itemheal) } sd = script->rid2sd(st); - if (!sd) return true; + if (sd == NULL) + return true; pc->itemheal(sd,sd->itemid,hp,sp); return true; } /*========================================== * *------------------------------------------*/ -BUILDIN(percentheal) +static BUILDIN(percentheal) { int hp,sp; struct map_session_data *sd; @@ -6325,33 +7007,36 @@ BUILDIN(percentheal) } sd = script->rid2sd(st); - if( sd == NULL ) + if (sd == NULL) return true; #ifdef RENEWAL if( sd->sc.data[SC_EXTREMITYFIST2] ) sp = 0; #endif - pc->percentheal(sd,hp,sp); + if (sd->sc.data[SC_BITESCAR]) { + hp = 0; + } + pc->percentheal(sd, hp, sp); return true; } /*========================================== * *------------------------------------------*/ -BUILDIN(jobchange) +static BUILDIN(jobchange) { - int job, upper=-1; + int class, upper=-1; - job=script_getnum(st,2); + class = script_getnum(st,2); if( script_hasdata(st,3) ) upper=script_getnum(st,3); - if (pc->db_checkid(job)) { + if (pc->db_checkid(class)) { struct map_session_data *sd = script->rid2sd(st); if (sd == NULL) return true; - pc->jobchange(sd, job, upper); + pc->jobchange(sd, class, upper); } return true; @@ -6360,66 +7045,67 @@ BUILDIN(jobchange) /*========================================== * *------------------------------------------*/ -BUILDIN(jobname) +static BUILDIN(jobname) { - int class_=script_getnum(st,2); - script_pushconststr(st, pc->job_name(class_)); + int class = script_getnum(st,2); + script_pushconststr(st, pc->job_name(class)); return true; } -/// Get input from the player. -/// For numeric inputs the value is capped to the range [min,max]. Returns 1 if -/// the value was higher than 'max', -1 if lower than 'min' and 0 otherwise. -/// For string inputs it returns 1 if the string was longer than 'max', -1 is -/// shorter than 'min' and 0 otherwise. -/// -/// input(<var>{,<min>{,<max>}}) -> <int> -BUILDIN(input) +/* + * Get input from the player. + * For numeric inputs the value is capped to the range [min,max]. Returns 1 if + * the value was higher than 'max', -1 if lower than 'min' and 0 otherwise. + * For string inputs it returns 1 if the string was longer than 'max', -1 is + * shorter than 'min' and 0 otherwise. + * + * input(<var>{,<min>{,<max>}}) -> <int> + */ +static BUILDIN(input) { - struct script_data* data; - int64 uid; - const char* name; - int min; - int max; struct map_session_data *sd = script->rid2sd(st); if (sd == NULL) return true; - data = script_getdata(st,2); - if( !data_isreference(data) ) { + struct script_data *data = script_getdata(st, 2); + if (!data_isreference(data)) { ShowError("script:input: not a variable\n"); script->reportdata(data); st->state = END; return false; } - uid = reference_getuid(data); - name = reference_getname(data); - min = (script_hasdata(st,3) ? script_getnum(st,3) : script->config.input_min_value); - max = (script_hasdata(st,4) ? script_getnum(st,4) : script->config.input_max_value); + + int64 uid = reference_getuid(data); + const char *name = reference_getname(data); + int min = (script_hasdata(st, 3) ? script_getnum(st, 3) : script->config.input_min_value); + int max = (script_hasdata(st, 4) ? script_getnum(st, 4) : script->config.input_max_value); #ifdef SECURE_NPCTIMEOUT sd->npc_idle_type = NPCT_WAIT; #endif - if( !sd->state.menu_or_input ) { + if (!sd->state.menu_or_input) { // first invocation, display npc input box sd->state.menu_or_input = 1; st->state = RERUNLINE; - if( is_string_variable(name) ) - clif->scriptinputstr(sd,st->oid); - else - clif->scriptinput(sd,st->oid); + if (is_string_variable(name)) { + clif->scriptinputstr(sd, st->oid); + } else { + sd->npc_amount_min = min; + sd->npc_amount_max = max; + clif->scriptinput(sd, st->oid); + } } else { // take received text/value and store it in the designated variable sd->state.menu_or_input = 0; if (is_string_variable(name)) { int len = (int)strlen(sd->npc_str); - script->set_reg(st, sd, uid, name, sd->npc_str, script_getref(st,2)); + script->set_reg(st, sd, uid, name, sd->npc_str, script_getref(st, 2)); script_pushint(st, (len > max ? 1 : len < min ? -1 : 0)); } else { int amount = sd->npc_amount; script->set_reg(st, sd, uid, name, (const void *)h64BPTRSIZE(cap_value(amount,min,max)), script_getref(st,2)); - script_pushint(st, (amount > max ? 1 : amount < min ? -1 : 0)); + script_pushint(st, sd->npc_input_capped_range); } st->state = RUN; } @@ -6427,13 +7113,13 @@ BUILDIN(input) } // declare the copyarray method here for future reference -BUILDIN(copyarray); +static BUILDIN(copyarray); /// Sets the value of a variable. /// The value is converted to the type of the variable. /// /// set(<variable>,<value>) -> <variable> -BUILDIN(__setr) +static BUILDIN(__setr) { struct map_session_data *sd = NULL; struct script_data* data; @@ -6441,6 +7127,7 @@ BUILDIN(__setr) int64 num; const char* name; char prefix; + struct reg_db *ref; data = script_getdata(st,2); //datavalue = script_getdata(st,3); @@ -6453,11 +7140,11 @@ BUILDIN(__setr) num = reference_getuid(data); name = reference_getname(data); + ref = reference_getref(data); prefix = *name; if (not_server_variable(prefix)) { - sd = script->rid2sd(st); - if (sd == NULL) { + if (ref == NULL && (sd = script->rid2sd(st)) == NULL) { ShowError("script:set: no player attached for player variable '%s'\n", name); return true; } @@ -6505,9 +7192,9 @@ BUILDIN(__setr) } if (is_string_variable(name)) - script->set_reg(st, sd, num, name, script_getstr(st, 3), script_getref(st, 2)); + script->set_reg(st, sd, num, name, script_getstr(st, 3), ref); else - script->set_reg(st, sd, num, name, (const void *)h64BPTRSIZE(script_getnum(st, 3)), script_getref(st, 2)); + script->set_reg(st, sd, num, name, (const void *)h64BPTRSIZE(script_getnum(st, 3)), ref); return true; } @@ -6520,7 +7207,7 @@ BUILDIN(__setr) /// ex: setarray arr[1],1,2,3; /// /// setarray <array variable>,<value1>{,<value2>...}; -BUILDIN(setarray) +static BUILDIN(setarray) { struct script_data* data; const char* name; @@ -6570,7 +7257,7 @@ BUILDIN(setarray) /// ex: cleararray arr[0],0,1; /// /// cleararray <array variable>,<value>,<count>; -BUILDIN(cleararray) +static BUILDIN(cleararray) { struct script_data* data; const char* name; @@ -6618,7 +7305,7 @@ BUILDIN(cleararray) /// ex: copyarray arr[0],arr[2],2; /// /// copyarray <destination array variable>,<source array variable>,<count>; -BUILDIN(copyarray) +static BUILDIN(copyarray) { struct script_data* data1; struct script_data* data2; @@ -6705,7 +7392,7 @@ BUILDIN(copyarray) /// ex: getarraysize(arr[3]) /// /// getarraysize(<array variable>) -> <int> -BUILDIN(getarraysize) +static BUILDIN(getarraysize) { struct script_data* data; @@ -6722,17 +7409,33 @@ BUILDIN(getarraysize) script_pushint(st, script->array_highest_key(st,st->rid ? script->rid2sd(st) : NULL,reference_getname(data),reference_getref(data))); return true; } -int script_array_index_cmp(const void *a, const void *b) +static int script_array_index_cmp(const void *a, const void *b) { return (*(const unsigned int *)a - *(const unsigned int *)b); // FIXME: Is the unsigned difference really intended here? } +static BUILDIN(getarrayindex) +{ + struct script_data *data = script_getdata(st, 2); + + if (!data_isreference(data) || reference_toconstant(data)) + { + ShowError("script:getarrayindex: not a variable\n"); + script->reportdata(data); + st->state = END; + return false;// not a variable + } + + script_pushint(st, reference_getindex(data)); + return true; +} + /// Deletes count or all the elements in an array, from the starting index. /// ex: deletearray arr[4],2; /// /// deletearray <array variable>; /// deletearray <array variable>,<count>; -BUILDIN(deletearray) +static BUILDIN(deletearray) { struct script_data* data; const char* name; @@ -6846,7 +7549,7 @@ BUILDIN(deletearray) /// Equivalent to var[index]. /// /// getelementofarray(<array variable>,<index>) -> <variable reference> -BUILDIN(getelementofarray) +static BUILDIN(getelementofarray) { struct script_data* data; int32 id; @@ -6865,7 +7568,7 @@ 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); @@ -6884,7 +7587,7 @@ BUILDIN(getelementofarray) /*========================================== * *------------------------------------------*/ -BUILDIN(setlook) +static BUILDIN(setlook) { int type,val; struct map_session_data *sd; @@ -6901,7 +7604,7 @@ BUILDIN(setlook) return true; } -BUILDIN(changelook) +static BUILDIN(changelook) { // As setlook but only client side int type,val; struct map_session_data *sd; @@ -6921,7 +7624,7 @@ BUILDIN(changelook) /*========================================== * *------------------------------------------*/ -BUILDIN(cutin) +static BUILDIN(cutin) { struct map_session_data *sd = script->rid2sd(st); if (sd == NULL) @@ -6934,7 +7637,7 @@ BUILDIN(cutin) /*========================================== * *------------------------------------------*/ -BUILDIN(viewpoint) +static BUILDIN(viewpoint) { int type,x,y,id,color; struct map_session_data *sd; @@ -6957,8 +7660,8 @@ BUILDIN(viewpoint) /*========================================== * *------------------------------------------*/ -BUILDIN(countitem) { - int nameid, i; +static BUILDIN(countitem) +{ int count = 0; struct item_data* id = NULL; @@ -6980,11 +7683,12 @@ 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; @@ -6994,10 +7698,10 @@ BUILDIN(countitem) { * countitem2(nameID,Identified,Refine,Attribute,Card0,Card1,Card2,Card3) [Lupus] * returns number of items that meet the conditions *------------------------------------------*/ -BUILDIN(countitem2) { +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); @@ -7022,12 +7726,12 @@ BUILDIN(countitem2) { iden = script_getnum(st,3); ref = script_getnum(st,4); attr = script_getnum(st,5); - c1 = (short)script_getnum(st,6); - c2 = (short)script_getnum(st,7); - c3 = (short)script_getnum(st,8); - c4 = (short)script_getnum(st,9); + c1 = script_getnum(st,6); + c2 = script_getnum(st,7); + 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 && @@ -7042,13 +7746,72 @@ BUILDIN(countitem2) { } /*========================================== + * countnameditem(item ID, { <Char Name / ID> }) + * returns number of named items. + *------------------------------------------*/ +static BUILDIN(countnameditem) +{ + int count = 0; + struct item_data* id = NULL; + struct map_session_data *sd; + + if (script_hasdata(st, 3)) { + if (script_isstringtype(st, 3)) { + // Character name was given + sd = script->nick2sd(st, script_getstr(st, 3)); + } else { + // Character ID was given + sd = script->charid2sd(st, script_getnum(st, 3)); + } + } else { + // Use RID by default if no name was provided + sd = script->rid2sd(st); + } + + // Player not attached + if (sd == NULL) { + return true; + } + + if (script_isstringtype(st, 2)) { + // Get item from DB via item name + id = itemdb->search_name(script_getstr(st, 2)); + } else { + // Get item from DB via item ID + id = itemdb->exists(script_getnum(st, 2)); + } + + if (id == NULL) { + ShowError("buildin_countnameditem: Invalid item '%s'.\n", script_getstr(st, 2)); // returns string, regardless of what it was + script_pushint(st, 0); + return false; + } + + for (int i = 0; i < MAX_INVENTORY; i++) { + if (sd->status.inventory[i].nameid > 0 && + sd->inventory_data[i] != NULL && + sd->status.inventory[i].amount > 0 && + sd->status.inventory[i].nameid == id->nameid && + sd->status.inventory[i].card[0] == CARD0_CREATE && + sd->status.inventory[i].card[2] == sd->status.char_id && + sd->status.inventory[i].card[3] == sd->status.char_id >> 16) + { + count += sd->status.inventory[i].amount; + } + } + + script_pushint(st, count); + return true; +} + +/*========================================== * Check if item with this amount can fit in inventory * Checking : weight, stack amount >32k, slots amount >(MAX_INVENTORY) * Return * 0 : fail * 1 : success (npc side only) *------------------------------------------*/ -BUILDIN(checkweight) +static BUILDIN(checkweight) { int slots, amount2=0; unsigned int weight=0, i, nbargs; @@ -7130,7 +7893,7 @@ BUILDIN(checkweight) return true; } -BUILDIN(checkweight2) +static BUILDIN(checkweight2) { //variable sub checkweight int i=0, amount2=0, slots=0, weight=0; @@ -7234,7 +7997,8 @@ BUILDIN(checkweight2) * getitembound <item id>,<amount>,<type>{,<account ID>}; * getitembound "<item id>",<amount>,<type>{,<account ID>}; *------------------------------------------*/ -BUILDIN(getitem) { +static BUILDIN(getitem) +{ int nameid,amount,get_count,i,flag = 0, offset = 0; struct item it; struct map_session_data *sd; @@ -7308,7 +8072,7 @@ BUILDIN(getitem) { if ((flag = pc->additem(sd, &it, get_count, LOG_TYPE_SCRIPT))) { clif->additem(sd, 0, 0, flag); if( pc->candrop(sd,&it) ) - map->addflooritem(&sd->bl, &it, get_count, sd->bl.m, sd->bl.x, sd->bl.y, 0, 0, 0, 0); + map->addflooritem(&sd->bl, &it, get_count, sd->bl.m, sd->bl.x, sd->bl.y, 0, 0, 0, 0, false); } } } @@ -7319,7 +8083,7 @@ BUILDIN(getitem) { /*========================================== * *------------------------------------------*/ -BUILDIN(getitem2) +static BUILDIN(getitem2) { int nameid,amount,flag = 0, offset = 0; int iden,ref,attr,c1,c2,c3,c4, bound = 0; @@ -7357,10 +8121,10 @@ BUILDIN(getitem2) iden=script_getnum(st,4); ref=script_getnum(st,5); attr=script_getnum(st,6); - c1=(short)script_getnum(st,7); - c2=(short)script_getnum(st,8); - c3=(short)script_getnum(st,9); - c4=(short)script_getnum(st,10); + c1 = script_getnum(st,7); + c2 = script_getnum(st,8); + c3 = script_getnum(st,9); + c4 = script_getnum(st,10); if (bound && (itemdb_type(nameid) == IT_PETEGG || itemdb_type(nameid) == IT_PETARMOR)) { ShowError("script_getitembound2: can't bind a pet egg/armor! Type=%d\n",bound); @@ -7399,10 +8163,10 @@ BUILDIN(getitem2) item_tmp.refine=ref; item_tmp.attribute=attr; item_tmp.bound=(unsigned char)bound; - item_tmp.card[0]=(short)c1; - item_tmp.card[1]=(short)c2; - item_tmp.card[2]=(short)c3; - item_tmp.card[3]=(short)c4; + item_tmp.card[0] = c1; + item_tmp.card[1] = c2; + item_tmp.card[2] = c3; + item_tmp.card[3] = c4; //Check if it's stackable. if (!itemdb->isstackable(nameid)) @@ -7416,7 +8180,7 @@ BUILDIN(getitem2) if ((flag = pc->additem(sd, &item_tmp, get_count, LOG_TYPE_SCRIPT))) { clif->additem(sd, 0, 0, flag); if( pc->candrop(sd,&item_tmp) ) - map->addflooritem(&sd->bl, &item_tmp, get_count, sd->bl.m, sd->bl.x, sd->bl.y, 0, 0, 0, 0); + map->addflooritem(&sd->bl, &item_tmp, get_count, sd->bl.m, sd->bl.x, sd->bl.y, 0, 0, 0, 0, false); } } } @@ -7429,7 +8193,8 @@ BUILDIN(getitem2) * rentitem <item id>,<seconds> * rentitem "<item name>",<seconds> *------------------------------------------*/ -BUILDIN(rentitem) { +static BUILDIN(rentitem) +{ struct map_session_data *sd; struct item it; int seconds; @@ -7477,7 +8242,8 @@ BUILDIN(rentitem) { * Returned Qty is always 1, only works on equip-able * equipment *------------------------------------------*/ -BUILDIN(getnameditem) { +static BUILDIN(getnameditem) +{ int nameid; struct item item_tmp; struct map_session_data *sd, *tsd; @@ -7517,12 +8283,12 @@ BUILDIN(getnameditem) { } memset(&item_tmp,0,sizeof(item_tmp)); - item_tmp.nameid=nameid; - item_tmp.amount=1; - item_tmp.identify=1; - item_tmp.card[0]=CARD0_CREATE; //we don't use 255! because for example SIGNED WEAPON shouldn't get TOP10 BS Fame bonus [Lupus] - item_tmp.card[2]=tsd->status.char_id; - item_tmp.card[3]=tsd->status.char_id >> 16; + item_tmp.nameid = nameid; + item_tmp.amount = 1; + item_tmp.identify = 1; + item_tmp.card[0] = CARD0_CREATE; //we don't use 255! because for example SIGNED WEAPON shouldn't get TOP10 BS Fame bonus [Lupus] + item_tmp.card[2] = GetWord(tsd->status.char_id, 0); + item_tmp.card[3] = GetWord(tsd->status.char_id, 1); if(pc->additem(sd,&item_tmp,1,LOG_TYPE_SCRIPT)) { script_pushint(st,0); return true; //Failed to add item, we will not drop if they don't fit @@ -7536,7 +8302,8 @@ BUILDIN(getnameditem) { * gets a random item ID from an item group [Skotlex] * groupranditem group_num *------------------------------------------*/ -BUILDIN(grouprandomitem) { +static BUILDIN(grouprandomitem) +{ struct item_data *data; int nameid; @@ -7566,7 +8333,7 @@ BUILDIN(grouprandomitem) { /*========================================== * *------------------------------------------*/ -BUILDIN(makeitem) +static BUILDIN(makeitem) { int nameid,amount; int x,y,m; @@ -7609,7 +8376,91 @@ BUILDIN(makeitem) item_tmp.nameid = nameid; item_tmp.identify=1; - map->addflooritem(NULL, &item_tmp, amount, m, x, y, 0, 0, 0, 0); + map->addflooritem(NULL, &item_tmp, amount, m, x, y, 0, 0, 0, 0, false); + + return true; +} + +/*========================================== + * makeitem2 <item id>, <amount>, <identify>, <refine>, <attribute>, <card1>, <card2>, <card3>, <card4>, {"<map name>", <X>, <Y>, <range>}; + *------------------------------------------*/ +static BUILDIN(makeitem2) +{ + struct map_session_data *sd = NULL; + struct item_data *i_data; + int nameid = 0, amount; + int16 x, y, m = -1, range; + struct item item_tmp; + + if (script_isstringtype(st, 2)) { + const char *name = script_getstr(st, 2); + struct item_data *item_data = itemdb->search_name(name); + if (item_data != NULL) + nameid = item_data->nameid; + } else { + nameid = script_getnum(st, 2); + } + + i_data = itemdb->exists(nameid); + if (i_data == NULL) { + ShowError("makeitem2: Unknown item %d requested.\n", nameid); + return true; + } + + if (script_hasdata(st, 11)) { + m = map->mapname2mapid(script_getstr(st, 11)); + } else { + sd = script->rid2sd(st); + if (sd == NULL) + return true; + m = sd->bl.m; + } + + if (m == -1) { + ShowError("makeitem2: Nonexistant map requested.\n"); + return true; + } + + x = (script_hasdata(st, 12) ? script_getnum(st, 12) : 0); + y = (script_hasdata(st, 13) ? script_getnum(st, 13) : 0); + + // pick random position on map + if (x <= 0 || x >= map->list[m].xs || y <= 0 || y >= map->list[m].ys) { + sd = map->id2sd(st->rid); + if ((x < 0 || y < 0) && sd == NULL) { + x = 0; + y = 0; + map->search_freecell(NULL, m, &x, &y, -1, -1, 1); + } else { + range = (script_hasdata(st, 14) ? cap_value(script_getnum(st, 14), 1, battle_config.area_size) : 3); + map->search_freecell(&sd->bl, sd->bl.m, &x, &y, range, range, 0); // Locate spot next to player. + } + } + + // if equip or weapon or egg type only drop one. + switch (i_data->type) { + case IT_ARMOR: + case IT_WEAPON: + case IT_PETARMOR: + case IT_PETEGG: + amount = 1; + break; + default: + amount = cap_value(script_getnum(st, 3), 1, MAX_AMOUNT); + break; + } + + memset(&item_tmp, 0, sizeof(item_tmp)); + item_tmp.nameid = nameid; + item_tmp.identify = script_getnum(st, 4); + item_tmp.refine = cap_value(script_getnum(st, 5), 0, MAX_REFINE); + item_tmp.attribute = script_getnum(st, 6); + item_tmp.card[0] = script_getnum(st, 7); + item_tmp.card[1] = script_getnum(st, 8); + item_tmp.card[2] = script_getnum(st, 9); + item_tmp.card[3] = script_getnum(st, 10); + + map->addflooritem(NULL, &item_tmp, amount, m, x, y, 0, 0, 0, 0, false); return true; } @@ -7617,10 +8468,14 @@ BUILDIN(makeitem) /// Counts / deletes the current item given by idx. /// Used by buildin_delitem_search /// Relies on all input data being already fully valid. -void buildin_delitem_delete(struct map_session_data* sd, int idx, int* amount, bool delete_items) +static void buildin_delitem_delete(struct map_session_data *sd, int idx, int *amount, bool delete_items) { int delamount; - struct item* inv = &sd->status.inventory[idx]; + struct item* inv; + + nullpo_retv(sd); + nullpo_retv(amount); + inv = &sd->status.inventory[idx]; delamount = ( amount[0] < inv->amount ) ? amount[0] : inv->amount; @@ -7641,12 +8496,14 @@ void buildin_delitem_delete(struct map_session_data* sd, int idx, int* amount, b /// Relies on all input data being already fully valid. /// @param exact_match will also match item attributes and cards, not just name id /// @return true when all items could be deleted, false when there were not enough items to delete -bool buildin_delitem_search(struct map_session_data* sd, struct item* it, bool exact_match) +static bool buildin_delitem_search(struct map_session_data *sd, struct item *it, bool exact_match) { bool delete_items = false; int i, amount; struct item* inv; + nullpo_retr(false, sd); + nullpo_retr(false, it); // prefer always non-equipped items it->equip = 0; @@ -7748,7 +8605,7 @@ bool buildin_delitem_search(struct map_session_data* sd, struct item* it, bool e /// /// delitem <item id>,<amount>{,<account id>} /// delitem "<item name>",<amount>{,<account id>} -BUILDIN(delitem) +static BUILDIN(delitem) { struct map_session_data *sd; struct item it; @@ -7805,7 +8662,7 @@ BUILDIN(delitem) /// /// delitem2 <item id>,<amount>,<identify>,<refine>,<attribute>,<card1>,<card2>,<card3>,<card4>{,<account ID>} /// delitem2 "<Item name>",<amount>,<identify>,<refine>,<attribute>,<card1>,<card2>,<card3>,<card4>{,<account ID>} -BUILDIN(delitem2) +static BUILDIN(delitem2) { struct map_session_data *sd; struct item it; @@ -7846,10 +8703,10 @@ BUILDIN(delitem2) it.identify=script_getnum(st,4); it.refine=script_getnum(st,5); it.attribute=script_getnum(st,6); - it.card[0]=(short)script_getnum(st,7); - it.card[1]=(short)script_getnum(st,8); - it.card[2]=(short)script_getnum(st,9); - it.card[3]=(short)script_getnum(st,10); + it.card[0] = script_getnum(st, 7); + it.card[1] = script_getnum(st, 8); + it.card[2] = script_getnum(st, 9); + it.card[3] = script_getnum(st, 10); if( it.amount <= 0 ) return true;// nothing to do @@ -7865,10 +8722,52 @@ BUILDIN(delitem2) return false; } +/** + * Deletes item at given index. + * delitem(<index>{, <amount{, <account id>}}); + */ +static BUILDIN(delitemidx) +{ + struct map_session_data *sd; + + if (script_hasdata(st, 4)) { + if ((sd = script->id2sd(st, script_getnum(st, 4))) == NULL) { + st->state = END; + return true; + } + } else { + if ((sd = script->rid2sd(st)) == NULL) + return true; + } + + int i = script_getnum(st, 2); + if (i < 0 || i >= sd->status.inventorySize) { + ShowError("buildin_delitemidx: Index (%d) should be from 0-%d.\n", i, sd->status.inventorySize - 1); + st->state = END; + return false; + } + + if (itemdb->exists(sd->status.inventory[i].nameid) == NULL) + ShowWarning("buildin_delitemidx: Deleting invalid Item ID (%d).\n", sd->status.inventory[i].nameid); + + int amount = 0; + if (script_hasdata(st, 3)) { + if ((amount = script_getnum(st, 3)) > sd->status.inventory[i].amount) + amount = sd->status.inventory[i].amount; + } else { + amount = sd->status.inventory[i].amount; + } + + if (amount > 0) + script->buildin_delitem_delete(sd, i, &amount, true); + + return true; +} + /*========================================== * Enables/Disables use of items while in an NPC [Skotlex] *------------------------------------------*/ -BUILDIN(enableitemuse) +static BUILDIN(enableitemuse) { struct map_session_data *sd = script->rid2sd(st); if (sd != NULL) @@ -7876,7 +8775,7 @@ BUILDIN(enableitemuse) return true; } -BUILDIN(disableitemuse) +static BUILDIN(disableitemuse) { struct map_session_data *sd = script->rid2sd(st); if (sd != NULL) @@ -7888,23 +8787,71 @@ BUILDIN(disableitemuse) * return the basic stats of sd * chk pc->readparam for available type *------------------------------------------*/ -BUILDIN(readparam) { +static BUILDIN(readparam) +{ int type; struct map_session_data *sd; + struct script_data *data = script_getdata(st, 2); - type=script_getnum(st,2); - if (script_hasdata(st,3)) - sd = script->nick2sd(st, script_getstr(st,3)); - else - sd=script->rid2sd(st); + if (reference_toparam(data)) { + type = reference_getparamtype(data); + } else { + type = script->conv_num(st, data); + } + + if (script_hasdata(st, 3)) { + if (script_isstringtype(st, 3)) { + sd = script->nick2sd(st, script_getstr(st, 3)); + } else { + sd = script->id2sd(st, script_getnum(st, 3)); + } + } else { + sd = script->rid2sd(st); + } if (sd == NULL) { - script_pushint(st,-1); + script_pushint(st, -1); return true; } - script_pushint(st,pc->readparam(sd,type)); + script_pushint(st, pc->readparam(sd, type)); + return true; +} + +static BUILDIN(setparam) +{ + int type; + struct map_session_data *sd; + struct script_data *data = script_getdata(st, 2); + int val = script_getnum(st, 3); + + if (data_isreference(data) && reference_toparam(data)) { + type = reference_getparamtype(data); + } else { + type = script->conv_num(st, data); + } + + 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) { + script_pushint(st, 0); + return true; + } + if (pc->setparam(sd, type, val) == 0) { + script_pushint(st, 0); + return false; + } + + script_pushint(st, 1); return true; } @@ -7916,62 +8863,67 @@ BUILDIN(readparam) { * 2 : guild_id * 3 : account_id * 4 : bg_id + * 5 : clan_id *------------------------------------------*/ -BUILDIN(getcharid) { - int num; +static BUILDIN(getcharid) +{ + int num = script_getnum(st, 2); struct map_session_data *sd; - num = script_getnum(st,2); - if( script_hasdata(st,3) ) - sd=map->nick2sd(script_getstr(st,3)); + if (script_hasdata(st, 3)) + sd = map->nick2sd(script_getstr(st, 3), false); else - sd=script->rid2sd(st); + sd = script->rid2sd(st); - if(sd==NULL) { - script_pushint(st,0); //return 0, according docs + if (sd == NULL) { + script_pushint(st, 0); //return 0, according docs return true; } - switch( num ) { - case 0: script_pushint(st,sd->status.char_id); break; - case 1: script_pushint(st,sd->status.party_id); break; - case 2: script_pushint(st,sd->status.guild_id); break; - case 3: script_pushint(st,sd->status.account_id); break; - case 4: script_pushint(st,sd->bg_id); break; - default: - ShowError("buildin_getcharid: invalid parameter (%d).\n", num); - script_pushint(st,0); - break; + switch (num) { + case 0: + script_pushint(st, sd->status.char_id); + break; + case 1: + script_pushint(st, sd->status.party_id); + break; + case 2: + script_pushint(st, sd->status.guild_id); + break; + case 3: + script_pushint(st, sd->status.account_id); + break; + case 4: + script_pushint(st, sd->bg_id); + break; + case 5: + script_pushint(st, sd->status.clan_id); + break; + default: + ShowError("buildin_getcharid: invalid parameter (%d).\n", num); + script_pushint(st, 0); + break; } return true; } + /*========================================== * returns the GID of an NPC *------------------------------------------*/ -BUILDIN(getnpcid) +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; @@ -7981,7 +8933,7 @@ BUILDIN(getnpcid) * Return the name of the party_id * null if not found *------------------------------------------*/ -BUILDIN(getpartyname) +static BUILDIN(getpartyname) { int party_id; struct party_data* p; @@ -8007,7 +8959,7 @@ BUILDIN(getpartyname) * 1 : char_id des membres * 2 : account_id des membres *------------------------------------------*/ -BUILDIN(getpartymember) +static BUILDIN(getpartymember) { struct party_data *p; int j=0,type=0; @@ -8023,19 +8975,19 @@ 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; } @@ -8044,7 +8996,7 @@ BUILDIN(getpartymember) * Retrieves party leader. if flag is specified, * return some of the leader data. Otherwise, return name. *------------------------------------------*/ -BUILDIN(getpartyleader) +static BUILDIN(getpartyleader) { int party_id, type = 0, i=0; struct party_data *p; @@ -8069,7 +9021,7 @@ BUILDIN(getpartyleader) switch (type) { case 1: script_pushint(st,p->party.member[i].account_id); break; case 2: script_pushint(st,p->party.member[i].char_id); break; - case 3: script_pushint(st,p->party.member[i].class_); break; + case 3: script_pushint(st,p->party.member[i].class); break; case 4: script_pushstrcopy(st,mapindex_id2name(p->party.member[i].map)); break; case 5: script_pushint(st,p->party.member[i].lv); break; default: script_pushstrcopy(st,p->party.member[i].name); break; @@ -8077,11 +9029,98 @@ BUILDIN(getpartyleader) return true; } +enum guildinfo_type { + GUILDINFO_NAME, + GUILDINFO_ID, + GUILDINFO_LEVEL, + GUILDINFO_ONLINE, + GUILDINFO_AV_LEVEL, + GUILDINFO_MAX_MEMBERS, + GUILDINFO_EXP, + GUILDINFO_NEXT_EXP, + GUILDINFO_SKILL_POINTS, + GUILDINFO_MASTER_NAME, + GUILDINFO_MASTER_CID, +}; + +static BUILDIN(getguildinfo) +{ + struct guild *g = NULL; + + if (script_hasdata(st, 3)) { + if (script_isstringtype(st, 3)) { + const char *guild_name = script_getstr(st, 3); + g = guild->searchname(guild_name); + } else { + int guild_id = script_getnum(st, 3); + g = guild->search(guild_id); + } + } else { + struct map_session_data *sd = script->rid2sd(st); + g = sd ? sd->guild : NULL; + } + + enum guildinfo_type type = script_getnum(st, 2); + + if (g == NULL) { + // guild does not exist + switch (type) { + case GUILDINFO_NAME: + case GUILDINFO_MASTER_NAME: + script_pushconststr(st, ""); + break; + default: + script_pushint(st, -1); + } + } else { + switch (type) { + case GUILDINFO_NAME: + script_pushstrcopy(st, g->name); + break; + case GUILDINFO_ID: + script_pushint(st, g->guild_id); + break; + case GUILDINFO_LEVEL: + script_pushint(st, g->guild_lv); + break; + case GUILDINFO_ONLINE: + script_pushint(st, g->connect_member); + break; + case GUILDINFO_AV_LEVEL: + script_pushint(st, g->average_lv); + break; + case GUILDINFO_MAX_MEMBERS: + script_pushint(st, g->max_member); + break; + case GUILDINFO_EXP: + script_pushint(st, g->exp); + break; + case GUILDINFO_NEXT_EXP: + script_pushint(st, g->next_exp); + break; + case GUILDINFO_SKILL_POINTS: + script_pushint(st, g->skill_point); + break; + case GUILDINFO_MASTER_NAME: + script_pushstrcopy(st, g->member[0].name); + break; + case GUILDINFO_MASTER_CID: + script_pushint(st, g->member[0].char_id); + break; + default: + ShowError("script:getguildinfo: unknown info type!\n"); + st->state = END; + return false; + } + } + return true; +} + /*========================================== * Return the name of the @guild_id * null if not found *------------------------------------------*/ -BUILDIN(getguildname) +static BUILDIN(getguildname) { int guild_id; struct guild* g; @@ -8103,7 +9142,7 @@ BUILDIN(getguildname) * Return the name of the guild master of @guild_id * null if not found *------------------------------------------*/ -BUILDIN(getguildmaster) +static BUILDIN(getguildmaster) { int guild_id; struct guild* g; @@ -8121,7 +9160,7 @@ BUILDIN(getguildmaster) return true; } -BUILDIN(getguildmasterid) +static BUILDIN(getguildmasterid) { int guild_id; struct guild* g; @@ -8148,7 +9187,7 @@ BUILDIN(getguildmasterid) * 1 : character ID * 2 : account ID *------------------------------------------*/ -BUILDIN(getguildmember) +static BUILDIN(getguildmember) { struct guild *g = NULL; int j = 0; @@ -8165,20 +9204,77 @@ 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; +} + +/** + * getguildonline(<Guild ID>{, type}) + * Returns amount of guild members online. +**/ + +enum script_getguildonline_types { + GUILD_ONLINE_ALL = 0, + GUILD_ONLINE_VENDOR, + GUILD_ONLINE_NO_VENDOR +}; + +BUILDIN(getguildonline) +{ + struct guild *g; + int guild_id = script_getnum(st, 2); + int type = GUILD_ONLINE_ALL, j = 0; + + if ((g = guild->search(guild_id)) == NULL) { + script_pushint(st, -1); + return true; + } + + if (script_hasdata(st, 3)) { + type = script_getnum(st, 3); + + if (type < GUILD_ONLINE_ALL || type > GUILD_ONLINE_NO_VENDOR) { + ShowWarning("buildin_getguildonline: Invalid type specified. Defaulting to GUILD_ONLINE_ALL.\n"); + type = GUILD_ONLINE_ALL; + } + } + + struct map_session_data *sd; + for (int i = 0; i < MAX_GUILD; i++) { + if (g->member[i].online && (sd = g->member[i].sd) != NULL) { + switch (type) { + case GUILD_ONLINE_VENDOR: + if (sd->state.vending > 0) + j++; + break; + + case GUILD_ONLINE_NO_VENDOR: + if (sd->state.vending == 0) + j++; + break; + + default: + j++; + break; + } + } + } + + script_pushint(st, j); + return true; } @@ -8189,43 +9285,61 @@ BUILDIN(getguildmember) * 1 : party_name or "" * 2 : guild_name or "" * 3 : map_name + * 4 : clan_name or "" * - : "" *------------------------------------------*/ -BUILDIN(strcharinfo) +static BUILDIN(strcharinfo) { - int num; + struct clan *c; struct guild* g; struct party_data* p; - struct map_session_data *sd = script->rid2sd(st); - if (sd == NULL) //Avoid crashing.... + struct map_session_data *sd; + + if (script_hasdata(st, 4)) + sd = map->id2sd(script_getnum(st, 4)); + else + sd = script->rid2sd(st); + + if (sd == NULL) { + if(script_hasdata(st, 3)) { + script_pushcopy(st, 3); + } else { + script_pushconststr(st, ""); + } return true; + } - num=script_getnum(st,2); - switch(num) { - case 0: - script_pushstrcopy(st,sd->status.name); - break; - case 1: - if( ( p = party->search(sd->status.party_id) ) != NULL ) { - script_pushstrcopy(st,p->party.name); - } else { - script_pushconststr(st,""); - } - break; - case 2: - if( ( g = sd->guild ) != NULL ) { - script_pushstrcopy(st,g->name); - } else { - script_pushconststr(st,""); - } - break; - case 3: - script_pushconststr(st,map->list[sd->bl.m].name); - break; - default: - ShowWarning("buildin_strcharinfo: unknown parameter.\n"); - script_pushconststr(st,""); - break; + switch (script_getnum(st, 2)) { + case 0: + script_pushstrcopy(st, sd->status.name); + break; + case 1: + if ((p = party->search(sd->status.party_id)) != NULL) { + script_pushstrcopy(st, p->party.name); + } else { + script_pushconststr(st, ""); + } + break; + case 2: + if ((g = sd->guild) != NULL) { + script_pushstrcopy(st, g->name); + } else { + script_pushconststr(st, ""); + } + break; + case 3: + script_pushconststr(st, map->list[sd->bl.m].name); + break; + case 4: + if ((c = sd->clan) != NULL) { + script_pushstrcopy(st, c->name); + } else { + script_pushconststr(st, ""); + } + break; + default: + ShowWarning("script:strcharinfo: unknown parameter.\n"); + script_pushconststr(st, ""); } return true; @@ -8240,43 +9354,53 @@ BUILDIN(strcharinfo) * 3 : ::str * 4 : map name *------------------------------------------*/ -BUILDIN(strnpcinfo) +static BUILDIN(strnpcinfo) { - int num; char *buf,*name=NULL; - struct npc_data *nd = map->id2nd(st->oid); + struct npc_data *nd; + + if (script_hasdata(st, 4)) + nd = map->id2nd(script_getnum(st, 4)); + else + nd = map->id2nd(st->oid); + if (nd == NULL) { - script_pushconststr(st, ""); + if (script_hasdata(st, 3)) { + script_pushcopy(st, 3); + } else { + script_pushconststr(st, ""); + } return true; } - num = script_getnum(st,2); - switch(num) { - case 0: // display name + switch (script_getnum(st,2)) { + case 0: // display name + name = aStrdup(nd->name); + break; + case 1: // visible part of display name + if ((buf = strchr(nd->name,'#')) != NULL) { name = aStrdup(nd->name); - break; - case 1: // visible part of display name - if((buf = strchr(nd->name,'#')) != NULL) - { - name = aStrdup(nd->name); - name[buf - nd->name] = 0; - } else // Return the name, there is no '#' present - name = aStrdup(nd->name); - break; - case 2: // # fragment - if((buf = strchr(nd->name,'#')) != NULL) - name = aStrdup(buf+1); - break; - case 3: // unique name - name = aStrdup(nd->exname); - break; - case 4: // map name - if( nd->bl.m >= 0 ) // Only valid map indexes allowed (issue:8034) - name = aStrdup(map->list[nd->bl.m].name); - break; + name[buf - nd->name] = 0; + } else { // Return the name, there is no '#' present + name = aStrdup(nd->name); + } + break; + case 2: // # fragment + if ((buf = strchr(nd->name,'#')) != NULL) { + name = aStrdup(buf+1); + } + break; + case 3: // unique name + name = aStrdup(nd->exname); + break; + case 4: // map name + if (nd->bl.m >= 0) { // Only valid map indexes allowed (issue:8034) + name = aStrdup(map->list[nd->bl.m].name); + } + break; } - if(name) + if (name) script_pushstr(st, name); else script_pushconststr(st, ""); @@ -8287,7 +9411,7 @@ BUILDIN(strnpcinfo) /** * charid2rid: Returns the RID associated to the given character ID */ -BUILDIN(charid2rid) +static BUILDIN(charid2rid) { int cid = script_getnum(st, 2); struct map_session_data *sd = map->charid2sd(cid); @@ -8304,7 +9428,7 @@ BUILDIN(charid2rid) /*========================================== * GetEquipID(Pos); Pos: 1-SCRIPT_EQUIP_TABLE_SIZE *------------------------------------------*/ -BUILDIN(getequipid) +static BUILDIN(getequipid) { int i, num; struct item_data* item; @@ -8340,7 +9464,7 @@ BUILDIN(getequipid) * Get the equipement name at pos * return item jname or "" *------------------------------------------*/ -BUILDIN(getequipname) +static BUILDIN(getequipname) { int i, num; struct item_data* item; @@ -8375,16 +9499,18 @@ BUILDIN(getequipname) /*========================================== * getbrokenid [Valaris] *------------------------------------------*/ -BUILDIN(getbrokenid) +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++) { - if(sd->status.inventory[i].attribute) { + 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) { id=sd->status.inventory[i].nameid; @@ -8401,15 +9527,17 @@ BUILDIN(getbrokenid) /*========================================== * getbrokencount *------------------------------------------*/ -BUILDIN(getbrokencount) +static BUILDIN(getbrokencount) { int i, counter = 0; struct map_session_data *sd = script->rid2sd(st); if (sd == NULL) return true; - for (i = 0; i < MAX_INVENTORY; i++) { - if (sd->status.inventory[i].attribute) + 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++; } @@ -8421,21 +9549,23 @@ BUILDIN(getbrokencount) /*========================================== * repair [Valaris] *------------------------------------------*/ -BUILDIN(repair) +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++) { - if(sd->status.inventory[i].attribute) { + 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=0; - clif->equiplist(sd); + sd->status.inventory[i].attribute |= ATTR_BROKEN; + sd->status.inventory[i].attribute ^= ATTR_BROKEN; + clif->equipList(sd); clif->produce_effect(sd, 0, sd->status.inventory[i].nameid); clif->misceffect(&sd->bl, 3); break; @@ -8449,18 +9579,21 @@ BUILDIN(repair) /*========================================== * repairall *------------------------------------------*/ -BUILDIN(repairall) +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].nameid && sd->status.inventory[i].attribute) + 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 = 0; + sd->status.inventory[i].attribute |= ATTR_BROKEN; + sd->status.inventory[i].attribute ^= ATTR_BROKEN; clif->produce_effect(sd,0,sd->status.inventory[i].nameid); repaircounter++; } @@ -8469,7 +9602,7 @@ BUILDIN(repairall) if(repaircounter) { clif->misceffect(&sd->bl, 3); - clif->equiplist(sd); + clif->equipList(sd); } return true; @@ -8478,7 +9611,7 @@ BUILDIN(repairall) /*========================================== * Chk if player have something equiped at pos *------------------------------------------*/ -BUILDIN(getequipisequiped) +static BUILDIN(getequipisequiped) { int i = -1,num; struct map_session_data *sd; @@ -8505,7 +9638,7 @@ BUILDIN(getequipisequiped) * 1 : true * 0 : false *------------------------------------------*/ -BUILDIN(getequipisenableref) +static BUILDIN(getequipisenableref) { int i = -1,num; struct map_session_data *sd; @@ -8525,13 +9658,42 @@ BUILDIN(getequipisenableref) return true; } +/** + * Checks if the equipped item allows options. + * *getequipisenableopt(<equipment_index>); + * + * @param equipment_index as the inventory index of the equipment. + * @return 1 on enabled 0 on disabled. + */ +static BUILDIN(getequipisenableopt) +{ + int i = -1, index = script_getnum(st, 2); + struct map_session_data *sd = script->rid2sd(st); + + if (sd == NULL) { + script_pushint(st, -1); + ShowError("buildin_getequipisenableopt: player is not attached!"); + return false; + } + + if (index > 0 && index <= ARRAYLENGTH(script->equip)) + i = pc->checkequip(sd, script->equip[index - 1]); + + if (i >=0 && sd->inventory_data[i] && !sd->inventory_data[i]->flag.no_options && !sd->status.inventory[i].expire_time) + script_pushint(st, 1); + else + script_pushint(st, 0); + + return true; +} + /*========================================== * Chk if the item equiped at pos is identify (huh ?) * return (npc) * 1 : true * 0 : false *------------------------------------------*/ -BUILDIN(getequipisidentify) +static BUILDIN(getequipisidentify) { int i = -1,num; struct map_session_data *sd; @@ -8552,27 +9714,29 @@ BUILDIN(getequipisidentify) } /*========================================== - * Get the item refined value at pos - * return (npc) - * x : refine amount - * 0 : false (not refined) + * Get the total refine amount of equip at given pos + * return total refine amount *------------------------------------------*/ -BUILDIN(getequiprefinerycnt) +static BUILDIN(getequiprefinerycnt) { - int i = -1,num; - struct map_session_data *sd; + int total_refine = 0; + struct map_session_data* sd = script->rid2sd(st); - num = script_getnum(st,2); - sd = script->rid2sd(st); - if( sd == NULL ) - return true; + if (sd != NULL) + { + int count = script_lastdata(st); + int script_equip_size = ARRAYLENGTH(script->equip); + for (int n = 2; n <= count; n++) { + int i = -1; + int num = script_getnum(st, n); + if (num > 0 && num <= script_equip_size) + i = pc->checkequip(sd, script->equip[num - 1]); - if (num > 0 && num <= ARRAYLENGTH(script->equip)) - i=pc->checkequip(sd,script->equip[num-1]); - if(i >= 0) - script_pushint(st,sd->status.inventory[i].refine); - else - script_pushint(st,0); + if (i >= 0) + total_refine += sd->status.inventory[i].refine; + } + } + script_pushint(st, total_refine); return true; } @@ -8584,7 +9748,7 @@ BUILDIN(getequiprefinerycnt) * x : weapon level * 0 : false *------------------------------------------*/ -BUILDIN(getequipweaponlv) +static BUILDIN(getequipweaponlv) { int i = -1,num; struct map_session_data *sd; @@ -8610,21 +9774,34 @@ BUILDIN(getequipweaponlv) * x : refine chance * 0 : false (max refine level or unequip..) *------------------------------------------*/ -BUILDIN(getequippercentrefinery) { - int i = -1,num; +static BUILDIN(getequippercentrefinery) +{ + int i = -1, num; struct map_session_data *sd; + int type = 0; + + num = script_getnum(st, 2); + type = (script_hasdata(st, 3)) ? script_getnum(st, 3) : REFINE_CHANCE_TYPE_NORMAL; - num = script_getnum(st,2); sd = script->rid2sd(st); - if( sd == NULL ) + if (sd == NULL) return true; + if (type < REFINE_CHANCE_TYPE_NORMAL || type >= REFINE_CHANCE_TYPE_MAX) { + ShowError("buildin_getequippercentrefinery: Invalid type (%d) provided!\n", type); + script_pushint(st, 0); + return false; + } + + if (num > 0 && num <= ARRAYLENGTH(script->equip)) - i=pc->checkequip(sd,script->equip[num-1]); - if(i >= 0 && sd->status.inventory[i].nameid && sd->status.inventory[i].refine < MAX_REFINE) - script_pushint(st,status->get_refine_chance(itemdb_wlv(sd->status.inventory[i].nameid), (int)sd->status.inventory[i].refine)); + i = pc->checkequip(sd, script->equip[num - 1]); + + if (i >= 0 && sd->status.inventory[i].nameid != 0 && sd->status.inventory[i].refine < MAX_REFINE) + script_pushint(st, + refine->get_refine_chance(itemdb_wlv(sd->status.inventory[i].nameid), (int) sd->status.inventory[i].refine, (enum refine_chance_type) type)); else - script_pushint(st,0); + script_pushint(st, 0); return true; } @@ -8632,7 +9809,7 @@ BUILDIN(getequippercentrefinery) { /*========================================== * Refine +1 item at pos and log and display refine *------------------------------------------*/ -BUILDIN(successrefitem) +static BUILDIN(successrefitem) { int i = -1 , num, up = 1; struct map_session_data *sd; @@ -8669,20 +9846,25 @@ 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]) ) { // Fame point system [DracoRPG] switch (sd->inventory_data[i]->wlv) { - case 1: - pc->addfame(sd,1); // Success to refine to +10 a lv1 weapon you forged = +1 fame point - break; - case 2: - pc->addfame(sd,25); // Success to refine to +10 a lv2 weapon you forged = +25 fame point - break; - case 3: - pc->addfame(sd,1000); // Success to refine to +10 a lv3 weapon you forged = +1000 fame point - break; + case 1: + pc->addfame(sd, RANKTYPE_BLACKSMITH, 1); // Success to refine to +10 a lv1 weapon you forged = +1 fame point + break; + case 2: + pc->addfame(sd, RANKTYPE_BLACKSMITH, 25); // Success to refine to +10 a lv2 weapon you forged = +25 fame point + break; + case 3: + pc->addfame(sd, RANKTYPE_BLACKSMITH, 1000); // Success to refine to +10 a lv3 weapon you forged = +1000 fame point + break; } } } @@ -8693,7 +9875,7 @@ BUILDIN(successrefitem) /*========================================== * Show a failed Refine +1 attempt *------------------------------------------*/ -BUILDIN(failedrefitem) +static BUILDIN(failedrefitem) { int i=-1,num; struct map_session_data *sd; @@ -8706,6 +9888,9 @@ 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 @@ -8721,7 +9906,7 @@ BUILDIN(failedrefitem) /*========================================== * Downgrades an Equipment Part by -1 . [Masao] *------------------------------------------*/ -BUILDIN(downrefitem) +static BUILDIN(downrefitem) { int i = -1, num, down = 1; struct map_session_data *sd; @@ -8753,6 +9938,9 @@ 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); } @@ -8762,7 +9950,7 @@ BUILDIN(downrefitem) /*========================================== * Delete the item equipped at pos. *------------------------------------------*/ -BUILDIN(delequip) +static BUILDIN(delequip) { int i=-1,num; struct map_session_data *sd; @@ -8790,7 +9978,8 @@ BUILDIN(delequip) /*========================================== * *------------------------------------------*/ -BUILDIN(statusup) { +static BUILDIN(statusup) +{ int type; struct map_session_data *sd; @@ -8806,7 +9995,7 @@ BUILDIN(statusup) { /*========================================== * *------------------------------------------*/ -BUILDIN(statusup2) +static BUILDIN(statusup2) { int type,val; struct map_session_data *sd; @@ -8822,6 +10011,25 @@ BUILDIN(statusup2) return true; } + +/*========================================== +* Returns the number of stat points needed to change the specified stat by val. +* needed_status_point(<type>,<val>); [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>; @@ -8829,7 +10037,8 @@ BUILDIN(statusup2) /// bonus3 <bonus type>,<val1>,<val2>,<val3>; /// bonus4 <bonus type>,<val1>,<val2>,<val3>,<val4>; /// bonus5 <bonus type>,<val1>,<val2>,<val3>,<val4>,<val5>; -BUILDIN(bonus) { +static BUILDIN(bonus) +{ int type; int val1; int val2 = 0; @@ -8863,7 +10072,7 @@ BUILDIN(bonus) { val1 = skill->name2id(script_getstr(st, 3)); break; } - // else fall through + FALLTHROUGH default: val1 = script_getnum(st,3); break; @@ -8911,7 +10120,8 @@ BUILDIN(bonus) { return true; } -BUILDIN(autobonus) { +static BUILDIN(autobonus) +{ unsigned int dur; short rate; short atk_type = 0; @@ -8920,7 +10130,7 @@ BUILDIN(autobonus) { if (sd == NULL) return true; // no player attached - if( sd->state.autobonus&sd->status.inventory[status->current_equip_item_index].equip ) + if (status->current_equip_item_index < 0 || sd->state.autobonus&sd->status.inventory[status->current_equip_item_index].equip) return true; rate = script_getnum(st,3); @@ -8945,7 +10155,8 @@ BUILDIN(autobonus) { return true; } -BUILDIN(autobonus2) { +static BUILDIN(autobonus2) +{ unsigned int dur; short rate; short atk_type = 0; @@ -8954,7 +10165,7 @@ BUILDIN(autobonus2) { if (sd == NULL) return true; // no player attached - if( sd->state.autobonus&sd->status.inventory[status->current_equip_item_index].equip ) + if (status->current_equip_item_index < 0 || sd->state.autobonus&sd->status.inventory[status->current_equip_item_index].equip) return true; rate = script_getnum(st,3); @@ -8979,7 +10190,8 @@ BUILDIN(autobonus2) { return true; } -BUILDIN(autobonus3) { +static BUILDIN(autobonus3) +{ unsigned int dur; short rate,atk_type; const char *bonus_script, *other_script = NULL; @@ -8987,7 +10199,7 @@ BUILDIN(autobonus3) { if (sd == NULL) return true; // no player attached - if( sd->state.autobonus&sd->status.inventory[status->current_equip_item_index].equip ) + if (status->current_equip_item_index < 0 || sd->state.autobonus&sd->status.inventory[status->current_equip_item_index].equip) return true; rate = script_getnum(st,3); @@ -9021,7 +10233,8 @@ BUILDIN(autobonus3) { /// skill <skill id>,<level> /// skill "<skill name>",<level>,<flag> /// skill "<skill name>",<level> -BUILDIN(skill) { +static BUILDIN(skill) +{ int id; int level; int flag = SKILL_GRANT_TEMPORARY; @@ -9047,7 +10260,8 @@ BUILDIN(skill) { /// addtoskill "<skill name>",<amount> /// /// @see skill -BUILDIN(addtoskill) { +static BUILDIN(addtoskill) +{ int id; int level; int flag = SKILL_GRANT_TEMPSTACK; @@ -9068,7 +10282,8 @@ BUILDIN(addtoskill) { /// /// guildskill <skill id>,<amount>; /// guildskill "<skill name>",<amount>; -BUILDIN(guildskill) { +static BUILDIN(guildskill) +{ int skill_id, id, max_points; int level; @@ -9084,6 +10299,9 @@ BUILDIN(guildskill) { skill_id = ( script_isstringtype(st,2) ? skill->name2id(script_getstr(st,2)) : script_getnum(st,2) ); level = script_getnum(st,3); + if (skill_id < GD_SKILLBASE || skill_id >= GD_MAX) + return true; // not guild skill + id = skill_id - GD_SKILLBASE; max_points = guild->skill_get_max(skill_id); @@ -9104,7 +10322,7 @@ BUILDIN(guildskill) { /// /// getskilllv(<skill id>) -> <level> /// getskilllv("<skill name>") -> <level> -BUILDIN(getskilllv) +static BUILDIN(getskilllv) { int id; struct map_session_data *sd = script->rid2sd(st); @@ -9121,7 +10339,8 @@ BUILDIN(getskilllv) /// /// getgdskilllv(<guild id>,<skill id>) -> <level> /// getgdskilllv(<guild id>,"<skill name>") -> <level> -BUILDIN(getgdskilllv) { +static BUILDIN(getgdskilllv) +{ int guild_id; uint16 skill_id; struct guild* g; @@ -9142,7 +10361,7 @@ BUILDIN(getgdskilllv) { /// before allowing the basic actions. /// /// basicskillcheck() -> <bool> -BUILDIN(basicskillcheck) +static BUILDIN(basicskillcheck) { script_pushint(st, battle_config.basic_skill_check); return true; @@ -9151,7 +10370,7 @@ BUILDIN(basicskillcheck) /// Returns the GM level of the player. /// /// getgmlevel() -> <level> -BUILDIN(getgmlevel) +static BUILDIN(getgmlevel) { struct map_session_data *sd = script->rid2sd(st); if (sd == NULL) @@ -9165,7 +10384,8 @@ BUILDIN(getgmlevel) /// set the group ID of the player. /// setgroupid(<new group id>{,"<character name>"|<account id>}) /// return 1 on success, 0 if failed. -BUILDIN(setgroupid) { +static BUILDIN(setgroupid) +{ struct map_session_data* sd = NULL; int new_group = script_getnum(st, 2); @@ -9188,21 +10408,31 @@ BUILDIN(setgroupid) { /// Returns the group ID of the player. /// -/// getgroupid() -> <int> -BUILDIN(getgroupid) +/// getgroupid({<account id>}) -> <int> +static BUILDIN(getgroupid) { - struct map_session_data *sd = script->rid2sd(st); - if (sd == NULL) + struct map_session_data *sd = NULL; + + if (script_hasdata(st, 2)) { + sd = map->id2sd(script_getnum(st, 2)); + } else { + sd = script->rid2sd(st); + } + + if (sd == NULL) { + script_pushint(st, -1); return true; // no player attached, report source - script_pushint(st, pc_get_group_id(sd)); + } + script_pushint(st, pc_get_group_id(sd)); return true; } /// Terminates the execution of this script instance. /// /// end -BUILDIN(end) { +static BUILDIN(end) +{ st->state = END; /* are we stopping inside a function? */ @@ -9222,10 +10452,16 @@ BUILDIN(end) { /// Checks if the player has that effect state (option). /// /// checkoption(<option>) -> <bool> -BUILDIN(checkoption) +static BUILDIN(checkoption) { int option; - struct map_session_data *sd = script->rid2sd(st); + struct map_session_data *sd; + + if (script_hasdata(st, 3)) + sd = map->id2sd(script_getnum(st, 3)); + else + sd = script->rid2sd(st); + if (sd == NULL) return true;// no player attached, report source @@ -9241,10 +10477,16 @@ BUILDIN(checkoption) /// Checks if the player is in that body state (opt1). /// /// checkoption1(<opt1>) -> <bool> -BUILDIN(checkoption1) +static BUILDIN(checkoption1) { int opt1; - struct map_session_data *sd = script->rid2sd(st); + struct map_session_data *sd; + + if (script_hasdata(st, 3)) + sd = map->id2sd(script_getnum(st, 3)); + else + sd = script->rid2sd(st); + if (sd == NULL) return true;// no player attached, report source @@ -9260,10 +10502,16 @@ BUILDIN(checkoption1) /// Checks if the player has that health state (opt2). /// /// checkoption2(<opt2>) -> <bool> -BUILDIN(checkoption2) +static BUILDIN(checkoption2) { int opt2; - struct map_session_data *sd = script->rid2sd(st); + struct map_session_data *sd; + + if (script_hasdata(st, 3)) + sd = map->id2sd(script_getnum(st, 3)); + else + sd = script->rid2sd(st); + if (sd == NULL) return true;// no player attached, report source @@ -9283,11 +10531,17 @@ BUILDIN(checkoption2) /// /// setoption <option>,<flag>; /// setoption <option>; -BUILDIN(setoption) +static BUILDIN(setoption) { int option; int flag = 1; - struct map_session_data *sd = script->rid2sd(st); + struct map_session_data *sd; + + if (script_hasdata(st, 4)) + sd = map->id2sd(script_getnum(st, 4)); + else + sd = script->rid2sd(st); + if (sd == NULL) return true;// no player attached, report source @@ -9316,7 +10570,7 @@ BUILDIN(setoption) /// checkcart() -> <bool> /// /// @author Valaris -BUILDIN(checkcart) +static BUILDIN(checkcart) { struct map_session_data *sd = script->rid2sd(st); if (sd == NULL) @@ -9341,7 +10595,7 @@ BUILDIN(checkcart) /// /// setcart <type>; /// setcart; -BUILDIN(setcart) +static BUILDIN(setcart) { int type = 1; struct map_session_data *sd = script->rid2sd(st); @@ -9360,7 +10614,7 @@ BUILDIN(setcart) /// checkfalcon() -> <bool> /// /// @author Valaris -BUILDIN(checkfalcon) +static BUILDIN(checkfalcon) { struct map_session_data *sd = script->rid2sd(st); if (sd == NULL) @@ -9379,7 +10633,7 @@ BUILDIN(checkfalcon) /// /// setfalcon <flag>; /// setfalcon; -BUILDIN(setfalcon) +static BUILDIN(setfalcon) { bool flag = true; struct map_session_data *sd = script->rid2sd(st); @@ -9416,7 +10670,7 @@ enum setmount_type { * The exact returned values are the same used as flag in setmount, except for * dragons, where SETMOUNT_TYPE_DRAGON is returned, regardless of color. */ -BUILDIN(checkmount) +static BUILDIN(checkmount) { struct map_session_data *sd = script->rid2sd(st); if (sd == NULL) @@ -9440,6 +10694,7 @@ BUILDIN(checkmount) /** * Mounts or dismounts a combat mount. * + * setmount <flag>, <mtype>; * setmount <flag>; * setmount; * @@ -9458,8 +10713,10 @@ BUILDIN(checkmount) * If an invalid value or no flag is specified, the appropriate mount is * auto-detected. As a result of this, there is no need to specify a flag at * all, unless it is a dragon color other than green. + * + * In newer clients you can specify the mado gear type though the mtype argument. */ -BUILDIN(setmount) +static BUILDIN(setmount) { int flag = SETMOUNT_TYPE_AUTODETECT; struct map_session_data *sd = script->rid2sd(st); @@ -9470,6 +10727,12 @@ BUILDIN(setmount) if (script_hasdata(st,2)) flag = script_getnum(st,2); + enum mado_type mtype = script_hasdata(st, 3) ? script_getnum(st, 3) : MADO_ROBOT; + if (mtype < MADO_ROBOT || mtype >= MADO_MAX) { + ShowError("script_setmount: Invalid mado type has been passed (%d).\n", flag); + return false; + } + // Color variants for Rune Knight dragon mounts. if (flag != SETMOUNT_TYPE_NONE) { if (flag < SETMOUNT_TYPE_AUTODETECT || flag >= SETMOUNT_TYPE_MAX) { @@ -9477,7 +10740,7 @@ BUILDIN(setmount) flag = SETMOUNT_TYPE_AUTODETECT; } // Sanity checks and auto-detection - if ((sd->class_&MAPID_THIRDMASK) == MAPID_RUNE_KNIGHT) { + if ((sd->job & MAPID_THIRDMASK) == MAPID_RUNE_KNIGHT) { if (pc->checkskill(sd, RK_DRAGONTRAINING)) { // Rune Knight (Dragon) unsigned int option; @@ -9489,14 +10752,14 @@ BUILDIN(setmount) OPTION_DRAGON1); // default value pc->setridingdragon(sd, option); } - } else if ((sd->class_&MAPID_THIRDMASK) == MAPID_RANGER) { + } else if ((sd->job & MAPID_THIRDMASK) == MAPID_RANGER) { // Ranger (Warg) if (pc->checkskill(sd, RA_WUGRIDER)) pc->setridingwug(sd, true); - } else if ((sd->class_&MAPID_THIRDMASK) == MAPID_MECHANIC) { + } else if ((sd->job & MAPID_THIRDMASK) == MAPID_MECHANIC) { // Mechanic (Mado Gear) if (pc->checkskill(sd, NC_MADOLICENCE)) - pc->setmadogear(sd, true); + pc->setmadogear(sd, true, mtype); } else { // Knight / Crusader (Peco Peco) if (pc->checkskill(sd, KN_RIDING)) @@ -9510,7 +10773,7 @@ BUILDIN(setmount) pc->setridingwug(sd, false); } if (pc_ismadogear(sd)) { - pc->setmadogear(sd, false); + pc->setmadogear(sd, false, mtype); } if (pc_isridingpeco(sd)) { pc->setridingpeco(sd, false); @@ -9524,7 +10787,7 @@ BUILDIN(setmount) /// /// checkwug() -> <bool> /// -BUILDIN(checkwug) +static BUILDIN(checkwug) { struct map_session_data *sd = script->rid2sd(st); if (sd == NULL) @@ -9542,7 +10805,8 @@ BUILDIN(checkwug) /// /// save "<map name>",<x>,<y> /// savepoint "<map name>",<x>,<y> -BUILDIN(savepoint) { +static BUILDIN(savepoint) +{ int x; int y; short mapid; @@ -9564,7 +10828,9 @@ BUILDIN(savepoint) { /*========================================== * GetTimeTick(0: System Tick, 1: Time Second Tick) *------------------------------------------*/ -BUILDIN(gettimetick) { /* Asgard Version */ +/* Asgard Version */ +static BUILDIN(gettimetick) +{ int type; time_t clock; struct tm *t; @@ -9597,7 +10863,9 @@ BUILDIN(gettimetick) { /* Asgard Version */ * 4: WeekDay 5: MonthDay 6: Month * 7: Year *------------------------------------------*/ -BUILDIN(gettime) { /* Asgard Version */ +/* Asgard Version */ +static BUILDIN(gettime) +{ int type; time_t clock; struct tm *t; @@ -9639,83 +10907,131 @@ BUILDIN(gettime) { /* Asgard Version */ return true; } -/*========================================== +/* * GetTimeStr("TimeFMT", Length); - *------------------------------------------*/ -BUILDIN(gettimestr) + */ +static BUILDIN(gettimestr) { char *tmpstr; const char *fmtstr; int maxlen; - time_t now = time(NULL); + time_t now; - fmtstr=script_getstr(st,2); - maxlen=script_getnum(st,3); + fmtstr = script_getstr(st, 2); + maxlen = script_getnum(st, 3); - tmpstr=(char *)aMalloc((maxlen+1)*sizeof(char)); - strftime(tmpstr,maxlen,fmtstr,localtime(&now)); - tmpstr[maxlen]='\0'; + if (script_hasdata(st, 4)) { + int timestamp = script_getnum(st, 4); + if (timestamp < 0) { + ShowWarning("buildin_gettimestr: UNIX timestamp must be in positive value.\n"); + return false; + } + + now = (time_t)timestamp; + } else { + now = time(NULL); + } + + tmpstr = (char *)aMalloc((maxlen +1)*sizeof(char)); + strftime(tmpstr, maxlen, fmtstr, localtime(&now)); + tmpstr[maxlen] = '\0'; - script_pushstr(st,tmpstr); + script_pushstr(st, tmpstr); return true; } /*========================================== * Open player storage *------------------------------------------*/ -BUILDIN(openstorage) +static BUILDIN(openstorage) { struct map_session_data *sd = script->rid2sd(st); if (sd == NULL) + return false; + + if (sd->storage.received == false) { + script_pushint(st, 0); + ShowWarning("buildin_openstorage: Storage data for AID %d has not been loaded.\n", sd->bl.id); + return false; + } + + // Mapflag preventing from openstorage here + if (!pc_has_permission(sd, PC_PERM_BYPASS_NOSTORAGE) && (map->list[sd->bl.m].flag.nostorage & 2)) { + script_pushint(st, 0); return true; + } storage->open(sd); + + script_pushint(st, 1); // success flag. return true; } -BUILDIN(guildopenstorage) +static BUILDIN(guildopenstorage) { int ret; struct map_session_data *sd = script->rid2sd(st); if (sd == NULL) return true; + // Mapflag preventing from openstorage here + if (!pc_has_permission(sd, PC_PERM_BYPASS_NOSTORAGE) && (map->list[sd->bl.m].flag.nogstorage & 2)) { + script_pushint(st, 1); + return true; + } + ret = gstorage->open(sd); script_pushint(st,ret); return true; } -/*========================================== - * Make player use a skill trought item usage - *------------------------------------------*/ -/// itemskill <skill id>,<level>{,flag -/// itemskill "<skill name>",<level>{,flag -BUILDIN(itemskill) +/** + * Makes the attached character use a skill by using an item. + * + * @code{.herc} + * itemskill(<skill id>, <skill level>{, <flag>}); + * itemskill("<skill name>", <skill level>{, <flag>}); + * @endcode + * + */ +static BUILDIN(itemskill) { - int id; - int lv; struct map_session_data *sd = script->rid2sd(st); + if (sd == NULL || sd->ud.skilltimer != INVALID_TIMER) return true; - id = ( script_isstringtype(st,2) ? skill->name2id(script_getstr(st,2)) : script_getnum(st,2) ); - lv = script_getnum(st,3); -/* temporarily disabled, awaiting for kenpachi to detail this so we can make it work properly */ -#if 0 - if( !script_hasdata(st, 4) ) { - if( !skill->check_condition_castbegin(sd,id,lv) || !skill->check_condition_castend(sd,id,lv) ) + pc->autocast_clear(sd); + sd->autocast.type = AUTOCAST_ITEM; + sd->autocast.skill_id = script_isstringtype(st, 2) ? skill->name2id(script_getstr(st, 2)) : script_getnum(st, 2); + sd->autocast.skill_lv = script_getnum(st, 3); + + int flag = script_hasdata(st, 4) ? script_getnum(st, 4) : ISF_NONE; + + sd->autocast.itemskill_check_conditions = ((flag & ISF_CHECKCONDITIONS) == ISF_CHECKCONDITIONS); + + if (sd->autocast.itemskill_check_conditions) { + if (skill->check_condition_castbegin(sd, sd->autocast.skill_id, sd->autocast.skill_lv) == 0 + || skill->check_condition_castend(sd, sd->autocast.skill_id, sd->autocast.skill_lv) == 0) { + pc->autocast_clear(sd); return true; + } + + sd->autocast.itemskill_conditions_checked = true; } -#endif - sd->skillitem=id; - sd->skillitemlv=lv; - clif->item_skill(sd,id,lv); + + sd->autocast.itemskill_instant_cast = ((flag & ISF_INSTANTCAST) == ISF_INSTANTCAST); + sd->autocast.itemskill_cast_on_self = ((flag & ISF_CASTONSELF) == ISF_CASTONSELF); + + clif->item_skill(sd, sd->autocast.skill_id, sd->autocast.skill_lv); + return true; } + /*========================================== * Attempt to create an item *------------------------------------------*/ -BUILDIN(produce) +static BUILDIN(produce) { int trigger; struct map_session_data *sd = script->rid2sd(st); @@ -9729,7 +11045,7 @@ BUILDIN(produce) /*========================================== * *------------------------------------------*/ -BUILDIN(cooking) +static BUILDIN(cooking) { int trigger; struct map_session_data *sd = script->rid2sd(st); @@ -9743,7 +11059,7 @@ BUILDIN(cooking) /*========================================== * Create a pet *------------------------------------------*/ -BUILDIN(makepet) +static BUILDIN(makepet) { struct map_session_data *sd; int id,pet_id; @@ -9760,8 +11076,8 @@ BUILDIN(makepet) if (pet_id >= 0 && sd) { sd->catch_target_class = pet->db[pet_id].class_; intif->create_pet(sd->status.account_id, sd->status.char_id, - (short)pet->db[pet_id].class_, (short)mob->db(pet->db[pet_id].class_)->lv, - (short)pet->db[pet_id].EggID, 0, (short)pet->db[pet_id].intimate, + pet->db[pet_id].class_, mob->db(pet->db[pet_id].class_)->lv, + pet->db[pet_id].EggID, 0, (short)pet->db[pet_id].intimate, 100, 0, 1, pet->db[pet_id].jname); } @@ -9770,7 +11086,7 @@ BUILDIN(makepet) /*========================================== * Give player exp base,job * quest_exp_rate/100 *------------------------------------------*/ -BUILDIN(getexp) +static BUILDIN(getexp) { int base=0,job=0; struct map_session_data *sd = script->rid2sd(st); @@ -9794,7 +11110,7 @@ BUILDIN(getexp) /*========================================== * Gain guild exp [Celest] *------------------------------------------*/ -BUILDIN(guildgetexp) +static BUILDIN(guildgetexp) { int exp; struct map_session_data *sd = script->rid2sd(st); @@ -9813,7 +11129,7 @@ BUILDIN(guildgetexp) /*========================================== * Changes the guild master of a guild [Skotlex] *------------------------------------------*/ -BUILDIN(guildchangegm) +static BUILDIN(guildchangegm) { struct map_session_data *sd; int guild_id; @@ -9826,7 +11142,7 @@ BUILDIN(guildchangegm) if (sd == NULL) script_pushint(st,0); else - script_pushint(st,guild->gm_change(guild_id, sd)); + script_pushint(st, guild->gm_change(guild_id, sd->status.char_id)); return true; } @@ -9839,7 +11155,7 @@ BUILDIN(guildchangegm) * @amount : nb to spawn * @event : event to attach to mob *------------------------------------------*/ -BUILDIN(monster) +static BUILDIN(monster) { const char *mapn = script_getstr(st,2); int x = script_getnum(st,3); @@ -9911,7 +11227,7 @@ BUILDIN(monster) /*========================================== * Request List of Monster Drops *------------------------------------------*/ -BUILDIN(getmobdrops) +static BUILDIN(getmobdrops) { int class_ = script_getnum(st,2); int i, j = 0; @@ -9932,13 +11248,13 @@ 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; @@ -9946,7 +11262,8 @@ BUILDIN(getmobdrops) /*========================================== * Same as monster but randomize location in x0,x1,y0,y1 area *------------------------------------------*/ -BUILDIN(areamonster) { +static BUILDIN(areamonster) +{ const char *mapn = script_getstr(st,2); int x0 = script_getnum(st,3); int y0 = script_getnum(st,4); @@ -10009,7 +11326,7 @@ BUILDIN(areamonster) { /*========================================== * KillMonster subcheck, verify if mob to kill ain't got an even to handle, could be force kill by allflag *------------------------------------------*/ -int buildin_killmonster_sub_strip(struct block_list *bl, va_list ap) +static int buildin_killmonster_sub_strip(struct block_list *bl, va_list ap) { //same fix but with killmonster instead - stripping events from mobs. struct mob_data *md = NULL; @@ -10032,7 +11349,7 @@ int buildin_killmonster_sub_strip(struct block_list *bl, va_list ap) md->state.npc_killmonster = 0; return 0; } -int buildin_killmonster_sub(struct block_list *bl, va_list ap) +static int buildin_killmonster_sub(struct block_list *bl, va_list ap) { struct mob_data *md = NULL; char *event = va_arg(ap,char *); @@ -10051,15 +11368,22 @@ int buildin_killmonster_sub(struct block_list *bl, va_list ap) } return 0; } -BUILDIN(killmonster) { +static BUILDIN(killmonster) +{ const char *mapname,*event; int16 m,allflag=0; mapname=script_getstr(st,2); event=script_getstr(st,3); - if(strcmp(event,"All")==0) + + if (strcmpi(event, "all") == 0) { + if (strcmp(event, "all") != 0) { + ShowWarning("buildin_killmonster: \"%s\" deprecated! Please use \"all\" instead.\n", event); + script->reportsrc(st); + } allflag = 1; - else + } else { script->check_event(st, event); + } if( (m=map->mapname2mapid(mapname))<0 ) return true; @@ -10080,23 +11404,25 @@ BUILDIN(killmonster) { return true; } -int buildin_killmonsterall_sub_strip(struct block_list *bl,va_list ap) +static int buildin_killmonsterall_sub_strip(struct block_list *bl, va_list ap) { //Strips the event from the mob if it's killed the old method. struct mob_data *md; md = BL_CAST(BL_MOB, bl); + nullpo_ret(md); if (md->npc_event[0]) md->npc_event[0] = 0; status_kill(bl); return 0; } -int buildin_killmonsterall_sub(struct block_list *bl,va_list ap) +static int buildin_killmonsterall_sub(struct block_list *bl, va_list ap) { status_kill(bl); return 0; } -BUILDIN(killmonsterall) { +static BUILDIN(killmonsterall) +{ const char *mapname; int16 m; mapname=script_getstr(st,2); @@ -10118,11 +11444,27 @@ BUILDIN(killmonsterall) { return true; } +static BUILDIN(killmonstergid) +{ + int mobgid = script_getnum(st, 2); + struct mob_data *md = map->id2md(mobgid); + + if (md == NULL) { + ShowWarning("buildin_killmonstergid: Error in finding monster GID '%d' or the target is not a monster.\n", mobgid); + return false; + } + + md->state.npc_killmonster = 1; + status_kill(&md->bl); + return true; +} + /*========================================== * Creates a clone of a player. * clone map, x, y, event, char_id, master_id, mode, flag, duration *------------------------------------------*/ -BUILDIN(clone) { +static BUILDIN(clone) +{ struct map_session_data *sd, *msd = NULL; int char_id, master_id = 0, x, y, flag = 0, m; uint32 mode = 0; @@ -10170,7 +11512,7 @@ BUILDIN(clone) { } /*========================================== *------------------------------------------*/ -BUILDIN(doevent) +static BUILDIN(doevent) { const char* event = script_getstr(st,2); struct map_session_data* sd; @@ -10186,7 +11528,7 @@ BUILDIN(doevent) } /*========================================== *------------------------------------------*/ -BUILDIN(donpcevent) +static BUILDIN(donpcevent) { const char* event = script_getstr(st,2); script->check_event(st, event); @@ -10201,61 +11543,311 @@ BUILDIN(donpcevent) /*========================================== *------------------------------------------*/ -BUILDIN(addtimer) +static BUILDIN(addtimer) { - int tick = script_getnum(st,2); + int tick = script_getnum(st, 2); const char* event = script_getstr(st, 3); struct map_session_data *sd; script->check_event(st, event); - sd = script->rid2sd(st); - if( sd == NULL ) - return true; - if (!pc->addeventtimer(sd,tick,event)) { - ShowWarning("buildin_addtimer: Event timer is full, can't add new event timer. (cid:%d timer:%s)\n",sd->status.char_id,event); + if (script_hasdata(st, 4)) + sd = map->id2sd(script_getnum(st, 4)); + else + sd = script->rid2sd(st); + + if (sd == NULL) { + script_pushint(st, 0); return false; } + + if (!pc->addeventtimer(sd, tick, event)) { + ShowWarning("script:addtimer: Event timer is full, can't add new event timer. (cid:%d timer:%s)\n", sd->status.char_id, event); + script_pushint(st, 0); + return false; + } + + script_pushint(st, 1); return true; } /*========================================== *------------------------------------------*/ -BUILDIN(deltimer) +static BUILDIN(deltimer) { const char *event; struct map_session_data *sd; event=script_getstr(st, 2); - sd = script->rid2sd(st); - if( sd == NULL ) + + if (script_hasdata(st, 3)) + sd = map->id2sd(script_getnum(st, 3)); + else + sd = script->rid2sd(st); + + if (sd == NULL) return true; script->check_event(st, event); - pc->deleventtimer(sd,event); + pc->deleventtimer(sd, event); return true; } /*========================================== *------------------------------------------*/ -BUILDIN(addtimercount) +static BUILDIN(addtimercount) { const char *event; int tick; struct map_session_data *sd; - event=script_getstr(st, 2); - tick=script_getnum(st,3); - sd = script->rid2sd(st); - if( sd == NULL ) + event = script_getstr(st, 2); + tick = script_getnum(st, 3); + + if (script_hasdata(st, 4)) + sd = map->id2sd(script_getnum(st, 4)); + else + sd = script->rid2sd(st); + + if (sd == NULL) return true; script->check_event(st, event); - pc->addeventtimercount(sd,event,tick); + pc->addeventtimercount(sd, event, tick); + return true; +} + +enum gettimer_mode { + GETTIMER_COUNT = 0, + GETTIMER_TICK_NEXT = 1, + GETTIMER_TICK_LAST = 2, +}; + +static BUILDIN(gettimer) +{ + struct map_session_data *sd; + const struct TimerData *td; + int i; + int tick; + const char *event = NULL; + int val = 0; + bool first = true; + short mode = script_getnum(st, 2); + + if (script_hasdata(st, 3)) + sd = map->id2sd(script_getnum(st, 3)); + else + sd = script->rid2sd(st); + + if (script_hasdata(st, 4)) { + event = script_getstr(st, 4); + script->check_event(st, event); + } + + if (sd == NULL) { + script_pushint(st, -1); + return true; + } + + switch (mode) { + case GETTIMER_COUNT: + // get number of timers + for (i = 0; i < MAX_EVENTTIMER; i++) { + if (sd->eventtimer[i] != INVALID_TIMER) { + if (event != NULL) { + td = timer->get(sd->eventtimer[i]); + Assert_retr(false, td != NULL); + + if (strcmp((char *)(td->data), event) == 0) { + val++; + } + } else { + val++; + } + } + } + break; + case GETTIMER_TICK_NEXT: + // get the number of tick before the next timer runs + for (i = 0; i < MAX_EVENTTIMER; i++) { + if (sd->eventtimer[i] != INVALID_TIMER) { + td = timer->get(sd->eventtimer[i]); + Assert_retr(false, td != NULL); + tick = max(0, DIFF_TICK32(td->tick, timer->gettick())); + + if (event != NULL) { + if ((first == true || tick < val) && strcmp((char *)(td->data), event) == 0) { + val = tick; + first = false; + } + } else if (first == true || tick < val) { + val = tick; + first = false; + } + } + } + break; + case GETTIMER_TICK_LAST: + // get the number of ticks before the last timer runs + for (i = MAX_EVENTTIMER - 1; i >= 0; i--) { + if (sd->eventtimer[i] != INVALID_TIMER) { + td = timer->get(sd->eventtimer[i]); + Assert_retr(false, td != NULL); + tick = max(0, DIFF_TICK32(td->tick, timer->gettick())); + + if (event != NULL) { + if (strcmp((char *)(td->data), event) == 0) { + val = max(val, tick); + } + } else { + val = max(val, tick); + } + } + } + break; + } + + script_pushint(st, val); + return true; +} + +static int buildin_getunits_sub(struct block_list *bl, va_list ap) +{ + struct script_state *st = va_arg(ap, struct script_state *); + struct map_session_data *sd = va_arg(ap, struct map_session_data *); + int32 id = va_arg(ap, int32); + uint32 start = va_arg(ap, uint32); + uint32 *count = va_arg(ap, uint32 *); + uint32 limit = va_arg(ap, uint32); + const char *name = va_arg(ap, const char *); + struct reg_db *ref = va_arg(ap, struct reg_db *); + enum bl_type type = va_arg(ap, enum bl_type); + uint32 index = start + *count; + + if ((bl->type & type) == 0) { + return 0; // type mismatch => skip + } + + if (index >= SCRIPT_MAX_ARRAYSIZE || *count >= limit) { + return -1; + } + + script->set_reg(st, sd, reference_uid(id, index), name, + (const void *)h64BPTRSIZE(bl->id), ref); + + (*count)++; + return 1; +} + +static int buildin_getunits_sub_pc(struct map_session_data *sd, va_list ap) +{ + return buildin_getunits_sub(&sd->bl, ap); +} + +static int buildin_getunits_sub_mob(struct mob_data *md, va_list ap) +{ + return buildin_getunits_sub(&md->bl, ap); +} + +static int buildin_getunits_sub_npc(struct npc_data *nd, va_list ap) +{ + return buildin_getunits_sub(&nd->bl, ap); +} + +static BUILDIN(getunits) +{ + const char *name; + int32 id; + uint32 start; + struct reg_db *ref; + enum bl_type type = script_getnum(st, 2); + struct script_data *data = script_getdata(st, 3); + uint32 count = 0; + uint32 limit = script_getnum(st, 4); + struct map_session_data *sd = NULL; + + if (!data_isreference(data) || reference_toconstant(data)) { + ShowError("script:getunits: second argument must be a variable\n"); + script->reportdata(data); + st->state = END; + return false; + } + + id = reference_getid(data); + start = reference_getindex(data); + name = reference_getname(data); + ref = reference_getref(data); + + if (not_server_variable(*name)) { + sd = script->rid2sd(st); + + if (sd == NULL) { + script_pushint(st, 0); + return true; // player variable but no player attached + } + } + + if (is_string_variable(name)) { + ShowError("script:getunits: second argument must be an integer variable\n"); + script->reportdata(data); + st->state = END; + return false; + } + + if (limit < 1 || limit > SCRIPT_MAX_ARRAYSIZE) { + limit = SCRIPT_MAX_ARRAYSIZE; + } + + if (script_hasdata(st, 5)) { + const char *mapname = script_getstr(st, 5); + int16 m = map->mapname2mapid(mapname); + + if (m == -1) { + ShowError("script:getunits: Invalid map(%s) provided.\n", mapname); + script->reportdata(data); + st->state = END; + return false; + } + + if (script_hasdata(st, 9)) { + int16 x1 = script_getnum(st, 6); + int16 y1 = script_getnum(st, 7); + int16 x2 = script_getnum(st, 8); + int16 y2 = script_getnum(st, 9); + + map->forcountinarea(buildin_getunits_sub, m, x1, y1, x2, y2, limit, type, + st, sd, id, start, &count, limit, name, ref, type); + } else { + map->forcountinmap(buildin_getunits_sub, m, limit, type, + st, sd, id, start, &count, limit, name, ref, type); + } + } else { + // for faster lookup we try to reduce the scope of the search if possible + switch (type) { + case BL_PC: + map->foreachpc(buildin_getunits_sub_pc, + st, sd, id, start, &count, limit, name, ref, type); + break; + case BL_MOB: + map->foreachmob(buildin_getunits_sub_mob, + st, sd, id, start, &count, limit, name, ref, type); + break; + case BL_NPC: + map->foreachnpc(buildin_getunits_sub_npc, + st, sd, id, start, &count, limit, name, ref, type); + break; + default: + // fallback to global lookup (slowest option) + map->foreachiddb(buildin_getunits_sub, + st, sd, id, start, &count, limit, name, ref, type); + } + } + + script_pushint(st, count); return true; } /*========================================== *------------------------------------------*/ -BUILDIN(initnpctimer) +static BUILDIN(initnpctimer) { struct npc_data *nd; int flag = 0; @@ -10300,7 +11892,7 @@ BUILDIN(initnpctimer) } /*========================================== *------------------------------------------*/ -BUILDIN(startnpctimer) +static BUILDIN(startnpctimer) { struct npc_data *nd; int flag = 0; @@ -10343,7 +11935,8 @@ BUILDIN(startnpctimer) } /*========================================== *------------------------------------------*/ -BUILDIN(stopnpctimer) { +static BUILDIN(stopnpctimer) +{ struct npc_data *nd; int flag = 0; @@ -10381,7 +11974,7 @@ BUILDIN(stopnpctimer) { } /*========================================== *------------------------------------------*/ -BUILDIN(getnpctimer) +static BUILDIN(getnpctimer) { struct npc_data *nd; struct map_session_data *sd; @@ -10419,7 +12012,7 @@ BUILDIN(getnpctimer) } /*========================================== *------------------------------------------*/ -BUILDIN(setnpctimer) +static BUILDIN(setnpctimer) { int tick; struct npc_data *nd; @@ -10444,7 +12037,7 @@ BUILDIN(setnpctimer) /*========================================== * attaches the player rid to the timer [Celest] *------------------------------------------*/ -BUILDIN(attachnpctimer) +static BUILDIN(attachnpctimer) { struct map_session_data *sd; struct npc_data *nd = map->id2nd(st->oid); @@ -10473,7 +12066,8 @@ BUILDIN(attachnpctimer) /*========================================== * detaches a player rid from the timer [Celest] *------------------------------------------*/ -BUILDIN(detachnpctimer) { +static BUILDIN(detachnpctimer) +{ struct npc_data *nd; if( script_hasdata(st,2) ) @@ -10497,7 +12091,8 @@ BUILDIN(detachnpctimer) { * it checks if there is a player attached to the current script. [Skotlex] * If no, returns 0, if yes, returns the account_id of the attached player. *------------------------------------------*/ -BUILDIN(playerattached) { +static BUILDIN(playerattached) +{ if(st->rid == 0 || map->id2sd(st->rid) == NULL) script_pushint(st,0); else @@ -10506,8 +12101,21 @@ BUILDIN(playerattached) { } /*========================================== + * Used by OnTouchNPC: label to return monster GID *------------------------------------------*/ -BUILDIN(announce) { +static BUILDIN(mobattached) +{ + if (st->rid == 0 || map->id2md(st->rid) == NULL) + script_pushint(st, 0); + else + script_pushint(st, st->rid); + return true; +} + +/*========================================== + *------------------------------------------*/ +static BUILDIN(announce) +{ const char *mes = script_getstr(st,2); int flag = script_getnum(st,3); const char *fontColor = script_hasdata(st,4) ? script_getstr(st,4) : NULL; @@ -10515,11 +12123,13 @@ BUILDIN(announce) { int fontSize = script_hasdata(st,6) ? script_getnum(st,6) : 12; // default fontSize int fontAlign = script_hasdata(st,7) ? script_getnum(st,7) : 0; // default fontAlign int fontY = script_hasdata(st,8) ? script_getnum(st,8) : 0; // default fontY + size_t len = strlen(mes); + send_target target = ALL_CLIENT; + struct block_list *bl = NULL; + Assert_retr(false, len < INT_MAX); if( flag&(BC_TARGET_MASK|BC_SOURCE_MASK) ) { // Broadcast source or broadcast region defined - send_target target; - struct block_list *bl = NULL; if (flag&BC_NPC) { // If bc_npc flag is set, use NPC as broadcast source bl = map->id2bl(st->oid); @@ -10537,27 +12147,23 @@ BUILDIN(announce) { case BC_SELF: target = SELF; break; default: target = ALL_CLIENT; break; // BC_ALL } - - if (fontColor) - clif->broadcast2(bl, mes, (int)strlen(mes)+1, (unsigned int)strtoul(fontColor, (char **)NULL, 0), fontType, fontSize, fontAlign, fontY, target); - else - clif->broadcast(bl, mes, (int)strlen(mes)+1, flag&BC_COLOR_MASK, target); - } else { - if (fontColor) - intif->broadcast2(mes, (int)strlen(mes)+1, (unsigned int)strtoul(fontColor, (char **)NULL, 0), fontType, fontSize, fontAlign, fontY); - else - intif->broadcast(mes, (int)strlen(mes)+1, flag&BC_COLOR_MASK); } + + if (fontColor) + clif->broadcast2(bl, mes, (int)len+1, (unsigned int)strtoul(fontColor, (char **)NULL, 0), fontType, fontSize, fontAlign, fontY, target); + else + clif->broadcast(bl, mes, (int)len+1, flag&BC_COLOR_MASK, target); + return true; } /*========================================== *------------------------------------------*/ -int buildin_announce_sub(struct block_list *bl, va_list ap) +static int buildin_announce_sub(struct block_list *bl, va_list ap) { - char *mes = va_arg(ap, char *); + const char *mes = va_arg(ap, const char *); int len = va_arg(ap, int); int type = va_arg(ap, int); - char *fontColor = va_arg(ap, char *); + const char *fontColor = va_arg(ap, const char *); short fontType = (short)va_arg(ap, int); short fontSize = (short)va_arg(ap, int); short fontAlign = (short)va_arg(ap, int); @@ -10571,7 +12177,7 @@ int buildin_announce_sub(struct block_list *bl, va_list ap) /* Runs item effect on attached character. * itemeffect <item id>; * itemeffect "<item name>"; */ -BUILDIN(itemeffect) +static BUILDIN(itemeffect) { struct npc_data *nd; struct item_data *item_data; @@ -10604,7 +12210,8 @@ BUILDIN(itemeffect) return true; } -BUILDIN(mapannounce) { +static BUILDIN(mapannounce) +{ const char *mapname = script_getstr(st,2); const char *mes = script_getstr(st,3); int flag = script_getnum(st,4); @@ -10614,17 +12221,20 @@ BUILDIN(mapannounce) { int fontAlign = script_hasdata(st,8) ? script_getnum(st,8) : 0; // default fontAlign int fontY = script_hasdata(st,9) ? script_getnum(st,9) : 0; // default fontY int16 m; + size_t len = strlen(mes); + Assert_retr(false, len < INT_MAX); if ((m = map->mapname2mapid(mapname)) < 0) return true; map->foreachinmap(script->buildin_announce_sub, m, BL_PC, - mes, strlen(mes)+1, flag&BC_COLOR_MASK, fontColor, fontType, fontSize, fontAlign, fontY); + mes, (int)len+1, flag&BC_COLOR_MASK, fontColor, fontType, fontSize, fontAlign, fontY); return true; } /*========================================== *------------------------------------------*/ -BUILDIN(areaannounce) { +static BUILDIN(areaannounce) +{ const char *mapname = script_getstr(st,2); int x0 = script_getnum(st,3); int y0 = script_getnum(st,4); @@ -10638,18 +12248,21 @@ BUILDIN(areaannounce) { int fontAlign = script_hasdata(st,12) ? script_getnum(st,12) : 0; // default fontAlign int fontY = script_hasdata(st,13) ? script_getnum(st,13) : 0; // default fontY int16 m; + size_t len = strlen(mes); + Assert_retr(false, len < INT_MAX); if ((m = map->mapname2mapid(mapname)) < 0) return true; map->foreachinarea(script->buildin_announce_sub, m, x0, y0, x1, y1, BL_PC, - mes, strlen(mes)+1, flag&BC_COLOR_MASK, fontColor, fontType, fontSize, fontAlign, fontY); + mes, (int)len+1, flag&BC_COLOR_MASK, fontColor, fontType, fontSize, fontAlign, fontY); return true; } /*========================================== *------------------------------------------*/ -BUILDIN(getusers) { +static BUILDIN(getusers) +{ int flag, val = 0; struct map_session_data* sd; struct block_list* bl = NULL; @@ -10685,7 +12298,7 @@ BUILDIN(getusers) { /*========================================== * Works like @WHO - displays all online users names in window *------------------------------------------*/ -BUILDIN(getusersname) +static BUILDIN(getusersname) { struct map_session_data *sd; const struct map_session_data *pl_sd; @@ -10714,7 +12327,7 @@ BUILDIN(getusersname) /*========================================== * getmapguildusers("mapname",guild ID) Returns the number guild members present on a map [Reddozen] *------------------------------------------*/ -BUILDIN(getmapguildusers) +static BUILDIN(getmapguildusers) { const char *str; int16 m; @@ -10742,7 +12355,8 @@ BUILDIN(getmapguildusers) } /*========================================== *------------------------------------------*/ -BUILDIN(getmapusers) { +static BUILDIN(getmapusers) +{ const char *str; int16 m; str=script_getstr(st,2); @@ -10755,14 +12369,15 @@ BUILDIN(getmapusers) { } /*========================================== *------------------------------------------*/ -int buildin_getareausers_sub(struct block_list *bl,va_list ap) +static int buildin_getareausers_sub(struct block_list *bl, va_list ap) { int *users=va_arg(ap,int *); + nullpo_ret(users); (*users)++; return 0; } -BUILDIN(getareausers) +static BUILDIN(getareausers) { int16 m = -1, x0, y0, x1, y1; int users = 0; @@ -10822,7 +12437,7 @@ BUILDIN(getareausers) /*========================================== *------------------------------------------*/ -int buildin_getareadropitem_sub(struct block_list *bl, va_list ap) +static int buildin_getareadropitem_sub(struct block_list *bl, va_list ap) { int item = va_arg(ap, int); int *amount = va_arg(ap, int *); @@ -10838,7 +12453,8 @@ int buildin_getareadropitem_sub(struct block_list *bl, va_list ap) return 0; } -BUILDIN(getareadropitem) { +static BUILDIN(getareadropitem) +{ const char *str; int16 m,x0,y0,x1,y1; int item,amount=0; @@ -10870,7 +12486,7 @@ BUILDIN(getareadropitem) { } /*========================================== *------------------------------------------*/ -BUILDIN(enablenpc) +static BUILDIN(enablenpc) { const char *str; str=script_getstr(st,2); @@ -10879,7 +12495,7 @@ BUILDIN(enablenpc) } /*========================================== *------------------------------------------*/ -BUILDIN(disablenpc) +static BUILDIN(disablenpc) { const char *str; str=script_getstr(st,2); @@ -10889,7 +12505,7 @@ BUILDIN(disablenpc) /*========================================== *------------------------------------------*/ -BUILDIN(hideoffnpc) +static BUILDIN(hideoffnpc) { const char *str; str=script_getstr(st,2); @@ -10898,13 +12514,63 @@ BUILDIN(hideoffnpc) } /*========================================== *------------------------------------------*/ -BUILDIN(hideonnpc) +static BUILDIN(hideonnpc) { const char *str; str=script_getstr(st,2); npc->enable(str,4); return true; } +/*========================================== + *------------------------------------------*/ +static BUILDIN(cloakonnpc) +{ + struct npc_data *nd = npc->name2id(script_getstr(st, 2)); + if (nd == NULL) { + ShowError("buildin_cloakonnpc: invalid npc name '%s'.\n", script_getstr(st, 2)); + return false; + } + + if (script_hasdata(st, 3)) { + struct map_session_data *sd = map->id2sd(script_getnum(st, 3)); + if (sd == NULL) + return false; + + uint32 val = nd->option; + nd->option |= OPTION_CLOAK; + clif->changeoption_target(&nd->bl, &sd->bl, SELF); + nd->option = val; + } else { + nd->option |= OPTION_CLOAK; + clif->changeoption(&nd->bl); + } + return true; +} +/*========================================== + *------------------------------------------*/ +static BUILDIN(cloakoffnpc) +{ + struct npc_data *nd = npc->name2id(script_getstr(st, 2)); + if (nd == NULL) { + ShowError("buildin_cloakoffnpc: invalid npc name '%s'.\n", script_getstr(st, 2)); + return false; + } + + if (script_hasdata(st, 3)) { + struct map_session_data *sd = map->id2sd(script_getnum(st, 3)); + if (sd == NULL) + return false; + + uint32 val = nd->option; + nd->option &= ~OPTION_CLOAK; + clif->changeoption_target(&nd->bl, &sd->bl, SELF); + nd->option = val; + } else { + nd->option &= ~OPTION_CLOAK; + clif->changeoption(&nd->bl); + } + return true; +} /* Starts a status effect on the target unit or on the attached player. * @@ -10913,7 +12579,7 @@ BUILDIN(hideonnpc) * sc_start4 <effect_id>,<duration>,<val1>,<val2>,<val3>,<val4>{,<rate,<flag>,{<unit_id>}}; * <flag>: @see enum scstart_flag */ -BUILDIN(sc_start) +static BUILDIN(sc_start) { struct npc_data *nd = map->id2nd(st->oid); struct block_list* bl; @@ -10981,7 +12647,8 @@ BUILDIN(sc_start) /// Ends one or all status effects on the target unit or on the attached player. /// /// sc_end <effect_id>{,<unit_id>}; -BUILDIN(sc_end) { +static BUILDIN(sc_end) +{ struct block_list* bl; int type; @@ -11016,7 +12683,9 @@ 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 @@ -11028,7 +12697,8 @@ BUILDIN(sc_end) { /*========================================== * @FIXME atm will return reduced tick, 0 immune, 1 no tick *------------------------------------------*/ -BUILDIN(getscrate) { +static BUILDIN(getscrate) +{ struct block_list *bl; int type,rate; @@ -11049,7 +12719,7 @@ BUILDIN(getscrate) { /*========================================== * getstatus <type>{, <info>}; *------------------------------------------*/ -BUILDIN(getstatus) +static BUILDIN(getstatus) { int id, type; struct map_session_data* sd = script->rid2sd(st); @@ -11080,14 +12750,16 @@ BUILDIN(getstatus) case 3: script_pushint(st, sd->sc.data[id]->val3); break; case 4: script_pushint(st, sd->sc.data[id]->val4); break; case 5: - { - const struct TimerData *td = timer->get(sd->sc.data[id]->timer); + if (sd->sc.data[id]->infinite_duration) { + script_pushint(st, INFINITE_DURATION); + } else { + const struct TimerData *td = timer->get(sd->sc.data[id]->timer); - if (td != NULL) { - // return the amount of time remaining - script_pushint(st, (int)(td->tick - timer->gettick())); // TODO: change this to int64 when we'll support 64 bit script values + if (td != NULL) { + // return the amount of time remaining + script_pushint(st, (int)(td->tick - timer->gettick())); // TODO: change this to int64 when we'll support 64 bit script values + } } - } break; default: script_pushint(st, 1); break; } @@ -11098,17 +12770,26 @@ BUILDIN(getstatus) /*========================================== * *------------------------------------------*/ -BUILDIN(debugmes) +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; } /*========================================== *------------------------------------------*/ -BUILDIN(catchpet) +static BUILDIN(catchpet) { int pet_id; struct map_session_data *sd; @@ -11125,7 +12806,7 @@ BUILDIN(catchpet) /*========================================== * [orn] *------------------------------------------*/ -BUILDIN(homunculus_evolution) +static BUILDIN(homunculus_evolution) { struct map_session_data *sd = script->rid2sd(st); if (sd == NULL) @@ -11145,7 +12826,7 @@ BUILDIN(homunculus_evolution) * Checks for vaporized morph state * and deletes ITEMID_STRANGE_EMBRYO. *------------------------------------------*/ -BUILDIN(homunculus_mutate) +static BUILDIN(homunculus_mutate) { bool success = false; struct map_session_data *sd = script->rid2sd(st); @@ -11159,7 +12840,7 @@ BUILDIN(homunculus_mutate) if (script_hasdata(st,2)) homun_id = script_getnum(st,2); else - homun_id = 6048 + (rnd() % 4); + homun_id = HOMID_EIRA + (rnd() % 4); m_class = homun->class2type(sd->hd->homunculus.class_); m_id = homun->class2type(homun_id); @@ -11186,7 +12867,7 @@ BUILDIN(homunculus_mutate) * Puts homunculus into morph state * and gives ITEMID_STRANGE_EMBRYO. *------------------------------------------*/ -BUILDIN(homunculus_morphembryo) +static BUILDIN(homunculus_morphembryo) { bool success = false; struct map_session_data *sd = script->rid2sd(st); @@ -11208,7 +12889,7 @@ BUILDIN(homunculus_morphembryo) clif->additem(sd, 0, 0, i); clif->emotion(&sd->hd->bl, E_SWT); } else { - homun->vaporize(sd, HOM_ST_MORPH); + homun->vaporize(sd, HOM_ST_MORPH, true); success = true; } } else { @@ -11229,7 +12910,7 @@ BUILDIN(homunculus_morphembryo) * 1 = Homunculus is vaporized (rest) * 2 = Homunculus is in morph state *------------------------------------------*/ -BUILDIN(homunculus_checkcall) +static BUILDIN(homunculus_checkcall) { struct map_session_data *sd = script->rid2sd(st); if (sd == NULL || sd->hd == NULL) @@ -11241,7 +12922,7 @@ BUILDIN(homunculus_checkcall) } // [Zephyrus] -BUILDIN(homunculus_shuffle) +static BUILDIN(homunculus_shuffle) { struct map_session_data *sd = script->rid2sd(st); if (sd == NULL) @@ -11254,24 +12935,24 @@ BUILDIN(homunculus_shuffle) } //These two functions bring the eA MAPID_* class functionality to scripts. -BUILDIN(eaclass) +static BUILDIN(eaclass) { - int class_; + int class; if (script_hasdata(st,2)) { - class_ = script_getnum(st,2); + class = script_getnum(st,2); } else { struct map_session_data *sd = script->rid2sd(st); if (sd == NULL) return true; - class_ = sd->status.class_; + class = sd->status.class; } - script_pushint(st,pc->jobid2mapid(class_)); + script_pushint(st,pc->jobid2mapid(class)); return true; } -BUILDIN(roclass) +static BUILDIN(roclass) { - int class_ =script_getnum(st,2); + int job = script_getnum(st,2); int sex; if (script_hasdata(st,3)) { sex = script_getnum(st,3); @@ -11282,14 +12963,14 @@ BUILDIN(roclass) else sex = 1; //Just use male when not found. } - script_pushint(st,pc->mapid2jobid(class_, sex)); + script_pushint(st,pc->mapid2jobid(job, sex)); return true; } /*========================================== * Tells client to open a hatching window, used for pet incubator *------------------------------------------*/ -BUILDIN(birthpet) +static BUILDIN(birthpet) { struct map_session_data *sd = script->rid2sd(st); if (sd == NULL) @@ -11312,7 +12993,7 @@ BUILDIN(birthpet) * 3 : don't reset skill, blvl=1 * 4 : jlvl=0 *------------------------------------------*/ -BUILDIN(resetlvl) +static BUILDIN(resetlvl) { int type=script_getnum(st,2); struct map_session_data *sd = script->rid2sd(st); @@ -11325,7 +13006,7 @@ BUILDIN(resetlvl) /*========================================== * Reset a player status point *------------------------------------------*/ -BUILDIN(resetstatus) +static BUILDIN(resetstatus) { struct map_session_data *sd = script->rid2sd(st); if (sd == NULL) @@ -11337,7 +13018,7 @@ BUILDIN(resetstatus) /*========================================== * script command resetskill *------------------------------------------*/ -BUILDIN(resetskill) +static BUILDIN(resetskill) { struct map_session_data *sd = script->rid2sd(st); if (sd == NULL) @@ -11349,7 +13030,7 @@ BUILDIN(resetskill) /*========================================== * Counts total amount of skill points. *------------------------------------------*/ -BUILDIN(skillpointcount) +static BUILDIN(skillpointcount) { struct map_session_data *sd = script->rid2sd(st); if (sd == NULL) @@ -11361,7 +13042,7 @@ BUILDIN(skillpointcount) /*========================================== * *------------------------------------------*/ -BUILDIN(changebase) +static BUILDIN(changebase) { struct map_session_data *sd = NULL; int vclass; @@ -11378,12 +13059,12 @@ BUILDIN(changebase) if(vclass == JOB_WEDDING) { if (!battle_config.wedding_modifydisplay || //Do not show the wedding sprites - sd->class_&JOBL_BABY //Baby classes screw up when showing wedding sprites. [Skotlex] They don't seem to anymore. + sd->job & JOBL_BABY //Baby classes screw up when showing wedding sprites. [Skotlex] They don't seem to anymore. ) return true; } - if(sd->disguise == -1 && vclass != sd->vd.class_) + if (sd->disguise == -1 && vclass != sd->vd.class) pc->changelook(sd,LOOK_BASE,vclass); //Updated client view. Base, Weapon and Cloth Colors. return true; @@ -11407,7 +13088,7 @@ static struct map_session_data *prepareChangeSex(struct script_state *st) /*========================================== * Unequip all item and request for a changesex to char-serv *------------------------------------------*/ -BUILDIN(changesex) +static BUILDIN(changesex) { struct map_session_data *sd = prepareChangeSex(st); if (sd == NULL) @@ -11419,7 +13100,7 @@ BUILDIN(changesex) /*========================================== * Unequip all items and change character sex [4144] *------------------------------------------*/ -BUILDIN(changecharsex) +static BUILDIN(changecharsex) { struct map_session_data *sd = prepareChangeSex(st); if (sd == NULL) @@ -11431,7 +13112,7 @@ BUILDIN(changecharsex) /*========================================== * Works like 'announce' but outputs in the common chat window *------------------------------------------*/ -BUILDIN(globalmes) +static BUILDIN(globalmes) { const char *name=NULL,*mes; @@ -11459,7 +13140,7 @@ BUILDIN(globalmes) /// Creates a waiting room (chat room) for this npc. /// /// waitingroom "<title>",<limit>{,"<event>"{,<trigger>{,<zeny>{,<minlvl>{,<maxlvl>}}}}}; -BUILDIN(waitingroom) +static BUILDIN(waitingroom) { struct npc_data* nd; const char* title = script_getstr(st, 2); @@ -11483,7 +13164,8 @@ BUILDIN(waitingroom) /// /// delwaitingroom "<npc_name>"; /// delwaitingroom; -BUILDIN(delwaitingroom) { +static BUILDIN(delwaitingroom) +{ struct npc_data* nd; if( script_hasdata(st,2) ) nd = npc->name2id(script_getstr(st, 2)); @@ -11498,7 +13180,8 @@ BUILDIN(delwaitingroom) { /// /// kickwaitingroomall "<npc_name>"; /// kickwaitingroomall; -BUILDIN(waitingroomkickall) { +static BUILDIN(waitingroomkickall) +{ struct npc_data* nd; struct chat_data* cd; @@ -11516,7 +13199,8 @@ BUILDIN(waitingroomkickall) { /// /// enablewaitingroomevent "<npc_name>"; /// enablewaitingroomevent; -BUILDIN(enablewaitingroomevent) { +static BUILDIN(enablewaitingroomevent) +{ struct npc_data* nd; struct chat_data* cd; @@ -11534,7 +13218,8 @@ BUILDIN(enablewaitingroomevent) { /// /// disablewaitingroomevent "<npc_name>"; /// disablewaitingroomevent; -BUILDIN(disablewaitingroomevent) { +static BUILDIN(disablewaitingroomevent) +{ struct npc_data *nd; struct chat_data *cd; @@ -11566,7 +13251,7 @@ BUILDIN(disablewaitingroomevent) { /// /// getwaitingroomstate(<type>,"<npc_name>") -> <info> /// getwaitingroomstate(<type>) -> <info> -BUILDIN(getwaitingroomstate) +static BUILDIN(getwaitingroomstate) { const struct npc_data *nd; const struct chat_data *cd; @@ -11588,7 +13273,8 @@ BUILDIN(getwaitingroomstate) case 0: for (i = 0; i < cd->users; i++) { struct map_session_data *sd = cd->usersd[i]; - mapreg->setreg(reference_uid(script->add_str("$@chatmembers"), i), sd->bl.id); + nullpo_retr(false, sd); + mapreg->setreg(reference_uid(script->add_variable("$@chatmembers"), i), sd->bl.id); } script_pushint(st, cd->users); break; @@ -11601,8 +13287,8 @@ BUILDIN(getwaitingroomstate) case 32: script_pushint(st, (cd->users >= cd->limit)); break; case 33: script_pushint(st, (cd->users >= cd->trigger)); break; - case 34: script_pushint(st, cd->minLvl); break; - case 35: script_pushint(st, cd->maxLvl); break; + case 34: script_pushint(st, cd->min_level); break; + case 35: script_pushint(st, cd->max_level); break; case 36: script_pushint(st, cd->zeny); break; default: script_pushint(st, -1); break; } @@ -11621,7 +13307,7 @@ BUILDIN(getwaitingroomstate) /// /// warpwaitingpc "<map name>",<x>,<y>,<number of players>; /// warpwaitingpc "<map name>",<x>,<y>; -BUILDIN(warpwaitingpc) +static BUILDIN(warpwaitingpc) { int x, y, i, n; const char* map_name; @@ -11643,6 +13329,7 @@ BUILDIN(warpwaitingpc) for (i = 0; i < n && cd->users > 0; i++) { struct map_session_data *sd = cd->usersd[0]; + nullpo_retr(false, sd); if (strcmp(map_name,"SavePoint") == 0 && map->list[sd->bl.m].flag.noteleport) { // can't teleport on this map break; @@ -11657,7 +13344,7 @@ 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); @@ -11666,7 +13353,7 @@ 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; } @@ -11677,7 +13364,8 @@ BUILDIN(warpwaitingpc) /// Detaches a character from a script. /// /// @param st Script state to detach the character from. -void script_detach_rid(struct script_state* st) { +static void script_detach_rid(struct script_state *st) +{ if(st->rid) { script->detach_state(st, false); st->rid = 0; @@ -11687,7 +13375,8 @@ void script_detach_rid(struct script_state* st) { /*========================================== * Attach sd char id to script and detach current one if any *------------------------------------------*/ -BUILDIN(attachrid) { +static BUILDIN(attachrid) +{ int rid = script_getnum(st,2); if (map->id2sd(rid) != NULL) { @@ -11703,7 +13392,7 @@ BUILDIN(attachrid) { /*========================================== * Detach script to rid *------------------------------------------*/ -BUILDIN(detachrid) +static BUILDIN(detachrid) { script->detach_rid(st); return true; @@ -11711,7 +13400,7 @@ BUILDIN(detachrid) /*========================================== * Chk if account connected, (and charid from account if specified) *------------------------------------------*/ -BUILDIN(isloggedin) +static BUILDIN(isloggedin) { struct map_session_data *sd = map->id2sd(script_getnum(st,2)); if (script_hasdata(st,3) && sd != NULL @@ -11724,7 +13413,8 @@ BUILDIN(isloggedin) /*========================================== * *------------------------------------------*/ -BUILDIN(setmapflagnosave) { +static BUILDIN(setmapflagnosave) +{ int16 m,x,y; unsigned short map_index; const char *str,*str2; @@ -11746,7 +13436,81 @@ BUILDIN(setmapflagnosave) { return true; } -BUILDIN(getmapflag) +enum mapinfo_info { + MAPINFO_NAME, + MAPINFO_ID, + MAPINFO_SIZE_X, + MAPINFO_SIZE_Y, + MAPINFO_ZONE, + MAPINFO_NPC_COUNT +}; + +static BUILDIN(getmapinfo) +{ + enum mapinfo_info mode = script_getnum(st, 2); + int16 m = -1; + + if (script_hasdata(st, 3)) { + if (script_isstringtype(st, 3)) { + const char *str = script_getstr(st, 3); + m = map->mapindex2mapid(strdb_iget(mapindex->db, str)); + } else { + m = script_getnum(st, 3); + } + } else { + struct block_list *bl = NULL; + + if (st->oid) { + bl = map->id2bl(st->oid); + } else if (st->rid) { + bl = map->id2bl(st->rid); + } + + if (bl == NULL) { + ShowError("buildin_getmapinfo: map not supplied and NPC/PC not attached!\n"); + script_pushint(st, -3); + return false; + } + + m = bl->m; + } + + if (m < 0) { + // here we don't throw an error, so the command can be used + // to detect whether or not a map exists + script_pushint(st, -1); + return true; + } + + switch (mode) { + case MAPINFO_NAME: + script_pushconststr(st, map->list[m].name); + break; + case MAPINFO_ID: + script_pushint(st, m); + break; + case MAPINFO_SIZE_X: + script_pushint(st, map->list[m].xs); + break; + case MAPINFO_SIZE_Y: + script_pushint(st, map->list[m].ys); + break; + case MAPINFO_ZONE: + script_pushstrcopy(st, map->list[m].zone->name); + break; + case MAPINFO_NPC_COUNT: + script_pushint(st, map->list[m].npc_num); + break; + default: + ShowError("buildin_getmapinfo: unknown option in second argument (%u).\n", mode); + script_pushint(st, -2); + return false; + } + + return true; +} + +static BUILDIN(getmapflag) { int16 m,i; const char *str; @@ -11808,14 +13572,19 @@ BUILDIN(getmapflag) case MF_RESET: script_pushint(st,map->list[m].flag.reset); break; case MF_NOTOMB: script_pushint(st,map->list[m].flag.notomb); break; case MF_NOCASHSHOP: script_pushint(st,map->list[m].flag.nocashshop); break; - case MF_NOVIEWID: script_pushint(st,map->list[m].flag.noviewid); break; + case MF_NOAUTOLOOT: script_pushint(st, map->list[m].flag.noautoloot); break; + case MF_NOVIEWID: script_pushint(st, map->list[m].flag.noviewid); break; + case MF_PAIRSHIP_STARTABLE: script_pushint(st, map->list[m].flag.pairship_startable); break; + case MF_PAIRSHIP_ENDABLE: script_pushint(st, map->list[m].flag.pairship_endable); break; + case MF_NOSTORAGE: script_pushint(st, map->list[m].flag.nostorage); break; + case MF_NOGSTORAGE: script_pushint(st, map->list[m].flag.nogstorage); break; } } return true; } /* pvp timer handling */ -int script_mapflag_pvp_sub(struct block_list *bl, va_list ap) +static int script_mapflag_pvp_sub(struct block_list *bl, va_list ap) { struct map_session_data *sd = NULL; @@ -11824,7 +13593,8 @@ int script_mapflag_pvp_sub(struct block_list *bl, va_list ap) sd = BL_UCAST(BL_PC, bl); if (sd->pvp_timer == INVALID_TIMER) { - sd->pvp_timer = timer->add(timer->gettick() + 200, pc->calc_pvprank_timer, sd->bl.id, 0); + if (!map->list[sd->bl.m].flag.pvp_nocalcrank) + sd->pvp_timer = timer->add(timer->gettick() + 200, pc->calc_pvprank_timer, sd->bl.id, 0); sd->pvp_rank = 0; sd->pvp_lastusers = 0; sd->pvp_point = 5; @@ -11836,7 +13606,8 @@ int script_mapflag_pvp_sub(struct block_list *bl, va_list ap) return 0; } -BUILDIN(setmapflag) { +static BUILDIN(setmapflag) +{ int16 m,i; const char *str, *val2 = NULL; int val=0; @@ -11875,6 +13646,7 @@ 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; @@ -11932,14 +13704,20 @@ BUILDIN(setmapflag) { case MF_RESET: map->list[m].flag.reset = 1; break; case MF_NOTOMB: map->list[m].flag.notomb = 1; break; case MF_NOCASHSHOP: map->list[m].flag.nocashshop = 1; break; + case MF_NOAUTOLOOT: map->list[m].flag.noautoloot = 1; break; case MF_NOVIEWID: map->list[m].flag.noviewid = (val <= 0) ? EQP_NONE : val; break; + case MF_PAIRSHIP_STARTABLE: map->list[m].flag.pairship_startable = 1; break; + case MF_PAIRSHIP_ENDABLE: map->list[m].flag.pairship_endable = 1; break; + case MF_NOSTORAGE: map->list[m].flag.nostorage = cap_value(val, 0, 3); break; + case MF_NOGSTORAGE: map->list[m].flag.nogstorage = cap_value(val, 0, 3); break; } } return true; } -BUILDIN(removemapflag) { +static BUILDIN(removemapflag) +{ int16 m,i; const char *str; @@ -11957,6 +13735,7 @@ 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; @@ -11968,6 +13747,7 @@ 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; @@ -12019,14 +13799,17 @@ BUILDIN(removemapflag) { case MF_RESET: map->list[m].flag.reset = 0; break; case MF_NOTOMB: map->list[m].flag.notomb = 0; break; case MF_NOCASHSHOP: map->list[m].flag.nocashshop = 0; break; + case MF_NOAUTOLOOT: map->list[m].flag.noautoloot = 0; break; case MF_NOVIEWID: map->list[m].flag.noviewid = EQP_NONE; break; + case MF_NOSTORAGE: map->list[m].flag.nostorage = 0; break; + case MF_NOGSTORAGE: map->list[m].flag.nogstorage = 0; break; } } return true; } -BUILDIN(pvpon) +static BUILDIN(pvpon) { int16 m; const char *str; @@ -12034,6 +13817,7 @@ 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 ) @@ -12059,7 +13843,8 @@ BUILDIN(pvpon) if( sd->bl.m != m || sd->pvp_timer != INVALID_TIMER ) continue; // not applicable - sd->pvp_timer = timer->add(timer->gettick()+200,pc->calc_pvprank_timer,sd->bl.id,0); + if (!map->list[m].flag.pvp_nocalcrank) + sd->pvp_timer = timer->add(timer->gettick()+200,pc->calc_pvprank_timer,sd->bl.id,0); sd->pvp_rank = 0; sd->pvp_lastusers = 0; sd->pvp_point = 5; @@ -12071,7 +13856,7 @@ BUILDIN(pvpon) return true; } -int buildin_pvpoff_sub(struct block_list *bl, va_list ap) +static int buildin_pvpoff_sub(struct block_list *bl, va_list ap) { struct map_session_data *sd = NULL; @@ -12087,11 +13872,13 @@ int buildin_pvpoff_sub(struct block_list *bl, va_list ap) return 0; } -BUILDIN(pvpoff) { +static BUILDIN(pvpoff) +{ int16 m; 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) @@ -12111,7 +13898,8 @@ BUILDIN(pvpoff) { return true; } -BUILDIN(gvgon) { +static BUILDIN(gvgon) +{ int16 m; const char *str; @@ -12120,6 +13908,7 @@ 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; @@ -12135,7 +13924,8 @@ BUILDIN(gvgon) { return true; } -BUILDIN(gvgoff) { +static BUILDIN(gvgoff) +{ int16 m; const char *str; @@ -12143,6 +13933,7 @@ 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); @@ -12158,7 +13949,8 @@ BUILDIN(gvgoff) { * emotion emotion#, <target: 0 - NPC, 1 - PC>, <NPC/PC name> *------------------------------------------*/ //Optional second parameter added by [Skotlex] -BUILDIN(emotion) { +static BUILDIN(emotion) +{ int type; int player=0; @@ -12187,7 +13979,7 @@ BUILDIN(emotion) { return true; } -int buildin_maprespawnguildid_sub_pc(struct map_session_data* sd, va_list ap) +static int buildin_maprespawnguildid_sub_pc(struct map_session_data *sd, va_list ap) { int16 m=va_arg(ap,int); int g_id=va_arg(ap,int); @@ -12204,7 +13996,7 @@ int buildin_maprespawnguildid_sub_pc(struct map_session_data* sd, va_list ap) return 1; } -int buildin_maprespawnguildid_sub_mob(struct block_list *bl, va_list ap) +static int buildin_maprespawnguildid_sub_mob(struct block_list *bl, va_list ap) { struct mob_data *md = NULL; @@ -12218,7 +14010,8 @@ int buildin_maprespawnguildid_sub_mob(struct block_list *bl, va_list ap) return 0; } -BUILDIN(maprespawnguildid) { +static BUILDIN(maprespawnguildid) +{ const char *mapname=script_getstr(st,2); int g_id=script_getnum(st,3); int flag=script_getnum(st,4); @@ -12235,28 +14028,32 @@ BUILDIN(maprespawnguildid) { return true; } -BUILDIN(agitstart) { +static BUILDIN(agitstart) +{ if(map->agit_flag==1) return true; // Agit already Start. map->agit_flag=1; guild->agit_start(); return true; } -BUILDIN(agitend) { +static BUILDIN(agitend) +{ if(map->agit_flag==0) return true; // Agit already End. map->agit_flag=0; guild->agit_end(); return true; } -BUILDIN(agitstart2) { +static BUILDIN(agitstart2) +{ if(map->agit2_flag==1) return true; // Agit2 already Start. map->agit2_flag=1; guild->agit2_start(); return true; } -BUILDIN(agitend2) { +static BUILDIN(agitend2) +{ if(map->agit2_flag==0) return true; // Agit2 already End. map->agit2_flag=0; guild->agit2_end(); @@ -12266,7 +14063,8 @@ BUILDIN(agitend2) { /*========================================== * Returns whether woe is on or off. *------------------------------------------*/ -BUILDIN(agitcheck) { +static BUILDIN(agitcheck) +{ script_pushint(st,map->agit_flag); return true; } @@ -12274,7 +14072,8 @@ BUILDIN(agitcheck) { /*========================================== * Returns whether woese is on or off. *------------------------------------------*/ -BUILDIN(agitcheck2) { +static BUILDIN(agitcheck2) +{ script_pushint(st,map->agit2_flag); return true; } @@ -12282,7 +14081,7 @@ BUILDIN(agitcheck2) { /// Sets the guild_id of this npc. /// /// flagemblem <guild_id>; -BUILDIN(flagemblem) +static BUILDIN(flagemblem) { struct npc_data *nd; int g_id = script_getnum(st,2); @@ -12307,7 +14106,7 @@ BUILDIN(flagemblem) return true; } -BUILDIN(getcastlename) +static BUILDIN(getcastlename) { const char* mapname = mapindex->getmapname(script_getstr(st,2),NULL); struct guild_castle* gc = guild->mapname2gc(mapname); @@ -12316,7 +14115,7 @@ BUILDIN(getcastlename) return true; } -BUILDIN(getcastledata) +static BUILDIN(getcastledata) { const char *mapname = mapindex->getmapname(script_getstr(st,2),NULL); int index = script_getnum(st,3); @@ -12359,7 +14158,7 @@ BUILDIN(getcastledata) return true; } -BUILDIN(setcastledata) +static BUILDIN(setcastledata) { const char *mapname = mapindex->getmapname(script_getstr(st,2),NULL); int index = script_getnum(st,3); @@ -12382,7 +14181,7 @@ BUILDIN(setcastledata) /* ===================================================================== * ---------------------------------------------------------------------*/ -BUILDIN(requestguildinfo) +static BUILDIN(requestguildinfo) { int guild_id=script_getnum(st,2); const char *event=NULL; @@ -12399,7 +14198,7 @@ BUILDIN(requestguildinfo) /// Returns the number of cards that have been compounded onto the specified equipped item. /// getequipcardcnt(<equipment slot>); -BUILDIN(getequipcardcnt) +static BUILDIN(getequipcardcnt) { int i=-1,j,num; struct map_session_data *sd; @@ -12436,149 +14235,116 @@ BUILDIN(getequipcardcnt) /// Removes all cards from the item found in the specified equipment slot of the invoking character, /// and give them to the character. If any cards were removed in this manner, it will also show a success effect. -/// successremovecards <slot>; -BUILDIN(successremovecards) +/// successremovecards(<slot>); +static BUILDIN(successremovecards) { - int i=-1,c,cardflag=0; + int i = -1, c, cardflag = 0; struct map_session_data *sd = script->rid2sd(st); - int num = script_getnum(st,2); + int num = script_getnum(st, 2); if (sd == NULL) return true; if (num > 0 && num <= ARRAYLENGTH(script->equip)) - i=pc->checkequip(sd,script->equip[num-1]); + i = pc->checkequip(sd,script->equip[num - 1]); - if (i < 0 || !sd->inventory_data[i]) { + if (i < 0 || sd->inventory_data[i] == NULL) return true; - } - if(itemdb_isspecial(sd->status.inventory[i].card[0])) + if (itemdb_isspecial(sd->status.inventory[i].card[0])) return true; - for( c = sd->inventory_data[i]->slot - 1; c >= 0; --c ) { - if( sd->status.inventory[i].card[c] && itemdb_type(sd->status.inventory[i].card[c]) == IT_CARD ) {// extract this card from the item + for (c = sd->inventory_data[i]->slot - 1; c >= 0; --c) { + if (sd->status.inventory[i].card[c] > 0 && itemdb_type(sd->status.inventory[i].card[c]) == IT_CARD) { int flag; struct item item_tmp; - memset(&item_tmp,0,sizeof(item_tmp)); + + memset(&item_tmp, 0, sizeof(item_tmp)); + cardflag = 1; - item_tmp.nameid = sd->status.inventory[i].card[c]; + item_tmp.nameid = sd->status.inventory[i].card[c]; item_tmp.identify = 1; + sd->status.inventory[i].card[c] = 0; - if((flag=pc->additem(sd,&item_tmp,1,LOG_TYPE_SCRIPT))) { - // get back the cart in inventory - clif->additem(sd,0,0,flag); - map->addflooritem(&sd->bl, &item_tmp, 1, sd->bl.m, sd->bl.x, sd->bl.y, 0, 0, 0, 0); + if ((flag = pc->additem(sd, &item_tmp, 1, LOG_TYPE_SCRIPT))) { + clif->additem(sd, 0, 0, flag); + map->addflooritem(&sd->bl, &item_tmp, 1, sd->bl.m, sd->bl.x, sd->bl.y, 0, 0, 0, 0, false); } } } if (cardflag == 1) { - //if card was remove replace item with no card - int flag, j; - struct item item_tmp; - memset(&item_tmp,0,sizeof(item_tmp)); - - item_tmp.nameid = sd->status.inventory[i].nameid; - item_tmp.identify = 1; - item_tmp.refine = sd->status.inventory[i].refine; - item_tmp.attribute = sd->status.inventory[i].attribute; - item_tmp.expire_time = sd->status.inventory[i].expire_time; - item_tmp.bound = sd->status.inventory[i].bound; - - for (j = sd->inventory_data[i]->slot; j < MAX_SLOTS; j++) - item_tmp.card[j]=sd->status.inventory[i].card[j]; - - pc->delitem(sd, i, 1, 0, DELITEM_MATERIALCHANGE, LOG_TYPE_SCRIPT); - if ((flag=pc->additem(sd,&item_tmp,1,LOG_TYPE_SCRIPT))) { - //chk if can be spawn in inventory otherwise put on floor - clif->additem(sd,0,0,flag); - map->addflooritem(&sd->bl, &item_tmp, 1, sd->bl.m, sd->bl.x, sd->bl.y, 0, 0, 0, 0); - } - + pc->unequipitem(sd, i, PCUNEQUIPITEM_FORCE); + clif->delitem(sd, i, 1, DELITEM_MATERIALCHANGE); + clif->additem(sd, i, 1, 0); + pc->equipitem(sd, i, sd->status.inventory[i].equip); clif->misceffect(&sd->bl,3); } return true; } /// Removes all cards from the item found in the specified equipment slot of the invoking character. -/// failedremovecards <slot>, <type>; +/// failedremovecards(<slot>, <type>); /// <type>=0 : will destroy both the item and the cards. /// <type>=1 : will keep the item, but destroy the cards. /// <type>=2 : will keep the cards, but destroy the item. -/// <type>=? : will just display the failure effect. -BUILDIN(failedremovecards) +/// <type>=3 : will just display the failure effect. +static BUILDIN(failedremovecards) { - int i=-1,c,cardflag=0; + int i = -1, c, cardflag = 0; + int num = script_getnum(st, 2); + int typefail = script_getnum(st, 3); struct map_session_data *sd = script->rid2sd(st); - int num = script_getnum(st,2); - int typefail = script_getnum(st,3); if (sd == NULL) return true; if (num > 0 && num <= ARRAYLENGTH(script->equip)) - i=pc->checkequip(sd,script->equip[num-1]); + i = pc->checkequip(sd, script->equip[num - 1]); - if (i < 0 || !sd->inventory_data[i]) + if (i < 0 || sd->inventory_data[i] == NULL) return true; - if(itemdb_isspecial(sd->status.inventory[i].card[0])) + if (itemdb_isspecial(sd->status.inventory[i].card[0])) return true; - for( c = sd->inventory_data[i]->slot - 1; c >= 0; --c ) { - if( sd->status.inventory[i].card[c] && itemdb_type(sd->status.inventory[i].card[c]) == IT_CARD ) { + for (c = sd->inventory_data[i]->slot - 1; c >= 0; --c) { + if (sd->status.inventory[i].card[c] > 0 && itemdb_type(sd->status.inventory[i].card[c]) == IT_CARD) { cardflag = 1; - if(typefail == 2) {// add cards to inventory, clear + if (typefail == 1) + sd->status.inventory[i].card[c] = 0; + + if (typefail == 2) { // add cards to inventory, clear int flag; struct item item_tmp; - memset(&item_tmp,0,sizeof(item_tmp)); + memset(&item_tmp, 0, sizeof(item_tmp)); - item_tmp.nameid = sd->status.inventory[i].card[c]; + item_tmp.nameid = sd->status.inventory[i].card[c]; item_tmp.identify = 1; - if((flag=pc->additem(sd,&item_tmp,1,LOG_TYPE_SCRIPT))) { - clif->additem(sd,0,0,flag); - map->addflooritem(&sd->bl, &item_tmp, 1, sd->bl.m, sd->bl.x, sd->bl.y, 0, 0, 0, 0); + if ((flag = pc->additem(sd, &item_tmp, 1, LOG_TYPE_SCRIPT))) { + clif->additem(sd, 0, 0, flag); + map->addflooritem(&sd->bl, &item_tmp, 1, sd->bl.m, sd->bl.x, sd->bl.y, 0, 0, 0, 0, false); } } } } if (cardflag == 1) { - if (typefail == 0 || typefail == 2) { - // destroy the item + if (typefail == 0 || typefail == 2) { // destroy the item pc->delitem(sd, i, 1, 0, DELITEM_FAILREFINE, LOG_TYPE_SCRIPT); } else if (typefail == 1) { - // destroy the card - int flag, j; - struct item item_tmp; - - memset(&item_tmp,0,sizeof(item_tmp)); - - item_tmp.nameid = sd->status.inventory[i].nameid; - item_tmp.identify = 1; - item_tmp.refine = sd->status.inventory[i].refine; - item_tmp.attribute = sd->status.inventory[i].attribute; - item_tmp.expire_time = sd->status.inventory[i].expire_time; - item_tmp.bound = sd->status.inventory[i].bound; - - for (j = sd->inventory_data[i]->slot; j < MAX_SLOTS; j++) - item_tmp.card[j]=sd->status.inventory[i].card[j]; - - pc->delitem(sd, i, 1, 0, DELITEM_FAILREFINE, LOG_TYPE_SCRIPT); - - if((flag=pc->additem(sd,&item_tmp,1,LOG_TYPE_SCRIPT))) { - clif->additem(sd,0,0,flag); - map->addflooritem(&sd->bl, &item_tmp, 1, sd->bl.m, sd->bl.x, sd->bl.y, 0, 0, 0, 0); - } + pc->unequipitem(sd, i, PCUNEQUIPITEM_FORCE); + clif->delitem(sd, i, 1, DELITEM_MATERIALCHANGE); + clif->additem(sd, i, 1, 0); + pc->equipitem(sd, i, sd->status.inventory[i].equip); } - clif->misceffect(&sd->bl,2); } + clif->misceffect(&sd->bl, 2); return true; } @@ -12589,7 +14355,8 @@ BUILDIN(failedremovecards) * improved by [Lance] * ================================================================*/ // Added by RoVeRT -BUILDIN(mapwarp) { +static BUILDIN(mapwarp) +{ int x,y,m,check_val=0,check_ID=0,i=0; struct guild *g = NULL; struct party_data *p = NULL; @@ -12642,7 +14409,7 @@ BUILDIN(mapwarp) { } // Added by RoVeRT -int buildin_mobcount_sub(struct block_list *bl, va_list ap) +static int buildin_mobcount_sub(struct block_list *bl, va_list ap) { char *event = va_arg(ap,char *); const struct mob_data *md = NULL; @@ -12657,7 +14424,8 @@ int buildin_mobcount_sub(struct block_list *bl, va_list ap) } // Added by RoVeRT -BUILDIN(mobcount) { +static BUILDIN(mobcount) +{ const char *mapname,*event; int16 m; mapname=script_getstr(st,2); @@ -12690,7 +14458,8 @@ BUILDIN(mobcount) { return true; } -BUILDIN(marriage) { +static BUILDIN(marriage) +{ const char *partner=script_getstr(st,2); struct map_session_data *sd = script->rid2sd(st); struct map_session_data *p_sd = script->nick2sd(st, partner); @@ -12702,7 +14471,7 @@ BUILDIN(marriage) { script_pushint(st,1); return true; } -BUILDIN(wedding_effect) +static BUILDIN(wedding_effect) { struct map_session_data *sd = script->rid2sd(st); struct block_list *bl; @@ -12714,7 +14483,7 @@ BUILDIN(wedding_effect) clif->wedding_effect(bl); return true; } -BUILDIN(divorce) +static BUILDIN(divorce) { struct map_session_data *sd = script->rid2sd(st); if (sd == NULL || pc->divorce(sd) < 0) { @@ -12725,7 +14494,8 @@ BUILDIN(divorce) return true; } -BUILDIN(ispartneron) { +static BUILDIN(ispartneron) +{ struct map_session_data *sd = script->rid2sd(st); if (sd==NULL || !pc->ismarried(sd) @@ -12738,7 +14508,7 @@ BUILDIN(ispartneron) { return true; } -BUILDIN(getpartnerid) +static BUILDIN(getpartnerid) { struct map_session_data *sd = script->rid2sd(st); if (sd == NULL) @@ -12748,7 +14518,7 @@ BUILDIN(getpartnerid) return true; } -BUILDIN(getchildid) +static BUILDIN(getchildid) { struct map_session_data *sd = script->rid2sd(st); if (sd == NULL) @@ -12758,7 +14528,7 @@ BUILDIN(getchildid) return true; } -BUILDIN(getmotherid) +static BUILDIN(getmotherid) { struct map_session_data *sd = script->rid2sd(st); if (sd == NULL) @@ -12768,7 +14538,8 @@ BUILDIN(getmotherid) return true; } -BUILDIN(getfatherid) { +static BUILDIN(getfatherid) +{ struct map_session_data *sd = script->rid2sd(st); if (sd == NULL) return true; @@ -12777,7 +14548,7 @@ BUILDIN(getfatherid) { return true; } -BUILDIN(warppartner) +static BUILDIN(warppartner) { int x,y; unsigned short map_index; @@ -12807,7 +14578,7 @@ BUILDIN(warppartner) /*================================================ * Script for Displaying MOB Information [Valaris] *------------------------------------------------*/ -BUILDIN(strmobinfo) +static BUILDIN(strmobinfo) { int num=script_getnum(st,2); @@ -12837,52 +14608,60 @@ BUILDIN(strmobinfo) return true; } -/*========================================== - * Summon guardians [Valaris] - * guardian("<map name>",<x>,<y>,"<name to show>",<mob id>{,"<event label>"}{,<guardian index>}) -> <id> - *------------------------------------------*/ -BUILDIN(guardian) { - int class_ = 0, x = 0, y = 0, guardian = 0; - const char *str, *mapname, *evt=""; +/** + * Summons a castle guardian mob. + * + * @code{.herc} + * guardian("<map name>", <x>, <y>, "<name to show>", <mob id>{, <guardian index>}); + * guardian("<map name>", <x>, <y>, "<name to show>", <mob id>{, "<event label>"{, <guardian index>}}); + * @endcode + * + * @author Valaris + * + **/ +static BUILDIN(guardian) +{ bool has_index = false; + int guardian = 0; + const char *event = ""; - mapname = script_getstr(st,2); - x = script_getnum(st,3); - y = script_getnum(st,4); - str = script_getstr(st,5); - class_ = script_getnum(st,6); - - if( script_hasdata(st,8) ) - {// "<event label>",<guardian index> - evt=script_getstr(st,7); - guardian=script_getnum(st,8); + if (script_hasdata(st, 8)) { /// "<event label>", <guardian index> + event = script_getstr(st, 7); + script->check_event(st, event); + guardian = script_getnum(st, 8); has_index = true; - } else if( script_hasdata(st,7) ) { - struct script_data *data = script_getdata(st,7); - script->get_val(st,data); // Dereference if it's a variable - if( data_isstring(data) ) { - // "<event label>" - evt=script_getstr(st,7); - } else if( data_isint(data) ) { - // <guardian index> - guardian=script_getnum(st,7); + } else if (script_hasdata(st, 7)) { + struct script_data *data = script_getdata(st, 7); + + script->get_val(st, data); /// Dereference if it's a variable. + + if (data_isstring(data)) { /// "<event label>" + event = script_getstr(st, 7); + script->check_event(st, event); + } else if (data_isint(data)) { /// <guardian index> + guardian = script_getnum(st, 7); has_index = true; } else { - ShowError("script:guardian: invalid data type for argument #6 (from 1)\n"); + ShowError("script:guardian: Invalid data type for argument #6!\n"); script->reportdata(data); return false; } } - script->check_event(st, evt); - script_pushint(st, mob->spawn_guardian(mapname,x,y,str,class_,evt,guardian,has_index)); + const char *mapname = script_getstr(st, 2); + const char *name = script_getstr(st, 5); + const int x = script_getnum(st, 3); + const int y = script_getnum(st, 4); + const int mob_id = script_getnum(st, 6); + script_pushint(st, mob->spawn_guardian(mapname, x, y, name, mob_id, event, guardian, has_index, st->oid)); return true; } /*========================================== * Invisible Walls [Zephyrus] *------------------------------------------*/ -BUILDIN(setwall) { +static BUILDIN(setwall) +{ const char *mapname, *name; int x, y, m, size, dir; bool shootable; @@ -12901,9 +14680,15 @@ BUILDIN(setwall) { map->iwall_set(m, x, y, size, dir, shootable, name); return true; } -BUILDIN(delwall) { + +static BUILDIN(delwall) +{ const char *name = script_getstr(st,2); - map->iwall_remove(name); + + if (!map->iwall_remove(name)) { + ShowWarning("buildin_delwall: Non-existent '%s' provided.\n", name); + return false; + } return true; } @@ -12915,7 +14700,8 @@ BUILDIN(delwall) { /// 1 - maximum hp /// 2 - current hp /// -BUILDIN(guardianinfo) { +static BUILDIN(guardianinfo) +{ const char* mapname = mapindex->getmapname(script_getstr(st,2),NULL); int id = script_getnum(st,3); int type = script_getnum(st,4); @@ -12947,7 +14733,8 @@ BUILDIN(guardianinfo) { /*========================================== * Get the item name by item_id or null *------------------------------------------*/ -BUILDIN(getitemname) { +static BUILDIN(getitemname) +{ int item_id=0; struct item_data *i_data; char *item_name; @@ -12975,7 +14762,7 @@ BUILDIN(getitemname) { /*========================================== * Returns number of slots an item has. [Skotlex] *------------------------------------------*/ -BUILDIN(getitemslots) +static BUILDIN(getitemslots) { int item_id; struct item_data *i_data; @@ -12991,88 +14778,529 @@ BUILDIN(getitemslots) return true; } -// TODO: add matk here if needed/once we get rid of RENEWAL +/** + * Returns various information about an item. + * + * @code{.herc} + * getiteminfo(<item ID>, <type>); + * getiteminfo("<item name>", <type>); + * @endcode + * + **/ +static BUILDIN(getiteminfo) +{ + struct item_data *it; -/*========================================== - * Returns some values of an item [Lupus] - * Price, Weight, etc... - * getiteminfo(itemID,n), where n - * 0 value_buy; - * 1 value_sell; - * 2 type; - * 3 maxchance = Max drop chance of this item e.g. 1 = 0.01% , etc.. - * if = 0, then monsters don't drop it at all (rare or a quest item) - * if = -1, then this item is sold in NPC shops only - * 4 sex; - * 5 equip; - * 6 weight; - * 7 atk; - * 8 def; - * 9 range; - * 10 slot; - * 11 look; - * 12 elv; - * 13 wlv; - * 14 view id - *------------------------------------------*/ -BUILDIN(getiteminfo) + if (script_isstringtype(st, 2)) { /// Item name. + const char *name = script_getstr(st, 2); + it = itemdb->search_name(name); + } else { /// Item ID. + it = itemdb->exists(script_getnum(st, 2)); + } + + if (it == NULL) { + script_pushint(st, -1); + return true; + } + + int type = script_getnum(st, 3); + + switch (type) { + case ITEMINFO_BUYPRICE: + script_pushint(st, it->value_buy); + break; + case ITEMINFO_SELLPRICE: + script_pushint(st, it->value_sell); + break; + case ITEMINFO_TYPE: + script_pushint(st, it->type); + break; + case ITEMINFO_MAXCHANCE: + script_pushint(st, it->maxchance); + break; + case ITEMINFO_SEX: + script_pushint(st, it->sex); + break; + case ITEMINFO_LOC: + script_pushint(st, it->equip); + break; + case ITEMINFO_WEIGHT: + script_pushint(st, it->weight); + break; + case ITEMINFO_ATK: + script_pushint(st, it->atk); + break; + case ITEMINFO_DEF: + script_pushint(st, it->def); + break; + case ITEMINFO_RANGE: + script_pushint(st, it->range); + break; + case ITEMINFO_SLOTS: + script_pushint(st, it->slot); + break; + case ITEMINFO_SUBTYPE: + script_pushint(st, it->subtype); + break; + case ITEMINFO_ELV: + script_pushint(st, it->elv); + break; + case ITEMINFO_WLV: + script_pushint(st, it->wlv); + break; + case ITEMINFO_VIEWID: + script_pushint(st, it->view_id); + break; + case ITEMINFO_MATK: + script_pushint(st, it->matk); + break; + case ITEMINFO_VIEWSPRITE: + script_pushint(st, it->view_sprite); + break; + case ITEMINFO_TRADE: + script_pushint(st, it->flag.trade_restriction); + break; + case ITEMINFO_ELV_MAX: + script_pushint(st, it->elvmax); + break; + case ITEMINFO_DROPEFFECT_MODE: + script_pushint(st, it->dropeffectmode); + break; + case ITEMINFO_DELAY: + script_pushint(st, it->delay); + break; + case ITEMINFO_CLASS_BASE_1: + script_pushint(st, it->class_base[0]); + break; + case ITEMINFO_CLASS_BASE_2: + script_pushint(st, it->class_base[1]); + break; + case ITEMINFO_CLASS_BASE_3: + script_pushint(st, it->class_base[2]); + break; + case ITEMINFO_CLASS_UPPER: + script_pushint(st, it->class_upper); + break; + case ITEMINFO_FLAG_NO_REFINE: + script_pushint(st, it->flag.no_refine); + break; + case ITEMINFO_FLAG_DELAY_CONSUME: + script_pushint(st, it->flag.delay_consume); + break; + case ITEMINFO_FLAG_AUTOEQUIP: + script_pushint(st, it->flag.autoequip); + break; + case ITEMINFO_FLAG_AUTO_FAVORITE: + script_pushint(st, it->flag.auto_favorite); + break; + case ITEMINFO_FLAG_BUYINGSTORE: + script_pushint(st, it->flag.buyingstore); + break; + case ITEMINFO_FLAG_BINDONEQUIP: + script_pushint(st, it->flag.bindonequip); + break; + case ITEMINFO_FLAG_KEEPAFTERUSE: + script_pushint(st, it->flag.keepafteruse); + break; + case ITEMINFO_FLAG_FORCE_SERIAL: + script_pushint(st, it->flag.force_serial); + break; + case ITEMINFO_FLAG_NO_OPTIONS: + script_pushint(st, it->flag.no_options); + break; + case ITEMINFO_FLAG_DROP_ANNOUNCE: + script_pushint(st, it->flag.drop_announce); + break; + case ITEMINFO_FLAG_SHOWDROPEFFECT: + script_pushint(st, it->flag.showdropeffect); + break; + case ITEMINFO_STACK_AMOUNT: + script_pushint(st, it->stack.amount); + break; + case ITEMINFO_STACK_FLAG: { + int stack_flag = 0; + + if (it->stack.inventory != 0) + stack_flag |= 1; + + if (it->stack.cart != 0) + stack_flag |= 2; + + if (it->stack.storage != 0) + stack_flag |= 4; + + if (it->stack.guildstorage != 0) + stack_flag |= 8; + + script_pushint(st, stack_flag); + break; + } + case ITEMINFO_ITEM_USAGE_FLAG: + script_pushint(st, it->item_usage.flag); + break; + case ITEMINFO_ITEM_USAGE_OVERRIDE: + script_pushint(st, it->item_usage.override); + break; + case ITEMINFO_GM_LV_TRADE_OVERRIDE: + script_pushint(st, it->gm_lv_trade_override); + break; + case ITEMINFO_ID: + script_pushint(st, it->nameid); + break; + case ITEMINFO_AEGISNAME: + script_pushstrcopy(st, it->name); + break; + case ITEMINFO_NAME: + script_pushstrcopy(st, it->jname); + break; + default: + ShowError("buildin_getiteminfo: Invalid item info type %d.\n", type); + script_pushint(st, -1); + return false; + } + + return true; +} + +/** + * Returns the value of the current equipment being parsed using static variables - + * current_equip_item_index and current_equip_option_index. + * !!Designed to be used with item_options.conf only!! + * *getequippedoptioninfo(<info_type>); + * + * @param (int) Types - + * IT_OPT_INDEX ID of the item option. + * IT_OPT_VALUE Amount of the bonus to be added. + * @return value of the type or -1. + */ +static BUILDIN(getequippedoptioninfo) { - int item_id,n; - struct item_data *i_data; + int val = 0, type = script_getnum(st, 2); + struct map_session_data *sd = NULL; + + if ((sd = script->rid2sd(st)) == NULL || status->current_equip_item_index == -1 || status->current_equip_option_index == -1 + || !sd->status.inventory[status->current_equip_item_index].option[status->current_equip_option_index].index) { + script_pushint(st, -1); + return false; + } + + switch (type) { + case IT_OPT_INDEX: + val = sd->status.inventory[status->current_equip_item_index].option[status->current_equip_option_index].index; + break; + case IT_OPT_VALUE: + val = sd->status.inventory[status->current_equip_item_index].option[status->current_equip_option_index].value; + break; + default: + ShowError("buildin_getequippedoptioninfo: Invalid option data type %d (Max %d).\n", type, IT_OPT_MAX-1); + script_pushint(st, -1); + return false; + } + + script_pushint(st, val); + + return true; +} - item_id = script_getnum(st,2); - n = script_getnum(st,3); - i_data = itemdb->exists(item_id); +/** + * Gets the option information of an equipment. + * *getequipoption(<equip_index>,<slot>,<type>); + * + * @param equip_index as the Index of the Equipment. + * @param slot as the slot# of the Item Option (1 to MAX_ITEM_OPTIONS) + * @param type IT_OPT_INDEX or IT_OPT_VALUE. + * @return (int) value or -1 on failure. + */ +static BUILDIN(getequipoption) +{ + int val = 0, equip_index = script_getnum(st, 2); + int slot = script_getnum(st, 3); + int opt_type = script_getnum(st, 4); + int i = -1; + struct map_session_data *sd = script->rid2sd(st); - if (i_data && n>=0 && n<=14) { - int *item_arr = (int*)&i_data->value_buy; - script_pushint(st,item_arr[n]); + if (sd == NULL) { + script_pushint(st, -1); + ShowError("buildin_getequipoption: Player not attached!\n"); + return false; + } + + if (slot <= 0 || slot > MAX_ITEM_OPTIONS) { + script_pushint(st, -1); + ShowError("buildin_getequipoption: Invalid option slot %d (Min: 1, Max: %d) provided.\n", slot, MAX_ITEM_OPTIONS); + return false; + } + + if (equip_index > 0 && equip_index <= ARRAYLENGTH(script->equip)) { + if ((i = pc->checkequip(sd, script->equip[equip_index - 1])) == -1) { + ShowError("buildin_getequipoption: No equipment is equipped in the given index %d.\n", equip_index); + script_pushint(st, -1); + return false; + } } else { - script_pushint(st,-1); + ShowError("buildin_getequipoption: Invalid equipment index %d provided.\n", equip_index); + script_pushint(st, 0); + return false; } + + if (sd->status.inventory[i].nameid != 0) { + switch (opt_type) { + case IT_OPT_INDEX: + val = sd->status.inventory[i].option[slot-1].index; + break; + case IT_OPT_VALUE: + val = sd->status.inventory[i].option[slot-1].value; + break; + default: + ShowError("buildin_getequipoption: Invalid option data type %d provided.\n", opt_type); + script_pushint(st, -1); + break; + } + } + + script_pushint(st, val); + return true; } +/** + * Set an equipment's option value. + * *setequipoption(<equip_index>,<slot>,<opt_index>,<value>); + * + * @param equip_index as the inventory index of the equipment. + * @param slot as the slot of the item option (1 to MAX_ITEM_OPTIONS) + * @param opt_index as the index of the option available as "Id" in db/item_options.conf. + * @param value as the value of the option type. + * For IT_OPT_INDEX see "Name" in item_options.conf + * For IT_OPT_VALUE, the value of the script bonus. + * @return 0 on failure, 1 on success. + */ +static BUILDIN(setequipoption) +{ + int equip_index = script_getnum(st, 2); + int slot = script_getnum(st, 3); + int opt_index = script_getnum(st, 4); + int value = script_getnum(st, 5); + int i = -1; + + struct map_session_data *sd = script->rid2sd(st); + struct itemdb_option *ito = NULL; + + if (sd == NULL) { + script_pushint(st, 0); + ShowError("buildin_setequipoption: Player not attached!\n"); + return false; + } + + if (slot <= 0 || slot > MAX_ITEM_OPTIONS) { + script_pushint(st, 0); + ShowError("buildin_setequipoption: Invalid option index %d (Min: 1, Max: %d) provided.\n", slot, MAX_ITEM_OPTIONS); + return false; + } + + if (equip_index > 0 && equip_index <= ARRAYLENGTH(script->equip)) { + if ((i = pc->checkequip(sd, script->equip[equip_index - 1])) == -1) { + ShowError("buildin_setequipoption: No equipment is equipped in the given index %d.\n", equip_index); + script_pushint(st, 0); + return false; + } + } else { + ShowError("buildin_setequipoption: Invalid equipment index %d provided.\n", equip_index); + script_pushint(st, 0); + return false; + } + + if (sd->status.inventory[i].nameid != 0) { + if (opt_index == 0) { + // Remove the option + sd->status.inventory[i].option[slot-1].index = 0; + sd->status.inventory[i].option[slot-1].value = 0; + } else { + if ((ito = itemdb->option_exists(opt_index)) == NULL) { + script_pushint(st, 0); + ShowError("buildin_setequipotion: Option index %d does not exist!\n", opt_index); + return false; + } else if (value < -INT16_MAX || value > INT16_MAX) { + script_pushint(st, 0); + ShowError("buildin_setequipotion: Option value %d exceeds maximum limit (%d to %d) for type!\n", value, -INT16_MAX, INT16_MAX); + return false; + } + /* Add Option Index */ + sd->status.inventory[i].option[slot-1].index = ito->index; + /* Add Option Value */ + sd->status.inventory[i].option[slot-1].value = value; + } + + /* Unequip and simulate deletion of the item. */ + pc->unequipitem(sd, i, PCUNEQUIPITEM_FORCE); // status calc will happen in pc->equipitem() below + clif->refine(sd->fd, 0, i, sd->status.inventory[i].refine); // notify client of a refine. + clif->delitem(sd, i, 1, DELITEM_MATERIALCHANGE); // notify client to simulate item deletion. + /* Log deletion of the item. */ + logs->pick_pc(sd, LOG_TYPE_SCRIPT, -1, &sd->status.inventory[i],sd->inventory_data[i]); + /* Equip and simulate addition of the item. */ + clif->additem(sd, i, 1, 0); // notify client to simulate item addition. + /* Log addition of the item. */ + logs->pick_pc(sd, LOG_TYPE_SCRIPT, 1, &sd->status.inventory[i], sd->inventory_data[i]); + pc->equipitem(sd, i, sd->status.inventory[i].equip); // force equip the item at the original position. + clif->misceffect(&sd->bl, 2); // show effect + } + + script_pushint(st, 1); + + return true; + +} + /*========================================== * Set some values of an item [Lupus] * Price, Weight, etc... - * setiteminfo(itemID,n,Value), where n - * 0 value_buy; - * 1 value_sell; - * 2 type; - * 3 maxchance = Max drop chance of this item e.g. 1 = 0.01% , etc.. - * if = 0, then monsters don't drop it at all (rare or a quest item) - * if = -1, then this item is sold in NPC shops only - * 4 sex; - * 5 equip; - * 6 weight; - * 7 atk; - * 8 def; - * 9 range; - * 10 slot; - * 11 look; - * 12 elv; - * 13 wlv; - * 14 view id - * Returns Value or -1 if the wrong field's been set *------------------------------------------*/ -BUILDIN(setiteminfo) +static BUILDIN(setiteminfo) { - int item_id,n,value; - struct item_data *i_data; + // TODO: Validate data in a similar way as during database load + int item_id = script_getnum(st, 2); + int n = script_getnum(st, 3); + int value = script_getnum(st,4); + struct item_data *it = itemdb->exists(item_id); - item_id = script_getnum(st,2); - n = script_getnum(st,3); - value = script_getnum(st,4); - i_data = itemdb->exists(item_id); + if (it == NULL) { + script_pushint(st, -1); + return true; + } - if (i_data && n>=0 && n<=14) { - int *item_arr = (int*)&i_data->value_buy; - item_arr[n] = value; - script_pushint(st,value); - } else { + switch (n) { + case ITEMINFO_BUYPRICE: + it->value_buy = value; + break; + case ITEMINFO_SELLPRICE: + it->value_sell = value; + break; + case ITEMINFO_TYPE: + it->type = value; + break; + case ITEMINFO_MAXCHANCE: + it->maxchance = value; + break; + case ITEMINFO_SEX: + it->sex = value; + break; + case ITEMINFO_LOC: + it->equip = value; + break; + case ITEMINFO_WEIGHT: + it->weight = value; + break; + case ITEMINFO_ATK: + it->atk = value; + break; + case ITEMINFO_DEF: + it->def = value; + break; + case ITEMINFO_RANGE: + it->range = value; + break; + case ITEMINFO_SLOTS: + it->slot = value; + break; + case ITEMINFO_SUBTYPE: + it->subtype = value; + break; + case ITEMINFO_ELV: + it->elv = value; + break; + case ITEMINFO_WLV: + it->wlv = value; + break; + case ITEMINFO_VIEWID: + it->view_id = value; + break; + case ITEMINFO_MATK: + it->matk = value; + break; + case ITEMINFO_VIEWSPRITE: + it->view_sprite = value; + break; + case ITEMINFO_TRADE: + it->flag.trade_restriction = value; + break; + case ITEMINFO_ELV_MAX: + it->elvmax = cap_value(value, 0, MAX_LEVEL); + break; + case ITEMINFO_DROPEFFECT_MODE: + it->dropeffectmode = value; + break; + case ITEMINFO_DELAY: + it->delay = value; + break; + case ITEMINFO_CLASS_BASE_1: + it->class_base[0] = value; + break; + case ITEMINFO_CLASS_BASE_2: + it->class_base[1] = value; + break; + case ITEMINFO_CLASS_BASE_3: + it->class_base[2] = value; + break; + case ITEMINFO_CLASS_UPPER: + it->class_upper = value; + break; + case ITEMINFO_FLAG_NO_REFINE: + it->flag.no_refine = cap_value(value, 0, MAX_REFINE); + break; + case ITEMINFO_FLAG_DELAY_CONSUME: + it->flag.delay_consume = value; + break; + case ITEMINFO_FLAG_AUTOEQUIP: + it->flag.autoequip = cap_value(value, 0, 1); + break; + case ITEMINFO_FLAG_AUTO_FAVORITE: + it->flag.auto_favorite = cap_value(value, 0, 1); + break; + case ITEMINFO_FLAG_BUYINGSTORE: + it->flag.buyingstore = cap_value(value, 0, 1); + break; + case ITEMINFO_FLAG_BINDONEQUIP: + it->flag.bindonequip = cap_value(value, 0, 1); + break; + case ITEMINFO_FLAG_KEEPAFTERUSE: + it->flag.keepafteruse = cap_value(value, 0, 1); + break; + case ITEMINFO_FLAG_FORCE_SERIAL: + it->flag.force_serial = cap_value(value, 0, 1); + break; + case ITEMINFO_FLAG_NO_OPTIONS: + it->flag.no_options = cap_value(value, 0, 1); + break; + case ITEMINFO_FLAG_DROP_ANNOUNCE: + it->flag.drop_announce = cap_value(value, 0, 1); + break; + case ITEMINFO_FLAG_SHOWDROPEFFECT: + it->flag.showdropeffect = cap_value(value, 0, 1); + break; + case ITEMINFO_STACK_AMOUNT: + it->stack.amount = cap_value(value, 0, USHRT_MAX); + break; + case ITEMINFO_STACK_FLAG: + it->stack.inventory = ((value & 1) != 0); + it->stack.cart = ((value & 2) != 0); + it->stack.storage = ((value & 4) != 0); + it->stack.guildstorage = ((value & 8) != 0); + break; + case ITEMINFO_ITEM_USAGE_FLAG: + it->item_usage.flag = cap_value(value, 0, 1); + break; + case ITEMINFO_ITEM_USAGE_OVERRIDE: + it->item_usage.override = value; + break; + case ITEMINFO_GM_LV_TRADE_OVERRIDE: + it->gm_lv_trade_override = value; + break; + default: + ShowError("buildin_setiteminfo: invalid type %d.\n", n); script_pushint(st,-1); + return false; } + script_pushint(st,value); return true; } @@ -13088,7 +15316,7 @@ BUILDIN(setiteminfo) * Useful for such quests as "Sign this refined item with players name" etc * Hat[0] +4 -> Player's Hat[0] +4 *------------------------------------------*/ -BUILDIN(getequipcardid) +static BUILDIN(getequipcardid) { int i=-1,num,slot; struct map_session_data *sd; @@ -13113,7 +15341,7 @@ BUILDIN(getequipcardid) /*========================================== * petskillbonus [Valaris] //Rewritten by [Skotlex] *------------------------------------------*/ -BUILDIN(petskillbonus) +static BUILDIN(petskillbonus) { struct pet_data *pd; @@ -13150,7 +15378,7 @@ BUILDIN(petskillbonus) /*========================================== * pet looting [Valaris] //Rewritten by [Skotlex] *------------------------------------------*/ -BUILDIN(petloot) +static BUILDIN(petloot) { int max; struct pet_data *pd; @@ -13187,96 +15415,119 @@ BUILDIN(petloot) * Set arrays with info of all sd inventory : * @inventorylist_id, @inventorylist_amount, @inventorylist_equip, * @inventorylist_refine, @inventorylist_identify, @inventorylist_attribute, - * @inventorylist_card(0..3), @inventorylist_expire + * @inventorylist_card(0..3), + * @inventorylist_opt_id(0..MAX_ITEM_OPTIONS), + * @inventorylist_opt_val(0..MAX_ITEM_OPTIONS), + * @inventorylist_opt_param(0..MAX_ITEM_OPTIONS), + * @inventorylist_expire, @inventorylist_bound, @inventorylist_favorite, + * @inventorylist_idx * @inventorylist_count = scalar *------------------------------------------*/ -BUILDIN(getinventorylist) +static BUILDIN(getinventorylist) { struct map_session_data *sd = script->rid2sd(st); - char card_var[NAME_LENGTH]; + char script_var[SCRIPT_VARNAME_LENGTH]; + int j = 0, k = 0; - int i,j=0,k; - if(!sd) return true; + if (sd == NULL) + return true; - for(i=0;i<MAX_INVENTORY;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); - if(sd->status.inventory[i].equip) { - pc->setreg(sd,reference_uid(script->add_str("@inventorylist_equip"), j),pc->equippoint(sd,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_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 != 0) { + 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]); + sprintf(script_var, "@inventorylist_card%d", k + 1); + pc->setreg(sd, reference_uid(script->add_variable(script_var), j), sd->status.inventory[i].card[k]); } - 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); + for (k = 0; k < MAX_ITEM_OPTIONS; k++) { + sprintf(script_var, "@inventorylist_opt_id%d", k + 1); + pc->setreg(sd, reference_uid(script->add_variable(script_var), j), sd->status.inventory[i].option[k].index); + sprintf(script_var, "@inventorylist_opt_val%d", k + 1); + pc->setreg(sd, reference_uid(script->add_variable(script_var), j), sd->status.inventory[i].option[k].value); + sprintf(script_var, "@inventorylist_opt_param%d", k + 1); + pc->setreg(sd, reference_uid(script->add_variable(script_var), j), sd->status.inventory[i].option[k].param); + } + 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); + pc->setreg(sd, reference_uid(script->add_variable("@inventorylist_favorite"), j), sd->status.inventory[i].favorite); + pc->setreg(sd, reference_uid(script->add_variable("@inventorylist_idx"), j), i); j++; } } - pc->setreg(sd,script->add_str("@inventorylist_count"),j); + pc->setreg(sd, script->add_variable("@inventorylist_count"), j); return true; } -BUILDIN(getcartinventorylist) +static BUILDIN(getcartinventorylist) { struct map_session_data *sd = script->rid2sd(st); - char card_var[26]; + char card_var[SCRIPT_VARNAME_LENGTH]; int i,j=0,k; if(!sd) return true; 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_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_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_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; } -BUILDIN(getskilllist) +static BUILDIN(getskilllist) { struct map_session_data *sd = script->rid2sd(st); int i,j=0; if (sd == NULL) return true; - for(i=0;i<MAX_SKILL;i++) { + 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; } -BUILDIN(clearitem) +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); } @@ -13287,7 +15538,7 @@ BUILDIN(clearitem) /*========================================== * Disguise Player (returns Mob/NPC ID if success, 0 on fail) *------------------------------------------*/ -BUILDIN(disguise) +static BUILDIN(disguise) { int id; struct map_session_data *sd = script->rid2sd(st); @@ -13308,7 +15559,7 @@ BUILDIN(disguise) /*========================================== * Undisguise Player (returns 1 if success, 0 on fail) *------------------------------------------*/ -BUILDIN(undisguise) +static BUILDIN(undisguise) { struct map_session_data *sd = script->rid2sd(st); if (sd == NULL) @@ -13327,22 +15578,33 @@ BUILDIN(undisguise) * Transform a bl to another class, * @type unused *------------------------------------------*/ -BUILDIN(classchange) { - int class_,type; - struct block_list *bl=map->id2bl(st->oid); +static BUILDIN(classchange) +{ + int class, type, target; + struct block_list *bl = map->id2bl(st->oid); + + if (bl == NULL) + return true; - if(bl==NULL) return true; + class = script_getnum(st, 2); + type = script_getnum(st, 3); + target = script_hasdata(st, 4) ? script_getnum(st, 4) : 0; - class_=script_getnum(st,2); - type=script_getnum(st,3); - clif->class_change(bl,class_,type); + if (target > 0) { + struct map_session_data *sd = script->charid2sd(st, target); + if (sd != NULL) { + clif->class_change(bl, class, type, sd); + } + } else { + clif->class_change(bl, class, type, NULL); + } return true; } /*========================================== * Display an effect *------------------------------------------*/ -BUILDIN(misceffect) +static BUILDIN(misceffect) { int type; @@ -13361,7 +15623,7 @@ BUILDIN(misceffect) /*========================================== * Play a BGM on a single client [Rikter/Yommy] *------------------------------------------*/ -BUILDIN(playbgm) +static BUILDIN(playbgm) { struct map_session_data* sd = script->rid2sd(st); @@ -13374,7 +15636,7 @@ BUILDIN(playbgm) return true; } -int playbgm_sub(struct block_list* bl,va_list ap) +static int playbgm_sub(struct block_list *bl, va_list ap) { const char* name = va_arg(ap,const char*); @@ -13383,10 +15645,11 @@ int playbgm_sub(struct block_list* bl,va_list ap) return 0; } -int playbgm_foreachpc_sub(struct map_session_data* sd, va_list args) +static int playbgm_foreachpc_sub(struct map_session_data *sd, va_list args) { const char* name = va_arg(args, const char*); + nullpo_ret(name); clif->playBGM(sd, name); return 0; } @@ -13394,7 +15657,8 @@ int playbgm_foreachpc_sub(struct map_session_data* sd, va_list args) /*========================================== * Play a BGM on multiple client [Rikter/Yommy] *------------------------------------------*/ -BUILDIN(playbgmall) { +static BUILDIN(playbgmall) +{ const char* name; name = script_getstr(st,2); @@ -13436,7 +15700,7 @@ BUILDIN(playbgmall) { /*========================================== * Play a .wav sound for sd *------------------------------------------*/ -BUILDIN(soundeffect) +static BUILDIN(soundeffect) { struct map_session_data *sd = script->rid2sd(st); const char* name = script_getstr(st,2); @@ -13448,7 +15712,7 @@ BUILDIN(soundeffect) return true; } -int soundeffect_sub(struct block_list *bl, va_list ap) +static int soundeffect_sub(struct block_list *bl, va_list ap) { struct map_session_data *sd = NULL; char *name = va_arg(ap, char *); @@ -13467,7 +15731,8 @@ int soundeffect_sub(struct block_list *bl, va_list ap) * Play a sound effect (.wav) on multiple clients * soundeffectall "<filepath>",<type>{,"<map name>"}{,<x0>,<y0>,<x1>,<y1>}; *------------------------------------------*/ -BUILDIN(soundeffectall) { +static BUILDIN(soundeffectall) +{ struct block_list* bl; const char* name; int type; @@ -13518,7 +15783,7 @@ BUILDIN(soundeffectall) { /*========================================== * pet status recovery [Valaris] / Rewritten by [Skotlex] *------------------------------------------*/ -BUILDIN(petrecovery) +static BUILDIN(petrecovery) { struct pet_data *pd; struct map_session_data *sd = script->rid2sd(st); @@ -13547,7 +15812,7 @@ BUILDIN(petrecovery) *------------------------------------------*/ /// petskillattack <skill id>,<level>,<div>,<rate>,<bonusrate> /// petskillattack "<skill name>",<level>,<div>,<rate>,<bonusrate> -BUILDIN(petskillattack) +static BUILDIN(petskillattack) { struct pet_data *pd; struct map_session_data *sd = script->rid2sd(st); @@ -13573,7 +15838,7 @@ BUILDIN(petskillattack) *------------------------------------------*/ /// petskillsupport <skill id>,<level>,<delay>,<hp>,<sp> /// petskillsupport "<skill name>",<level>,<delay>,<hp>,<sp> -BUILDIN(petskillsupport) +static BUILDIN(petskillsupport) { struct pet_data *pd; struct map_session_data *sd = script->rid2sd(st); @@ -13612,7 +15877,7 @@ BUILDIN(petskillsupport) *------------------------------------------*/ /// skilleffect <skill id>,<level> /// skilleffect "<skill name>",<level> -BUILDIN(skilleffect) +static BUILDIN(skilleffect) { struct map_session_data *sd; @@ -13640,7 +15905,8 @@ BUILDIN(skilleffect) *------------------------------------------*/ /// npcskilleffect <skill id>,<level>,<x>,<y> /// npcskilleffect "<skill name>",<level>,<x>,<y> -BUILDIN(npcskilleffect) { +static BUILDIN(npcskilleffect) +{ struct block_list *bl= map->id2bl(st->oid); uint16 skill_id=( script_isstringtype(st,2) ? skill->name2id(script_getstr(st,2)) : script_getnum(st,2) ); @@ -13657,26 +15923,43 @@ BUILDIN(npcskilleffect) { /*========================================== * Special effects [Valaris] *------------------------------------------*/ -BUILDIN(specialeffect) { +static BUILDIN(specialeffect) +{ struct block_list *bl = NULL; - int type = script_getnum(st,2); - enum send_target target = script_hasdata(st,3) ? (send_target)script_getnum(st,3) : AREA; + int type = script_getnum(st, 2); + enum send_target target = AREA; - if (script_hasdata(st,4)) { - struct npc_data *nd = npc->name2id(script_getstr(st,4)); - if (nd != NULL) - bl = &nd->bl; + 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) + if (bl == NULL) { return true; + } if (target == SELF) { - struct map_session_data *sd = script->rid2sd(st); - if (sd != NULL) + 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->specialeffect_single(bl, type, sd->fd); + } } else { clif->specialeffect(bl, type, target); } @@ -13684,7 +15967,58 @@ BUILDIN(specialeffect) { return true; } -BUILDIN(specialeffect2) { +/*========================================== + * Special effects with num [4144] + *------------------------------------------*/ +static BUILDIN(specialeffectnum) +{ + struct block_list *bl = NULL; + int type = script_getnum(st, 2); + int num = script_getnum(st, 3); + int num2 = script_getnum(st, 4); + enum send_target target = AREA; + + if (script_hasdata(st, 5)) { + target = script_getnum(st, 5); + } + + if (script_hasdata(st, 6)) { + if (script_isstringtype(st, 6)) { + struct npc_data *nd = npc->name2id(script_getstr(st, 6)); + if (nd != NULL) { + bl = &nd->bl; + } + } else { + bl = map->id2bl(script_getnum(st, 6)); + } + } else { + bl = map->id2bl(st->oid); + } + + if (bl == NULL) { + return true; + } + + uint64 bigNum = ((uint64)num2) * 0xffffffff + num; + if (target == SELF) { + struct map_session_data *sd; + if (script_hasdata(st, 7)) { + sd = map->id2sd(script_getnum(st, 7)); + } else { + sd = script->rid2sd(st); + } + if (sd != NULL) { + clif->specialeffect_value_single(bl, type, bigNum, sd->fd); + } + } else { + clif->specialeffect_value(bl, type, bigNum, target); + } + + return true; +} + +static BUILDIN(specialeffect2) +{ struct map_session_data *sd; int type = script_getnum(st,2); enum send_target target = script_hasdata(st,3) ? (send_target)script_getnum(st,3) : AREA; @@ -13700,10 +16034,54 @@ 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] *------------------------------------------*/ -BUILDIN(nude) +static BUILDIN(nude) { struct map_session_data *sd = script->rid2sd(st); int i, calcflag = 0; @@ -13728,12 +16106,11 @@ BUILDIN(nude) /*========================================== * gmcommand [MouseJstr] *------------------------------------------*/ -BUILDIN(atcommand) +static BUILDIN(atcommand) { struct map_session_data *sd, *dummy_sd = NULL; int fd; const char* cmd; - bool ret = true; cmd = script_getstr(st,2); @@ -13756,11 +16133,12 @@ BUILDIN(atcommand) if (!atcommand->exec(fd, sd, cmd, false)) { ShowWarning("script: buildin_atcommand: failed to execute command '%s'\n", cmd); - script->reportsrc(st); - ret = false; + if (dummy_sd != NULL) + aFree(dummy_sd); + return false; } if (dummy_sd) aFree(dummy_sd); - return ret; + return true; } /** @@ -13770,7 +16148,7 @@ BUILDIN(atcommand) * dispbottom "<message>"{,<color>}; * @endcode */ -BUILDIN(dispbottom) +static BUILDIN(dispbottom) { struct map_session_data *sd = script->rid2sd(st); const char *message = script_getstr(st,2); @@ -13782,7 +16160,7 @@ BUILDIN(dispbottom) int color = script_getnum(st,3); clif->messagecolor_self(sd->fd, color, message); } else { - clif_disp_onlyself(sd, message, (int)strlen(message)); + clif_disp_onlyself(sd, message); } return true; @@ -13792,52 +16170,130 @@ BUILDIN(dispbottom) * All The Players Full Recovery * (HP/SP full restore and resurrect if need) *------------------------------------------*/ -BUILDIN(recovery) +static int buildin_recovery_sub(struct map_session_data *sd) { - struct map_session_data *sd; - struct s_mapiterator* iter; + nullpo_retr(0, sd); - iter = mapit_getallusers(); - for (sd = BL_UCAST(BL_PC, mapit->first(iter)); mapit->exists(iter); sd = BL_UCAST(BL_PC, mapit->next(iter))) { - if(pc_isdead(sd)) - status->revive(&sd->bl, 100, 100); - else - status_percent_heal(&sd->bl, 100, 100); - clif->message(sd->fd,msg_sd(sd,880)); // "You have been recovered!" + if (pc_isdead(sd)) { + status->revive(&sd->bl, 100, 100); + } else { + status_percent_heal(&sd->bl, 100, 100); + } + + return 0; +} + +static int buildin_recovery_pc_sub(struct map_session_data *sd, va_list ap) +{ + return script->buildin_recovery_sub(sd); +} + +static int buildin_recovery_bl_sub(struct block_list *bl, va_list ap) +{ + return script->buildin_recovery_sub(BL_CAST(BL_PC, bl)); +} + +static BUILDIN(recovery) +{ + if (script_hasdata(st, 2)) { + if (script_isstringtype(st, 2)) { + int16 m = map->mapname2mapid(script_getstr(st, 2)); + + if (m == -1) { + ShowWarning("script:recovery: invalid map!\n"); + return false; + } + + if (script_hasdata(st, 6)) { + int16 x1 = script_getnum(st, 3); + int16 y1 = script_getnum(st, 4); + int16 x2 = script_getnum(st, 5); + int16 y2 = script_getnum(st, 6); + map->foreachinarea(script->buildin_recovery_bl_sub, m, x1, y1, x2, y2, BL_PC); + } else { + map->foreachinmap(script->buildin_recovery_bl_sub, m, BL_PC); + } + } else { + struct map_session_data *sd = script->id2sd(st, script_getnum(st, 2)); + + if (sd != NULL) { + script->buildin_recovery_sub(sd); + } + } + } else { + map->foreachpc(script->buildin_recovery_pc_sub); } - mapit->free(iter); return true; } -/*========================================== - * Get your pet info: getpetinfo(n) - * n -> 0:pet_id 1:pet_class 2:pet_name - * 3:friendly 4:hungry, 5: rename flag. - *------------------------------------------*/ -BUILDIN(getpetinfo) + +/* + * Get your current pet information + */ +static BUILDIN(getpetinfo) { struct map_session_data *sd = script->rid2sd(st); - struct pet_data *pd; - int type=script_getnum(st,2); + if (sd == NULL) + return true; - if (sd == NULL || sd->pd == NULL) { - if (type == 2) - script_pushconststr(st,"null"); + struct pet_data *pd = sd->pd; + int type = script_getnum(st, 2); + if (pd == NULL) { + if (type == PETINFO_NAME) + script_pushconststr(st, "null"); else - script_pushint(st,0); + script_pushint(st, 0); return true; } - pd = sd->pd; + switch(type) { - case 0: script_pushint(st,pd->pet.pet_id); break; - case 1: script_pushint(st,pd->pet.class_); break; - case 2: script_pushstrcopy(st,pd->pet.name); break; - case 3: script_pushint(st,pd->pet.intimate); break; - case 4: script_pushint(st,pd->pet.hungry); break; - case 5: script_pushint(st,pd->pet.rename_flag); break; - default: - script_pushint(st,0); - break; + case PETINFO_ID: + script_pushint(st, pd->pet.pet_id); + break; + case PETINFO_CLASS: + script_pushint(st, pd->pet.class_); + break; + case PETINFO_NAME: + script_pushstrcopy(st, pd->pet.name); + break; + case PETINFO_INTIMACY: + script_pushint(st, pd->pet.intimate); + break; + case PETINFO_HUNGRY: + script_pushint(st, pd->pet.hungry); + break; + case PETINFO_RENAME: + script_pushint(st, pd->pet.rename_flag); + break; + case PETINFO_GID: + script_pushint(st, pd->bl.id); + break; + case PETINFO_EGGITEM: + script_pushint(st, pd->pet.egg_id); + break; + case PETINFO_FOODITEM: + script_pushint(st, pd->petDB->FoodID); + break; + case PETINFO_ACCESSORYITEM: + script_pushint(st, pd->petDB->AcceID); + break; + case PETINFO_ACCESSORYFLAG: + script_pushint(st, (pd->pet.equip != 0)? 1:0); + break; + case PETINFO_EVO_EGGID: + if (VECTOR_DATA(pd->petDB->evolve_data) != NULL) + script_pushint(st, VECTOR_DATA(pd->petDB->evolve_data)->petEggId); + else + script_pushint(st, 0); + break; + case PETINFO_AUTOFEED: + script_pushint(st, pd->pet.autofeed); + break; + default: + ShowWarning("buildin_getpetinfo: Invalid type %d.\n", type); + script_pushint(st, 0); + return false; } + return true; } @@ -13847,7 +16303,7 @@ BUILDIN(getpetinfo) * 3:friendly 4:hungry, 5: rename flag. * 6: level *------------------------------------------*/ -BUILDIN(gethominfo) +static BUILDIN(gethominfo) { struct map_session_data *sd = script->rid2sd(st); int type = script_getnum(st,2); @@ -13875,20 +16331,15 @@ BUILDIN(gethominfo) return true; } -/// Retrieves information about character's mercenary -/// getmercinfo <type>[,<char id>]; -BUILDIN(getmercinfo) +/* + * Retrieves information about character's mercenary + * getmercinfo <type>{, <char id> }; + */ +static BUILDIN(getmercinfo) { - int type; - struct map_session_data* sd; - struct mercenary_data* md; - - type = script_getnum(st,2); - - if (script_hasdata(st,3)) { - int char_id = script_getnum(st,3); - - if ((sd = script->charid2sd(st, char_id)) == NULL) { + struct map_session_data *sd; + if (script_hasdata(st, 3)) { + if ((sd = script->charid2sd(st, script_getnum(st, 3))) == NULL) { script_pushnil(st); return true; } @@ -13897,27 +16348,48 @@ BUILDIN(getmercinfo) return true; } - md = ( sd->status.mer_id && sd->md ) ? sd->md : NULL; + struct mercenary_data *md = (sd->status.mer_id && sd->md)? sd->md : NULL; + int type = script_getnum(st, 2); + if (md == NULL) { + if (type == MERCINFO_NAME) + script_pushconststr(st, ""); + else + script_pushint(st, 0); + return true; + } - switch( type ) - { - case 0: script_pushint(st,md ? md->mercenary.mercenary_id : 0); break; - case 1: script_pushint(st,md ? md->mercenary.class_ : 0); break; - case 2: - if( md ) - script_pushstrcopy(st,md->db->name); - else - script_pushconststr(st,""); - break; - case 3: script_pushint(st,md ? mercenary->get_faith(md) : 0); break; - case 4: script_pushint(st,md ? mercenary->get_calls(md) : 0); break; - case 5: script_pushint(st,md ? md->mercenary.kill_count : 0); break; - case 6: script_pushint(st,md ? mercenary->get_lifetime(md) : 0); break; - case 7: script_pushint(st,md ? md->db->lv : 0); break; - default: - ShowError("buildin_getmercinfo: Invalid type %d (char_id=%d).\n", type, sd->status.char_id); - script_pushnil(st); - return false; + switch (type) { + case MERCINFO_ID: + script_pushint(st, md->mercenary.mercenary_id); + break; + case MERCINFO_CLASS: + script_pushint(st, md->mercenary.class_); + break; + case MERCINFO_NAME: + script_pushstrcopy(st, md->db->name); + break; + case MERCINFO_FAITH: + script_pushint(st, mercenary->get_faith(md)); + break; + case MERCINFO_CALLS: + script_pushint(st, mercenary->get_calls(md)); + break; + case MERCINFO_KILLCOUNT: + script_pushint(st, md->mercenary.kill_count); + break; + case MERCINFO_LIFETIME: + script_pushint(st, mercenary->get_lifetime(md)); + break; + case MERCINFO_LEVEL: + script_pushint(st, md->db->lv); + break; + case MERCINFO_GID: + script_pushint(st, md->bl.id); + break; + default: + ShowError("buildin_getmercinfo: Invalid type %d (char_id=%d).\n", type, sd->status.char_id); + script_pushnil(st); + return false; } return true; @@ -13928,21 +16400,20 @@ BUILDIN(getmercinfo) * selected card or not. * checkequipedcard(4001); *------------------------------------------*/ -BUILDIN(checkequipedcard) +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; @@ -13955,7 +16426,7 @@ BUILDIN(checkequipedcard) return true; } -BUILDIN(__jump_zero) +static BUILDIN(__jump_zero) { int sel; sel=script_getnum(st,2); @@ -13977,7 +16448,7 @@ BUILDIN(__jump_zero) /*========================================== * movenpc [MouseJstr] *------------------------------------------*/ -BUILDIN(movenpc) +static BUILDIN(movenpc) { struct npc_data *nd = NULL; const char *npc_name; @@ -13999,7 +16470,7 @@ BUILDIN(movenpc) /*========================================== * message [MouseJstr] *------------------------------------------*/ -BUILDIN(message) +static BUILDIN(message) { const char *message; struct map_session_data *sd = NULL; @@ -14018,14 +16489,38 @@ 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>"}; + * usage: npctalk("<message>"{, "<npc name>"{, <show_name>}}); *------------------------------------------*/ -BUILDIN(npctalk) +static BUILDIN(npctalk) { struct npc_data* nd; const char *str = script_getstr(st,2); + bool show_name = true; if (script_hasdata(st, 3)) { nd = npc->name2id(script_getstr(st, 3)); @@ -14033,61 +16528,78 @@ BUILDIN(npctalk) nd = map->id2nd(st->oid); } + if (script_hasdata(st, 4)) { + show_name = (script_getnum(st, 4) != 0) ? true : false; + } + if (nd != NULL) { char name[NAME_LENGTH], message[256]; safestrncpy(name, nd->name, sizeof(name)); strtok(name, "#"); // discard extra name identifier if present - safesnprintf(message, sizeof(message), "%s : %s", name, str); - clif->disp_overhead(&nd->bl, message); + if (show_name) { + safesnprintf(message, sizeof(message), "%s : %s", name, str); + } else { + safesnprintf(message, sizeof(message), "%s", str); + } + clif->disp_overhead(&nd->bl, message, AREA_CHAT_WOC, NULL); } return true; } // change npc walkspeed [Valaris] -BUILDIN(npcspeed) { - struct npc_data* nd; - int speed; - - speed = script_getnum(st,2); - nd = map->id2nd(st->oid); +static BUILDIN(npcspeed) +{ + struct npc_data *nd = map->id2nd(st->oid); + int speed = script_getnum(st, 2); if (nd != NULL) { unit->bl2ud2(&nd->bl); // ensure nd->ud is safe to edit + if (nd->ud == NULL) { + ShowWarning("buildin_npcspeed: floating NPC don't have unit data.\n"); + return false; + } nd->speed = speed; nd->ud->state.speed_changed = 1; } return true; } + // make an npc walk to a position [Valaris] -BUILDIN(npcwalkto) +static BUILDIN(npcwalkto) { struct npc_data *nd = map->id2nd(st->oid); - int x=0,y=0; - - x=script_getnum(st,2); - y=script_getnum(st,3); + int x = script_getnum(st, 2); + int y = script_getnum(st, 3); if (nd != NULL) { unit->bl2ud2(&nd->bl); // ensure nd->ud is safe to edit + if (nd->ud == NULL) { + ShowWarning("buildin_npcwalkto: floating NPC don't have unit data.\n"); + return false; + } if (!nd->status.hp) { status_calc_npc(nd, SCO_FIRST); } else { status_calc_npc(nd, SCO_NONE); } - unit->walktoxy(&nd->bl,x,y,0); + unit->walk_toxy(&nd->bl, x, y, 0); } return true; } // stop an npc's movement [Valaris] -BUILDIN(npcstop) +static BUILDIN(npcstop) { struct npc_data *nd = map->id2nd(st->oid); if (nd != NULL) { unit->bl2ud2(&nd->bl); // ensure nd->ud is safe to edit + if (nd->ud == NULL) { + ShowWarning("buildin_npcstop: floating NPC don't have unit data.\n"); + return false; + } unit->stop_walking(&nd->bl, STOPWALKING_FLAG_FIXPOS|STOPWALKING_FLAG_NEXTCELL); } @@ -14095,7 +16607,7 @@ BUILDIN(npcstop) } // set click npc distance [4144] -BUILDIN(setnpcdistance) +static BUILDIN(setnpcdistance) { struct npc_data *nd = map->id2nd(st->oid); if (nd == NULL) @@ -14107,7 +16619,7 @@ BUILDIN(setnpcdistance) } // return current npc direction [4144] -BUILDIN(getnpcdir) +static BUILDIN(getnpcdir) { const struct npc_data *nd = NULL; @@ -14133,7 +16645,7 @@ BUILDIN(getnpcdir) } // set npc direction [4144] -BUILDIN(setnpcdir) +static BUILDIN(setnpcdir) { int newdir; struct npc_data *nd = NULL; @@ -14167,7 +16679,7 @@ BUILDIN(setnpcdir) } // return npc class [4144] -BUILDIN(getnpcclass) +static BUILDIN(getnpcclass) { const struct npc_data *nd = NULL; @@ -14195,7 +16707,7 @@ BUILDIN(getnpcclass) /*========================================== * getlook char info. getlook(arg) *------------------------------------------*/ -BUILDIN(getlook) +static BUILDIN(getlook) { int type,val = -1; struct map_session_data *sd = script->rid2sd(st); @@ -14205,15 +16717,15 @@ BUILDIN(getlook) type=script_getnum(st,2); switch(type) { case LOOK_HAIR: val = sd->status.hair; break; //1 - case LOOK_WEAPON: val = sd->status.weapon; break; //2 - case LOOK_HEAD_BOTTOM: val = sd->status.head_bottom; break; //3 - case LOOK_HEAD_TOP: val = sd->status.head_top; break; //4 - case LOOK_HEAD_MID: val = sd->status.head_mid; break; //5 + case LOOK_WEAPON: val = sd->status.look.weapon; break; //2 + case LOOK_HEAD_BOTTOM: val = sd->status.look.head_bottom; break; //3 + case LOOK_HEAD_TOP: val = sd->status.look.head_top; break; //4 + case LOOK_HEAD_MID: val = sd->status.look.head_mid; break; //5 case LOOK_HAIR_COLOR: val = sd->status.hair_color; break; //6 case LOOK_CLOTHES_COLOR: val = sd->status.clothes_color; break; //7 - case LOOK_SHIELD: val = sd->status.shield; break; //8 + case LOOK_SHIELD: val = sd->status.look.shield; break; //8 case LOOK_SHOES: break; //9 - case LOOK_ROBE: val = sd->status.robe; break; //12 + case LOOK_ROBE: val = sd->status.look.robe; break; //12 case LOOK_BODY2: val=sd->status.body; break; //13 } @@ -14224,7 +16736,7 @@ BUILDIN(getlook) /*========================================== * get char save point. argument: 0- map name, 1- x, 2- y *------------------------------------------*/ -BUILDIN(getsavepoint) +static BUILDIN(getsavepoint) { int type; struct map_session_data *sd = script->rid2sd(st); @@ -14267,7 +16779,7 @@ BUILDIN(getsavepoint) * 0 - success * -1 - some error, MapName$,MapX,MapY contains unknown value. *------------------------------------------*/ -BUILDIN(getmapxy) +static BUILDIN(getmapxy) { struct block_list *bl = NULL; struct map_session_data *sd = NULL; @@ -14320,7 +16832,7 @@ BUILDIN(getmapxy) case 0: //Get Character Position if (script_hasdata(st,6)) { if (script_isstringtype(st,6)) - sd = map->nick2sd(script_getstr(st,6)); + sd = map->nick2sd(script_getstr(st,6), false); else sd = map->id2sd(script_getnum(st,6)); } else { @@ -14347,7 +16859,7 @@ BUILDIN(getmapxy) case 2: //Get Pet Position if (script_hasdata(st,6)) { if (script_isstringtype(st,6)) - sd = map->nick2sd(script_getstr(st,6)); + sd = map->nick2sd(script_getstr(st,6), false); else { bl = map->id2bl(script_getnum(st,6)); break; @@ -14369,7 +16881,7 @@ BUILDIN(getmapxy) case 4: //Get Homun Position if (script_hasdata(st,6)) { if (script_isstringtype(st,6)) { - sd = map->nick2sd(script_getstr(st,6)); + sd = map->nick2sd(script_getstr(st,6), false); } else { bl = map->id2bl(script_getnum(st,6)); break; @@ -14384,7 +16896,7 @@ BUILDIN(getmapxy) case 5: //Get Mercenary Position if (script_hasdata(st,6)) { if (script_isstringtype(st,6)) { - sd = map->nick2sd(script_getstr(st,6)); + sd = map->nick2sd(script_getstr(st,6), false); } else { bl = map->id2bl(script_getnum(st,6)); break; @@ -14399,7 +16911,7 @@ BUILDIN(getmapxy) case 6: //Get Elemental Position if (script_hasdata(st,6)) { if (script_isstringtype(st,6)) { - sd = map->nick2sd(script_getstr(st,6)); + sd = map->nick2sd(script_getstr(st,6), false); } else { bl = map->id2bl(script_getnum(st,6)); break; @@ -14463,61 +16975,97 @@ BUILDIN(getmapxy) return true; } +enum logmes_type { + LOGMES_NPC, + LOGMES_ATCOMMAND +}; + /*========================================== - * Allows player to write NPC logs (i.e. Bank NPC, etc) [Lupus] + * Allows player to write logs (i.e. Bank NPC, etc) [Lupus] *------------------------------------------*/ -BUILDIN(logmes) +static BUILDIN(logmes) { - const char *str; + const char *str = script_getstr(st, 2); struct map_session_data *sd = script->rid2sd(st); + enum logmes_type type = LOGMES_NPC; + nullpo_retr(false, sd); - if (sd == NULL) - return true; + if (script_hasdata(st, 3)) { + type = script_getnum(st, 3); + } + + switch (type) { + case LOGMES_ATCOMMAND: + logs->atcommand(sd, str); + break; + case LOGMES_NPC: + logs->npc(sd, str); + break; + default: + ShowError("script:logmes: Unknown log type!\n"); + st->state = END; + return false; + } - str = script_getstr(st,2); - logs->npc(sd,str); return true; } -BUILDIN(summon) +/** + * Summons a mob which will act as a slave for the invoking character. + * + * @code{.herc} + * summon("mob name", <mob id>{, <timeout>{, "event label"}}); + * @endcode + * + * @author Celest + * + **/ +static BUILDIN(summon) { - int class_, timeout=0; - const char *str,*event=""; - struct mob_data *md; - int64 tick = timer->gettick(); struct map_session_data *sd = script->rid2sd(st); + if (sd == NULL) return true; - str = script_getstr(st,2); - class_ = script_getnum(st,3); - if( script_hasdata(st,4) ) - timeout=script_getnum(st,4); - if( script_hasdata(st,5) ) { - event=script_getstr(st,5); + const int64 tick = timer->gettick(); + + clif->skill_poseffect(&sd->bl, AM_CALLHOMUN, 1, sd->bl.x, sd->bl.y, tick); + + const char *event = ""; + + if (script_hasdata(st, 5)) { + event = script_getstr(st, 5); script->check_event(st, event); } - clif->skill_poseffect(&sd->bl,AM_CALLHOMUN,1,sd->bl.x,sd->bl.y,tick); + const char *name = script_getstr(st, 2); + const int mob_id = script_getnum(st, 3); + struct mob_data *md = mob->once_spawn_sub(&sd->bl, sd->bl.m, sd->bl.x, sd->bl.y, name, mob_id, event, + SZ_SMALL, AI_NONE, 0); - md = mob->once_spawn_sub(&sd->bl, sd->bl.m, sd->bl.x, sd->bl.y, str, class_, event, SZ_SMALL, AI_NONE); - if (md) { - md->master_id=sd->bl.id; + if (md != NULL) { + md->master_id = sd->bl.id; md->special_state.ai = AI_ATTACK; - if( md->deletetimer != INVALID_TIMER ) + + if (md->deletetimer != INVALID_TIMER) timer->delete(md->deletetimer, mob->timer_delete); - md->deletetimer = timer->add(tick+(timeout>0?timeout*1000:60000),mob->timer_delete,md->bl.id,0); - mob->spawn (md); //Now it is ready for spawning. - clif->specialeffect(&md->bl,344,AREA); + + const int timeout = script_hasdata(st, 4) ? script_getnum(st, 4) * 1000 : 60000; + + md->deletetimer = timer->add(tick + ((timeout == 0) ? 60000 : timeout), mob->timer_delete, md->bl.id, 0); + mob->spawn(md); + clif->specialeffect(&md->bl, 344, AREA); sc_start4(NULL, &md->bl, SC_MODECHANGE, 100, 1, 0, MD_AGGRESSIVE, 0, 60000); } + return true; } /*========================================== * Checks whether it is daytime/nighttime *------------------------------------------*/ -BUILDIN(isnight) { +static BUILDIN(isnight) +{ script_pushint(st,(map->night_flag == 1)); return true; } @@ -14526,7 +17074,7 @@ BUILDIN(isnight) { * Check how many items/cards in the list are * equipped - used for 2/15's cards patch [celest] *------------------------------------------------*/ -BUILDIN(isequippedcnt) +static BUILDIN(isequippedcnt) { int i, j, k, id = 1; int ret = 0; @@ -14547,6 +17095,8 @@ BUILDIN(isequippedcnt) if(j == EQI_HAND_R && sd->equip_index[EQI_HAND_L] == index) continue; if(j == EQI_HEAD_MID && sd->equip_index[EQI_HEAD_LOW] == index) continue; if(j == EQI_HEAD_TOP && (sd->equip_index[EQI_HEAD_MID] == index || sd->equip_index[EQI_HEAD_LOW] == index)) continue; + if(j == EQI_COSTUME_MID && sd->equip_index[EQI_COSTUME_LOW] == index) continue; + if(j == EQI_COSTUME_TOP && (sd->equip_index[EQI_COSTUME_MID] == index || sd->equip_index[EQI_COSTUME_LOW] == index)) continue; if(!sd->inventory_data[index]) continue; @@ -14575,7 +17125,7 @@ BUILDIN(isequippedcnt) * -- Items checked cannot be reused in another * card set to prevent exploits *------------------------------------------------*/ -BUILDIN(isequipped) +static BUILDIN(isequipped) { int i, j, k, id = 1; int index, flag; @@ -14600,6 +17150,8 @@ BUILDIN(isequipped) if(j == EQI_HAND_R && sd->equip_index[EQI_HAND_L] == index) continue; if(j == EQI_HEAD_MID && sd->equip_index[EQI_HEAD_LOW] == index) continue; if(j == EQI_HEAD_TOP && (sd->equip_index[EQI_HEAD_MID] == index || sd->equip_index[EQI_HEAD_LOW] == index)) continue; + if(j == EQI_COSTUME_MID && sd->equip_index[EQI_COSTUME_LOW] == index) continue; + if(j == EQI_COSTUME_TOP && (sd->equip_index[EQI_COSTUME_MID] == index || sd->equip_index[EQI_COSTUME_LOW] == index)) continue; if(!sd->inventory_data[index]) continue; @@ -14655,7 +17207,7 @@ BUILDIN(isequipped) * Check how many given inserted cards in the CURRENT * weapon - used for 2/15's cards patch [Lupus] *------------------------------------------------*/ -BUILDIN(cardscnt) +static BUILDIN(cardscnt) { int i, k, id = 1; int ret = 0; @@ -14697,25 +17249,30 @@ BUILDIN(cardscnt) * Returns the refined number of the current item, or an * item with inventory index specified *-------------------------------------------------------*/ -BUILDIN(getrefine) +static BUILDIN(getrefine) { struct map_session_data *sd = script->rid2sd(st); if (sd == NULL) return true; - script_pushint(st,sd->status.inventory[status->current_equip_item_index].refine); + if (status->current_equip_item_index < 0) + script_pushint(st, 0); + else + script_pushint(st, sd->status.inventory[status->current_equip_item_index].refine); return true; } /*======================================================= * Day/Night controls *-------------------------------------------------------*/ -BUILDIN(night) { +static BUILDIN(night) +{ if (map->night_flag != 1) pc->map_night_timer(pc->night_timer_tid, 0, 0, 1); return true; } -BUILDIN(day) { +static BUILDIN(day) +{ if (map->night_flag != 0) pc->map_day_timer(pc->day_timer_tid, 0, 0, 1); return true; } @@ -14723,7 +17280,7 @@ BUILDIN(day) { //======================================================= // Unequip [Spectre] //------------------------------------------------------- -BUILDIN(unequip) +static BUILDIN(unequip) { size_t num; struct map_session_data *sd; @@ -14738,7 +17295,7 @@ BUILDIN(unequip) return true; } -BUILDIN(equip) +static BUILDIN(equip) { int nameid=0,i; struct item_data *item_data; @@ -14749,17 +17306,17 @@ BUILDIN(equip) nameid=script_getnum(st,2); if((item_data = itemdb->exists(nameid)) == NULL) { - ShowError("wrong item ID : equipitem(%i)\n",nameid); + 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; } -BUILDIN(autoequip) +static BUILDIN(autoequip) { int nameid, flag; struct item_data *item_data; @@ -14786,7 +17343,7 @@ BUILDIN(autoequip) * Equip2 * equip2 <item id>,<refine>,<attribute>,<card1>,<card2>,<card3>,<card4>; *-------------------------------------------------------*/ -BUILDIN(equip2) +static BUILDIN(equip2) { int i,nameid,ref,attr,c0,c1,c2,c3; struct item_data *item_data; @@ -14805,33 +17362,33 @@ BUILDIN(equip2) return false; } - ref = script_getnum(st,3); - attr = script_getnum(st,4); - c0 = (short)script_getnum(st,5); - c1 = (short)script_getnum(st,6); - c2 = (short)script_getnum(st,7); - c3 = (short)script_getnum(st,8); + ref = script_getnum(st, 3); + attr = script_getnum(st, 4); + c0 = script_getnum(st, 5); + c1 = script_getnum(st, 6); + 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; } -BUILDIN(setbattleflag) +static BUILDIN(setbattleflag) { const char *flag, *value; @@ -14846,7 +17403,7 @@ BUILDIN(setbattleflag) return true; } -BUILDIN(getbattleflag) +static BUILDIN(getbattleflag) { const char *flag; int value; @@ -14868,7 +17425,7 @@ BUILDIN(getbattleflag) //======================================================= // strlen [Valaris] //------------------------------------------------------- -BUILDIN(getstrlen) +static BUILDIN(getstrlen) { const char *str = script_getstr(st,2); @@ -14881,7 +17438,7 @@ BUILDIN(getstrlen) //======================================================= // isalpha [Valaris] //------------------------------------------------------- -BUILDIN(charisalpha) +static BUILDIN(charisalpha) { const char *str=script_getstr(st,2); int pos=script_getnum(st,3); @@ -14895,7 +17452,7 @@ BUILDIN(charisalpha) //======================================================= // charisupper <str>, <index> //------------------------------------------------------- -BUILDIN(charisupper) +static BUILDIN(charisupper) { const char *str = script_getstr(st,2); int pos = script_getnum(st,3); @@ -14909,7 +17466,7 @@ BUILDIN(charisupper) //======================================================= // charislower <str>, <index> //------------------------------------------------------- -BUILDIN(charislower) +static BUILDIN(charislower) { const char *str = script_getstr(st,2); int pos = script_getnum(st,3); @@ -14923,7 +17480,8 @@ BUILDIN(charislower) //======================================================= // charat <str>, <index> //------------------------------------------------------- -BUILDIN(charat) { +static BUILDIN(charat) +{ const char *str = script_getstr(st,2); int pos = script_getnum(st,3); @@ -14938,9 +17496,164 @@ BUILDIN(charat) { } //======================================================= +// isstr <argument> +// +// returns type: +// 0 - int +// 1 - string +// 2 - other +//------------------------------------------------------- +static BUILDIN(isstr) +{ + if (script_isinttype(st, 2)) { + script_pushint(st, 0); + } else if (script_isstringtype(st, 2)) { + script_pushint(st, 1); + } else { + script_pushint(st, 2); + } + return true; +} + +enum datatype { + DATATYPE_NIL = 1 << 7, // we don't start at 1, to leave room for primitives + DATATYPE_STR = 1 << 8, + DATATYPE_INT = 1 << 9, + DATATYPE_CONST = 1 << 10, + DATATYPE_PARAM = 1 << 11, + DATATYPE_VAR = 1 << 12, + DATATYPE_LABEL = 1 << 13, +}; + +static BUILDIN(getdatatype) +{ + int type; + + if (script_hasdata(st, 2)) { + struct script_data *data = script_getdata(st, 2); + + if (data_isstring(data)) { + type = DATATYPE_STR; + if (data->type == C_CONSTSTR) { + type |= DATATYPE_CONST; + } + } else if (data_isint(data)) { + type = DATATYPE_INT; + } else if (data_islabel(data)) { + type = DATATYPE_LABEL; + } else if (data_isreference(data)) { + if (reference_toconstant(data)) { + type = DATATYPE_CONST | DATATYPE_INT; + } else if (reference_toparam(data)) { + type = DATATYPE_PARAM | DATATYPE_INT; + } else if (reference_tovariable(data)) { + type = DATATYPE_VAR; + if (is_string_variable(reference_getname(data))) { + type |= DATATYPE_STR; + } else { + type |= DATATYPE_INT; + } + } else { + ShowError("script:getdatatype: Unknown reference type!\n"); + script->reportdata(data); + st->state = END; + return false; + } + } else { + type = data->type; // fallback to primitive type if unknown + } + } else { + type = DATATYPE_NIL; // nothing was passed + } + + script_pushint(st, type); + 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> +//------------------------------------------------------- +static BUILDIN(chr) +{ + char output[2]; + output[0] = script_getnum(st, 2); + output[1] = '\0'; + + script_pushstrcopy(st, output); + return true; +} + +//======================================================= +// ord <chr> +//------------------------------------------------------- +static BUILDIN(ord) +{ + const char *chr = script_getstr(st, 2); + script_pushint(st, *chr); + return true; +} + +//======================================================= // setchar <string>, <char>, <index> //------------------------------------------------------- -BUILDIN(setchar) +static BUILDIN(setchar) { const char *str = script_getstr(st,2); const char *c = script_getstr(st,3); @@ -14957,7 +17670,7 @@ BUILDIN(setchar) //======================================================= // insertchar <string>, <char>, <index> //------------------------------------------------------- -BUILDIN(insertchar) +static BUILDIN(insertchar) { const char *str = script_getstr(st,2); const char *c = script_getstr(st,3); @@ -14984,7 +17697,7 @@ BUILDIN(insertchar) //======================================================= // delchar <string>, <index> //------------------------------------------------------- -BUILDIN(delchar) +static BUILDIN(delchar) { const char *str = script_getstr(st,2); int index = script_getnum(st,3); @@ -15010,7 +17723,7 @@ BUILDIN(delchar) //======================================================= // strtoupper <str> //------------------------------------------------------- -BUILDIN(strtoupper) +static BUILDIN(strtoupper) { const char *str = script_getstr(st,2); char *output = aStrdup(str); @@ -15028,7 +17741,7 @@ BUILDIN(strtoupper) //======================================================= // strtolower <str> //------------------------------------------------------- -BUILDIN(strtolower) +static BUILDIN(strtolower) { const char *str = script_getstr(st,2); char *output = aStrdup(str); @@ -15046,7 +17759,7 @@ BUILDIN(strtolower) //======================================================= // substr <str>, <start>, <end> //------------------------------------------------------- -BUILDIN(substr) +static BUILDIN(substr) { const char *str = script_getstr(st,2); char *output; @@ -15072,7 +17785,7 @@ BUILDIN(substr) // explode <dest_string_array>, <str>, <delimiter> // Note: delimiter is limited to 1 char //------------------------------------------------------- -BUILDIN(explode) +static BUILDIN(explode) { struct script_data* data = script_getdata(st, 2); const char *str = script_getstr(st,3); @@ -15137,7 +17850,7 @@ BUILDIN(explode) // implode <string_array> // implode <string_array>, <glue> //------------------------------------------------------- -BUILDIN(implode) +static BUILDIN(implode) { struct script_data* data = script_getdata(st, 2); const char *name; @@ -15229,130 +17942,19 @@ BUILDIN(implode) // Implements C sprintf, except format %n. The resulting string is // returned, instead of being saved in variable by reference. //------------------------------------------------------- -BUILDIN(sprintf) { - unsigned int argc = 0, arg = 0; - const char* format; - char* p; - char* q; - char* buf = NULL; - char* buf2 = NULL; - struct script_data* data; - size_t len, buf2_len = 0; - StringBuf final_buf; - - // Fetch init data - format = script_getstr(st, 2); - argc = script_lastdata(st)-2; - len = strlen(format); - - // Skip parsing, where no parsing is required. - if(len==0) { - script_pushconststr(st,""); - return true; - } - - // Pessimistic alloc - CREATE(buf, char, len+1); - - // Need not be parsed, just solve stuff like %%. - if(argc==0) { - memcpy(buf,format,len+1); - script_pushstrcopy(st, buf); - aFree(buf); - return true; - } - - safestrncpy(buf, format, len+1); - - // Issue sprintf for each parameter - StrBuf->Init(&final_buf); - q = buf; - while((p = strchr(q, '%'))!=NULL) { - if(p!=q) { - len = p-q+1; - if(buf2_len<len) { - RECREATE(buf2, char, len); - buf2_len = len; - } - safestrncpy(buf2, q, len); - StrBuf->AppendStr(&final_buf, buf2); - q = p; - } - p = q+1; - if(*p=='%') { // %% - StrBuf->AppendStr(&final_buf, "%"); - q+=2; - continue; - } - if(*p=='n') { // %n - ShowWarning("buildin_sprintf: Format %%n not supported! Skipping...\n"); - script->reportsrc(st); - q+=2; - continue; - } - if(arg>=argc) { - ShowError("buildin_sprintf: Not enough arguments passed!\n"); - aFree(buf); - if(buf2) aFree(buf2); - StrBuf->Destroy(&final_buf); - script_pushconststr(st,""); - return false; - } - if((p = strchr(q+1, '%'))==NULL) { - p = strchr(q, 0); // EOS - } - len = p-q+1; - if(buf2_len<len) { - RECREATE(buf2, char, len); - buf2_len = len; - } - safestrncpy(buf2, q, len); - q = p; - - // Note: This assumes the passed value being the correct - // type to the current format specifier. If not, the server - // probably crashes or returns anything else, than expected, - // but it would behave in normal code the same way so it's - // the scripter's responsibility. - data = script_getdata(st, arg+3); - if(data_isstring(data)) { // String - StrBuf->Printf(&final_buf, buf2, script_getstr(st, arg+3)); - } else if(data_isint(data)) { // Number - StrBuf->Printf(&final_buf, buf2, script_getnum(st, arg+3)); - } else if(data_isreference(data)) { // Variable - char* name = reference_getname(data); - if(name[strlen(name)-1]=='$') { // var Str - StrBuf->Printf(&final_buf, buf2, script_getstr(st, arg+3)); - } else { // var Int - StrBuf->Printf(&final_buf, buf2, script_getnum(st, arg+3)); - } - } else { // Unsupported type - ShowError("buildin_sprintf: Unknown argument type!\n"); - aFree(buf); - if(buf2) aFree(buf2); - StrBuf->Destroy(&final_buf); - script_pushconststr(st,""); - return false; - } - arg++; - } - - // Append anything left - if(*q) { - StrBuf->AppendStr(&final_buf, q); - } +static BUILDIN(sprintf) +{ + struct StringBuf buf; + StrBuf->Init(&buf); - // Passed more, than needed - if(arg<argc) { - ShowWarning("buildin_sprintf: Unused arguments passed.\n"); - script->reportsrc(st); + if (!script->sprintf_helper(st, 2, &buf)) { + StrBuf->Destroy(&buf); + script_pushconststr(st, ""); + return false; } - script_pushstrcopy(st, StrBuf->Value(&final_buf)); - - aFree(buf); - if(buf2) aFree(buf2); - StrBuf->Destroy(&final_buf); + script_pushstrcopy(st, StrBuf->Value(&buf)); + StrBuf->Destroy(&buf); return true; } @@ -15361,7 +17963,8 @@ BUILDIN(sprintf) { // sscanf(<str>, <format>, ...); // Implements C sscanf. //------------------------------------------------------- -BUILDIN(sscanf) { +static BUILDIN(sscanf) +{ unsigned int argc, arg = 0; struct script_data* data; struct map_session_data* sd = NULL; @@ -15474,7 +18077,8 @@ BUILDIN(sscanf) { // Implements PHP style strpos. Adapted from code from // http://www.daniweb.com/code/snippet313.html, Dave Sinkula //------------------------------------------------------- -BUILDIN(strpos) { +static BUILDIN(strpos) +{ const char *haystack = script_getstr(st,2); const char *needle = script_getstr(st,3); int i; @@ -15518,7 +18122,7 @@ BUILDIN(strpos) { // instances as specified in <count>. By default will be case // sensitive. //--------------------------------------------------------------- -BUILDIN(replacestr) +static BUILDIN(replacestr) { const char *input = script_getstr(st, 2); const char *find = script_getstr(st, 3); @@ -15601,7 +18205,7 @@ BUILDIN(replacestr) // Note: Counts the number of times <search> occurs in // <input>. By default will be case sensitive. //-------------------------------------------------------- -BUILDIN(countstr) +static BUILDIN(countstr) { const char *input = script_getstr(st, 2); const char *find = script_getstr(st, 3); @@ -15658,7 +18262,8 @@ BUILDIN(countstr) /// setnpcdisplay("<npc name>", "<new display name>", <new class id>) -> <int> /// setnpcdisplay("<npc name>", "<new display name>") -> <int> /// setnpcdisplay("<npc name>", <new class id>) -> <int> -BUILDIN(setnpcdisplay) { +static BUILDIN(setnpcdisplay) +{ const char* name; const char* newname = NULL; int class_ = -1, size = -1; @@ -15704,14 +18309,16 @@ BUILDIN(setnpcdisplay) { return true; } -BUILDIN(atoi) { +static BUILDIN(atoi) +{ const char *value; value = script_getstr(st,2); script_pushint(st,atoi(value)); return true; } -BUILDIN(axtoi) { +static BUILDIN(axtoi) +{ const char *hex = script_getstr(st,2); long value = strtol(hex, NULL, 16); #if LONG_MAX > INT_MAX || LONG_MIN < INT_MIN @@ -15721,7 +18328,8 @@ BUILDIN(axtoi) { return true; } -BUILDIN(strtol) { +static BUILDIN(strtol) +{ const char *string = script_getstr(st, 2); int base = script_getnum(st, 3); long value = strtol(string, NULL, base); @@ -15733,7 +18341,7 @@ BUILDIN(strtol) { } // case-insensitive substring search [lordalfa] -BUILDIN(compare) +static BUILDIN(compare) { const char *message; const char *cmpstring; @@ -15743,7 +18351,7 @@ BUILDIN(compare) return true; } -BUILDIN(strcmp) +static BUILDIN(strcmp) { const char *str1 = script_getstr(st,2); const char *str2 = script_getstr(st,3); @@ -15753,7 +18361,7 @@ BUILDIN(strcmp) // List of mathematics commands ---> -BUILDIN(log10) +static BUILDIN(log10) { double i, a; i = script_getnum(st,2); @@ -15762,16 +18370,20 @@ BUILDIN(log10) return true; } -BUILDIN(sqrt) //[zBuffer] +static BUILDIN(sqrt) //[zBuffer] { double i, a; i = script_getnum(st,2); + if (i < 0) { + ShowError("sqrt from negative value\n"); + return false; + } a = sqrt(i); script_pushint(st,(int)a); return true; } -BUILDIN(pow) //[zBuffer] +static BUILDIN(pow) //[zBuffer] { double i, a, b; a = script_getnum(st,2); @@ -15781,7 +18393,7 @@ BUILDIN(pow) //[zBuffer] return true; } -BUILDIN(distance) //[zBuffer] +static BUILDIN(distance) //[zBuffer] { int x0, y0, x1, y1; @@ -15796,7 +18408,7 @@ BUILDIN(distance) //[zBuffer] // <--- List of mathematics commands -BUILDIN(min) +static BUILDIN(min) { int i, min; @@ -15811,7 +18423,7 @@ BUILDIN(min) return true; } -BUILDIN(max) +static BUILDIN(max) { int i, max; @@ -15826,22 +18438,34 @@ BUILDIN(max) return true; } -BUILDIN(md5) +static BUILDIN(cap_value) +{ + int value = script_getnum(st, 2); + int min = script_getnum(st, 3); + int max = script_getnum(st, 4); + + script_pushint(st, (int)cap_value(value, min, max)); + + return true; +} + +static BUILDIN(md5) { const char *tmpstr; char *md5str; tmpstr = script_getstr(st,2); md5str = (char *)aMalloc((32+1)*sizeof(char)); - MD5_String(tmpstr, md5str); + md5->string(tmpstr, md5str); script_pushstr(st, md5str); return true; } -BUILDIN(swap) +static BUILDIN(swap) { struct map_session_data *sd = NULL; struct script_data *data1, *data2; + struct reg_db *ref1, *ref2; const char *varname1, *varname2; int64 uid1, uid2; @@ -15882,6 +18506,8 @@ BUILDIN(swap) uid1 = reference_getuid(data1); uid2 = reference_getuid(data2); + ref1 = reference_getref(data1); + ref2 = reference_getref(data2); if (is_string_variable(varname1)) { const char *value1, *value2; @@ -15890,8 +18516,8 @@ BUILDIN(swap) value2 = script_getstr(st,3); if (strcmpi(value1, value2)) { - script->set_reg(st, sd, uid1, varname1, value2, script_getref(st,3)); - script->set_reg(st, sd, uid2, varname2, value1, script_getref(st,2)); + script->set_reg(st, sd, uid1, varname1, value2, ref1); + script->set_reg(st, sd, uid2, varname2, value1, ref2); } } else { @@ -15901,8 +18527,8 @@ BUILDIN(swap) value2 = script_getnum(st,3); if (value1 != value2) { - script->set_reg(st, sd, uid1, varname1, (const void *)h64BPTRSIZE(value2), script_getref(st,3)); - script->set_reg(st, sd, uid2, varname2, (const void *)h64BPTRSIZE(value1), script_getref(st,2)); + script->set_reg(st, sd, uid1, varname1, (const void *)h64BPTRSIZE(value2), ref1); + script->set_reg(st, sd, uid2, varname2, (const void *)h64BPTRSIZE(value1), ref2); } } return true; @@ -15910,7 +18536,7 @@ BUILDIN(swap) // [zBuffer] List of dynamic var commands ---> -BUILDIN(setd) +static BUILDIN(setd) { struct map_session_data *sd = NULL; char varname[100]; @@ -15940,7 +18566,7 @@ BUILDIN(setd) return true; } -int buildin_query_sql_sub(struct script_state *st, struct Sql *handle) +static int buildin_query_sql_sub(struct script_state *st, struct Sql *handle) { int i, j; struct map_session_data *sd = NULL; @@ -16022,11 +18648,13 @@ int buildin_query_sql_sub(struct script_state *st, struct Sql *handle) return true; } -BUILDIN(query_sql) { +static BUILDIN(query_sql) +{ return script->buildin_query_sql_sub(st, map->mysql_handle); } -BUILDIN(query_logsql) { +static BUILDIN(query_logsql) +{ if( !logs->config.sql_logs ) {// logs->mysql_handle == NULL ShowWarning("buildin_query_logsql: SQL logs are disabled, query '%s' will not be executed.\n", script_getstr(st,2)); script_pushint(st,-1); @@ -16036,7 +18664,7 @@ BUILDIN(query_logsql) { } //Allows escaping of a given string. -BUILDIN(escape_sql) +static BUILDIN(escape_sql) { const char *str; char *esc_str; @@ -16050,25 +18678,37 @@ BUILDIN(escape_sql) return true; } -BUILDIN(getd) { +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; } // <--- [zBuffer] List of dynamic var commands // Pet stat [Lance] -BUILDIN(petstat) +static BUILDIN(petstat) { struct pet_data *pd; int flag = script_getnum(st,2); @@ -16094,7 +18734,7 @@ BUILDIN(petstat) return true; } -BUILDIN(callshop) +static BUILDIN(callshop) { struct npc_data *nd; const char *shopname; @@ -16134,7 +18774,7 @@ BUILDIN(callshop) return true; } -BUILDIN(npcshopitem) +static BUILDIN(npcshopitem) { const char* npcname = script_getstr(st, 2); struct npc_data* nd = npc->name2id(npcname); @@ -16163,7 +18803,7 @@ BUILDIN(npcshopitem) return true; } -BUILDIN(npcshopadditem) +static BUILDIN(npcshopadditem) { const char* npcname = script_getstr(st,2); struct npc_data* nd = npc->name2id(npcname); @@ -16192,7 +18832,7 @@ BUILDIN(npcshopadditem) return true; } -BUILDIN(npcshopdelitem) +static BUILDIN(npcshopdelitem) { const char* npcname = script_getstr(st,2); struct npc_data* nd = npc->name2id(npcname); @@ -16214,10 +18854,12 @@ BUILDIN(npcshopdelitem) unsigned int nameid = script_getnum(st,i); ARR_FIND(0, size, n, nd->u.shop.shop_item[n].nameid == nameid); - if (n < size) { - memmove(&nd->u.shop.shop_item[n], &nd->u.shop.shop_item[n+1], sizeof(nd->u.shop.shop_item[0])*(size-n)); - size--; + if (n == size) { + continue; + } else if (n < size - 1) { + memmove(&nd->u.shop.shop_item[n], &nd->u.shop.shop_item[n+1], sizeof(nd->u.shop.shop_item[0]) * (size - n - 1)); } + size--; } RECREATE(nd->u.shop.shop_item, struct npc_item_list, size); @@ -16228,7 +18870,8 @@ BUILDIN(npcshopdelitem) } //Sets a script to attach to a shop npc. -BUILDIN(npcshopattach) { +static BUILDIN(npcshopattach) +{ const char* npcname = script_getstr(st,2); struct npc_data* nd = npc->name2id(npcname); int flag = 1; @@ -16260,7 +18903,7 @@ BUILDIN(npcshopattach) { * 1 - Equip script * 2 - Unequip script *------------------------------------------*/ -BUILDIN(setitemscript) +static BUILDIN(setitemscript) { int item_id,n=0; const char *new_bonus_script; @@ -16306,7 +18949,8 @@ BUILDIN(setitemscript) * is updated to the new rate. Rate must be in the range [1:10000] * Returns 1 if succeeded (added/updated a mob drop) *-------------------------------------------------------*/ -BUILDIN(addmonsterdrop) { +static BUILDIN(addmonsterdrop) +{ struct mob_db *monster; int item_id, rate, i, c = MAX_MOB_DROP; @@ -16366,7 +19010,8 @@ BUILDIN(addmonsterdrop) { * * Returns 1 if succeeded (deleted a mob drop) *-------------------------------------------------------*/ -BUILDIN(delmonsterdrop) { +static BUILDIN(delmonsterdrop) +{ struct mob_db *monster; int item_id, i; @@ -16408,7 +19053,7 @@ BUILDIN(delmonsterdrop) { * Name, Level, race, size, etc... * getmonsterinfo(monsterID,queryIndex); *------------------------------------------*/ -BUILDIN(getmonsterinfo) +static BUILDIN(getmonsterinfo) { struct mob_db *monster; int mob_id; @@ -16447,12 +19092,13 @@ BUILDIN(getmonsterinfo) case 20: script_pushint(st,monster->status.def_ele); break; case 21: script_pushint(st,monster->status.mode); break; case 22: script_pushint(st,monster->mexp); break; + case 23: script_pushint(st, monster->dmg_taken_rate); break; default: script_pushint(st,-1); //wrong Index } return true; } -BUILDIN(checkvending) // check vending [Nab4] +static BUILDIN(checkvending) // check vending [Nab4] { struct map_session_data *sd = NULL; @@ -16470,7 +19116,8 @@ BUILDIN(checkvending) // check vending [Nab4] } // check chatting [Marka] -BUILDIN(checkchatting) { +static BUILDIN(checkchatting) +{ struct map_session_data *sd = NULL; if (script_hasdata(st,2)) @@ -16479,14 +19126,15 @@ BUILDIN(checkchatting) { sd = script->rid2sd(st); if (sd != NULL) - script_pushint(st,(sd->chatID != 0)); + script_pushint(st, (sd->chat_id != 0)); else script_pushint(st,0); return true; } -BUILDIN(checkidle) { +static BUILDIN(checkidle) +{ struct map_session_data *sd = NULL; if (script_hasdata(st, 2)) @@ -16502,7 +19150,7 @@ BUILDIN(checkidle) { return true; } -BUILDIN(searchitem) +static BUILDIN(searchitem) { struct script_data* data = script_getdata(st, 2); const char *itemname = script_getstr(st,3); @@ -16518,7 +19166,7 @@ BUILDIN(searchitem) if ((items[0] = itemdb->exists(atoi(itemname)))) { count = 1; } else { - count = itemdb->search_name_array(items, ARRAYLENGTH(items), itemname, 0); + count = itemdb->search_name_array(items, ARRAYLENGTH(items), itemname, IT_SEARCH_NAME_PARTIAL); if (count > MAX_SEARCH) count = MAX_SEARCH; } @@ -16565,7 +19213,7 @@ BUILDIN(searchitem) } // [zBuffer] List of player cont commands ---> -BUILDIN(rid2name) +static BUILDIN(rid2name) { struct block_list *bl = NULL; int rid = script_getnum(st,2); @@ -16589,7 +19237,8 @@ BUILDIN(rid2name) return true; } -BUILDIN(pcblockmove) { +static BUILDIN(pcblockmove) +{ int id, flag; struct map_session_data *sd = NULL; @@ -16601,13 +19250,101 @@ BUILDIN(pcblockmove) { else sd = script->rid2sd(st); - if (sd != NULL) - sd->state.blockedmove = flag > 0; + if (!sd) + return true; + + if (flag) + sd->block_action.move = 1; + else + sd->block_action.move = 0; + + return true; +} + +static BUILDIN(setpcblock) +{ + struct map_session_data *sd = script_hasdata(st, 4) ? script->id2sd(st, script_getnum(st, 4)) : script->rid2sd(st); + enum pcblock_action_flag type = script_getnum(st, 2); + int state = (script_getnum(st, 3) > 0) ? 1 : 0; + + if (sd == NULL) { + script_pushint(st, 0); + return true; + } + + if ((type & PCBLOCK_MOVE) != 0) + sd->block_action.move = state; + + if ((type & PCBLOCK_ATTACK) != 0) + sd->block_action.attack = state; + + if ((type & PCBLOCK_SKILL) != 0) + sd->block_action.skill = state; + if ((type & PCBLOCK_USEITEM) != 0) + sd->block_action.useitem = state; + + if ((type & PCBLOCK_CHAT) != 0) + sd->block_action.chat = state; + + if ((type & PCBLOCK_IMMUNE) != 0) + sd->block_action.immune = state; + + if ((type & PCBLOCK_SITSTAND) != 0) + sd->block_action.sitstand = state; + + if ((type & PCBLOCK_COMMANDS) != 0) + sd->block_action.commands = state; + + if ((type & PCBLOCK_NPC) != 0) + sd->block_action.npc = state; + + script_pushint(st, 1); return true; } -BUILDIN(pcfollow) +static BUILDIN(checkpcblock) +{ + struct map_session_data *sd = script_hasdata(st, 2) ? script->id2sd(st, script_getnum(st, 2)) : script->rid2sd(st); + int retval = PCBLOCK_NONE; + + if (sd == NULL) { + script_pushint(st, PCBLOCK_NONE); + return true; + } + + if (sd->block_action.move != 0) + retval |= PCBLOCK_MOVE; + + if (sd->block_action.attack != 0) + retval |= PCBLOCK_ATTACK; + + if (sd->block_action.skill != 0) + retval |= PCBLOCK_SKILL; + + if (sd->block_action.useitem != 0) + retval |= PCBLOCK_USEITEM; + + if (sd->block_action.chat != 0) + retval |= PCBLOCK_CHAT; + + if (sd->block_action.immune != 0) + retval |= PCBLOCK_IMMUNE; + + if (sd->block_action.sitstand != 0) + retval |= PCBLOCK_SITSTAND; + + if (sd->block_action.commands != 0) + retval |= PCBLOCK_COMMANDS; + + if (sd->block_action.npc != 0) + retval |= PCBLOCK_NPC; + + script_pushint(st, retval); + return true; +} + +static BUILDIN(pcfollow) { int id, targetid; struct map_session_data *sd = NULL; @@ -16626,7 +19363,7 @@ BUILDIN(pcfollow) return true; } -BUILDIN(pcstopfollow) +static BUILDIN(pcstopfollow) { int id; struct map_session_data *sd = NULL; @@ -16647,7 +19384,8 @@ BUILDIN(pcstopfollow) // [zBuffer] List of mob control commands ---> //## TODO always return if the request/whatever was successfull [FlavioJS] -BUILDIN(getunittype) { +static BUILDIN(getunittype) +{ struct block_list* bl; int value; @@ -16674,39 +19412,1852 @@ BUILDIN(getunittype) { return true; } +/** + * Sets real-time unit data for a game object. + * + * @code{.herc} + * setunitdata <GUID>, <DataType>, <Val1>{, <Val2>, <Val3>} + * @endcode + * + * @param1 GUID GID of the unit. + * @param2 DataType Type of Data to be set for the unit. + * @param3 Value#1 Value to be passed as change in data. + * @param4 Value#2 Optional int value to be passed for certain data types. + * @param5 Value#3 Optional int value to be passed for certain data types. + * @return 1 on success, 0 on failure. + * + * Note: Please make this script command only modify ONE INTEGER value. + * If need to modify string type data, or having multiple arguments, please introduce a new script command. + * + **/ +static BUILDIN(setunitdata) +{ + struct block_list *bl = map->id2bl(script_getnum(st, 2)); + + if (bl == NULL) { + ShowWarning("buildin_setunitdata: Error in finding object with given GID %d!\n", script_getnum(st, 2)); + script_pushint(st, 0); + return false; + } + + int type = script_getnum(st, 3); + + // Type bounds. + if (type < UDT_SIZE || type >= UDT_MAX) { // Note: UDT_TYPE is not valid here + ShowError("buildin_setunitdata: Invalid unit data type %d provided.\n", type); + script_pushint(st, 0); + return false; + } + + const char *mapname = NULL; + int val = 0; + + // Mandatory argument #3. Subject to deprecate. + if (type == UDT_MAPIDXY) { + if (!script_isstringtype(st, 4)) { + ShowError("buildin_setunitdata: Invalid data type for argument #3.\n"); + script_pushint(st, 0); + return false; + } + + mapname = script_getstr(st, 4); + } else { + if (script_isstringtype(st, 4)) { + ShowError("buildin_setunitdata: Invalid data type for argument #3.\n"); + script_pushint(st, 0); + return false; + } + + val = script_getnum(st, 4); + } + +/**************************************************************************************************** + * Define temporary macros. [BEGIN] + ****************************************************************************************************/ + +// Checks if value is out of bounds. +#define setunitdata_check_bounds(arg, min, max) \ + do { \ + if (script_getnum(st, (arg)) < (min) || script_getnum(st, (arg)) > (max)) { \ + ShowError("buildin_setunitdata: Invalid value %d for argument #%d. (min: %d, max: %d)\n", \ + script_getnum(st, (arg)), (arg) - 1, (min), (max)); \ + script_pushint(st, 0); \ + return false; \ + } \ + } while(0); + +// Checks if value is too low. +#define setunitdata_check_min(arg, min) \ + do { \ + if (script_getnum(st, (arg)) < (min)) { \ + ShowError("buildin_setunitdata: Invalid value %d for argument #%d. (min: %d)\n", \ + script_getnum(st, (arg)), (arg) - 1, (min)); \ + script_pushint(st, 0); \ + return false; \ + } \ + } while(0); + +// Checks if the argument doesn't exist, if required. Also checks if the argument exists, if not required. +#define setunitdata_assert_arg(arg, required) \ + do { \ + if (required && !script_hasdata(st, (arg))) { \ + ShowError("buildin_setunitdata: Type %d reqires argument #%d.\n", type, (arg) - 1); \ + script_pushint(st, 0); \ + return false; \ + } else if (!required && script_hasdata(st, arg)) { \ + ShowError("buildin_setunitdata: Argument %d is not required for type %d.\n", (arg) - 1, type); \ + script_pushint(st, 0); \ + return false; \ + } \ + } while (0); + +// Checks if the data is an integer. +#define setunitdata_check_int(arg) \ + do { \ + setunitdata_assert_arg((arg), true); \ + if (script_isstringtype(st, (arg))) { \ + ShowError("buildin_setunitdata: Argument #%d expects integer, string given.\n", (arg) - 1); \ + script_pushint(st, 0); \ + return false; \ + } \ + } while(0); + +// Checks if the data is a string. +#define setunitdata_check_string(arg) \ + do { \ + setunitdata_assert_arg((arg), true); \ + if (script_isinttype(st, (arg))) { \ + ShowError("buildin_setunitdata: Argument #%d expects string, integer given.\n", (arg) - 1); \ + script_pushint(st, 0); \ + return false; \ + } \ + } while(0); + +/**************************************************************************************************** + * Define temporary macros. [END] + ****************************************************************************************************/ + + if (type != UDT_MAPIDXY && type != UDT_WALKTOXY) { + setunitdata_assert_arg(5, false); + setunitdata_assert_arg(6, false); + } + + int val2 = 0; + int val3 = 0; + + struct map_session_data *tsd = NULL; + + switch (type) { + case UDT_SIZE: + setunitdata_check_bounds(4, SZ_SMALL, SZ_BIG); + break; + case UDT_LEVEL: + case UDT_HP: + case UDT_MAXHP: + case UDT_SP: + case UDT_MAXSP: + case UDT_CLASS: + case UDT_HEADBOTTOM: + case UDT_HEADMIDDLE: + case UDT_HEADTOP: + case UDT_CLOTHCOLOR: + case UDT_SHIELD: + case UDT_WEAPON: + case UDT_INTIMACY: + case UDT_LIFETIME: + case UDT_MERC_KILLCOUNT: + case UDT_ROBE: + case UDT_BODY2: + setunitdata_check_min(4, 0); + break; + case UDT_MASTERAID: + setunitdata_check_min(4, 0); + tsd = map->id2sd(val); + + if (tsd == NULL) { + ShowWarning("buildin_setunitdata: Account ID %d not found for master change!\n", val); + script_pushint(st, 0); + return false; + } + + break; + case UDT_MASTERCID: + setunitdata_check_min(4, 0); + tsd = map->charid2sd(val); + + if (tsd == NULL) { + ShowWarning("buildin_setunitdata: Character ID %d not found for master change!\n", val); + script_pushint(st, 0); + return false; + } + + break; + case UDT_MAPIDXY: + if ((val = map->mapname2mapid(mapname)) == INDEX_NOT_FOUND) { + ShowError("buildin_setunitdata: Non-existent map %s provided.\n", mapname); + script_pushint(st, 0); + return false; + } + + setunitdata_check_int(5); + setunitdata_check_int(6); + setunitdata_check_bounds(5, 0, MAX_MAP_SIZE / 2); + setunitdata_check_bounds(6, 0, MAX_MAP_SIZE / 2); + val2 = script_getnum(st, 5); + val3 = script_getnum(st, 6); + break; + case UDT_WALKTOXY: + setunitdata_assert_arg(6, false); + setunitdata_check_int(5); + val2 = script_getnum(st, 5); + setunitdata_check_bounds(4, 0, MAX_MAP_SIZE / 2); + setunitdata_check_bounds(5, 0, MAX_MAP_SIZE / 2); + break; + case UDT_SPEED: + setunitdata_check_bounds(4, 0, MAX_WALK_SPEED); + break; + case UDT_MODE: + setunitdata_check_bounds(4, MD_NONE, MD_MASK); + break; + case UDT_AI: + setunitdata_check_bounds(4, AI_NONE, AI_MAX-1); + break; + case UDT_SCOPTION: + setunitdata_check_bounds(4, OPTION_NOTHING, OPTION_COSTUME); + break; + case UDT_SEX: + setunitdata_check_bounds(4, SEX_FEMALE, SEX_MALE); + break; + case UDT_HAIRSTYLE: + setunitdata_check_bounds(4, 0, battle->bc->max_hair_style); + break; + case UDT_HAIRCOLOR: + setunitdata_check_bounds(4, 0, battle->bc->max_hair_color); + break; + case UDT_LOOKDIR: + setunitdata_check_bounds(4, 0, 7); + break; + case UDT_CANMOVETICK: + setunitdata_check_min(4, 0); + break; + case UDT_STR: + case UDT_AGI: + case UDT_VIT: + case UDT_INT: + case UDT_DEX: + case UDT_LUK: + case UDT_STATPOINT: + case UDT_ATKRANGE: + case UDT_ATKMIN: + case UDT_ATKMAX: + case UDT_MATKMIN: + case UDT_MATKMAX: + case UDT_AMOTION: + case UDT_ADELAY: + case UDT_DMOTION: + setunitdata_check_bounds(4, 0, USHRT_MAX); + break; + case UDT_DEF: + case UDT_MDEF: + case UDT_HIT: + case UDT_FLEE: + case UDT_PDODGE: + case UDT_CRIT: + setunitdata_check_bounds(4, 0, SHRT_MAX); + break; + case UDT_HUNGER: + setunitdata_check_bounds(4, PET_HUNGER_STARVING, PET_HUNGER_STUFFED); // Pets and Homunculi have the same hunger value bounds. + break; + case UDT_RACE: + case UDT_ELETYPE: + case UDT_ELELEVEL: + setunitdata_check_bounds(4, 0, CHAR_MAX); + break; + case UDT_GROUP: + setunitdata_check_bounds(4, 0, INT_MAX); + + struct unit_data *ud = unit->bl2ud2(bl); + + if (ud == NULL) { + ShowError("buildin_setunitdata: ud is NULL!\n"); + script_pushint(st, 0); + return false; + } + + ud->groupId = script_getnum(st, 4); + clif->blname_ack(0, bl); // Send update to client. + script_pushint(st, 1); + return true; + case UDT_DAMAGE_TAKEN_RATE: + setunitdata_check_bounds(4, 1, INT_MAX); + break; + default: + break; + } + +/**************************************************************************************************** + * Undefine temporary macros. [BEGIN] + ****************************************************************************************************/ + +#undef setunitdata_check_bounds +#undef setunitdata_check_min +#undef setunitdata_assert_arg +#undef setunitdata_check_int +#undef setunitdata_check_string + +/**************************************************************************************************** + * Undefine temporary macros. [END] + ****************************************************************************************************/ + + // Set the values. + switch (bl->type) { + case BL_MOB: { + struct mob_data *md = BL_UCAST(BL_MOB, bl); + + if (md == NULL) { + ShowError("buildin_setunitdata: Can't find monster for GID %d!\n", script_getnum(st, 2)); + script_pushint(st, 0); + return false; + } + + switch (type) { + case UDT_SIZE: + md->status.size = (unsigned char)val; + break; + case UDT_LEVEL: + md->level = val; + + if ((battle_config.show_mob_info & 4) != 0) + clif->blname_ack(0, &md->bl); + + break; + case UDT_HP: + status->set_hp(bl, (unsigned int)val, STATUS_HEAL_DEFAULT); + clif->blname_ack(0, &md->bl); + break; + case UDT_MAXHP: + md->status.max_hp = (unsigned int)val; + clif->blname_ack(0, &md->bl); + break; + case UDT_SP: + status->set_sp(bl, (unsigned int)val, STATUS_HEAL_DEFAULT); + break; + case UDT_MAXSP: + md->status.max_sp = (unsigned int)val; + break; + case UDT_MASTERAID: + md->master_id = val; + break; + case UDT_MAPIDXY: + unit->warp(bl, (short)val, (short)val2, (short)val3, CLR_TELEPORT); + break; + case UDT_WALKTOXY: + if (unit->walk_toxy(bl, (short)val, (short)val2, 2) != 0) + unit->movepos(bl, (short)val, (short)val2, 0, 0); + break; + case UDT_SPEED: + md->status.speed = (unsigned short)val; + status->calc_misc(bl, &md->status, md->level); + break; + case UDT_MODE: + md->status.mode = (enum e_mode)val; + break; + case UDT_AI: + md->special_state.ai = (enum ai)val; + break; + case UDT_SCOPTION: + md->sc.option = (unsigned int)val; + break; + case UDT_SEX: + md->vd->sex = (char)val; + break; + case UDT_CLASS: + mob->class_change(md, val); + break; + case UDT_HAIRSTYLE: + clif->changelook(bl, LOOK_HAIR, val); + break; + case UDT_HAIRCOLOR: + clif->changelook(bl, LOOK_HAIR_COLOR, val); + break; + case UDT_HEADBOTTOM: + clif->changelook(bl, LOOK_HEAD_BOTTOM, val); + break; + case UDT_HEADMIDDLE: + clif->changelook(bl, LOOK_HEAD_MID, val); + break; + case UDT_HEADTOP: + clif->changelook(bl, LOOK_HEAD_TOP, val); + break; + case UDT_CLOTHCOLOR: + clif->changelook(bl, LOOK_CLOTHES_COLOR, val); + break; + case UDT_SHIELD: + clif->changelook(bl, LOOK_SHIELD, val); + break; + case UDT_WEAPON: + clif->changelook(bl, LOOK_WEAPON, val); + break; + case UDT_LOOKDIR: + unit->set_dir(bl, (enum unit_dir)val); + break; + case UDT_CANMOVETICK: + md->ud.canmove_tick = val; + break; + case UDT_STR: + md->status.str = (unsigned short)val; + status->calc_misc(bl, &md->status, md->level); + break; + case UDT_AGI: + md->status.agi = (unsigned short)val; + status->calc_misc(bl, &md->status, md->level); + break; + case UDT_VIT: + md->status.vit = (unsigned short)val; + status->calc_misc(bl, &md->status, md->level); + break; + case UDT_INT: + md->status.int_ = (unsigned short)val; + status->calc_misc(bl, &md->status, md->level); + break; + case UDT_DEX: + md->status.dex = (unsigned short)val; + status->calc_misc(bl, &md->status, md->level); + break; + case UDT_LUK: + md->status.luk = (unsigned short)val; + status->calc_misc(bl, &md->status, md->level); + break; + case UDT_ATKRANGE: + md->status.rhw.range = (unsigned short)val; + break; + case UDT_ATKMIN: + md->status.rhw.atk = (unsigned short)val; + break; + case UDT_ATKMAX: + md->status.rhw.atk2 = (unsigned short)val; + break; + case UDT_MATKMIN: + md->status.matk_min = (unsigned short)val; + break; + case UDT_MATKMAX: + md->status.matk_max = (unsigned short)val; + break; + case UDT_DEF: + md->status.def = (defType)val; + break; + case UDT_MDEF: + md->status.mdef = (defType)val; + break; + case UDT_HIT: + md->status.hit = (short)val; + break; + case UDT_FLEE: + md->status.flee = (short)val; + break; + case UDT_PDODGE: + md->status.flee2 = (short)val; + break; + case UDT_CRIT: + md->status.cri = (short)val; + break; + case UDT_RACE: + md->status.race = (unsigned char)val; + break; + case UDT_ELETYPE: + md->status.def_ele = (unsigned char)val; + break; + case UDT_ELELEVEL: + md->status.ele_lv = (unsigned char)val; + break; + case UDT_AMOTION: + md->status.amotion = (unsigned short)val; + break; + case UDT_ADELAY: + md->status.adelay = (unsigned short)val; + break; + case UDT_DMOTION: + md->status.dmotion = (unsigned short)val; + break; + case UDT_DAMAGE_TAKEN_RATE: + md->dmg_taken_rate = (int)val; + break; + default: + ShowWarning("buildin_setunitdata: Invalid data type '%d' for mob unit.\n", type); + script_pushint(st, 0); + return false; + } + + break; + } + case BL_HOM: { + struct homun_data *hd = BL_UCAST(BL_HOM, bl); + + if (hd == NULL) { + ShowError("buildin_setunitdata: Can't find Homunculus for GID %d!\n", script_getnum(st, 2)); + script_pushint(st, 0); + return false; + } + + switch (type) { + case UDT_SIZE: + hd->base_status.size = (unsigned char)val; + break; + case UDT_LEVEL: + hd->homunculus.level = (short)val; + break; + case UDT_HP: + 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, STATUS_HEAL_DEFAULT); + break; + case UDT_MAXSP: + hd->homunculus.max_sp = val; + break; + case UDT_MASTERCID: + hd->homunculus.char_id = val; + hd->master = tsd; + break; + case UDT_MAPIDXY: + unit->warp(bl, (short)val, (short)val2, (short)val3, CLR_TELEPORT); + break; + case UDT_WALKTOXY: + if (unit->walk_toxy(bl, (short)val, (short)val2, 2) != 0) + unit->movepos(bl, (short)val, (short)val2, 0, 0); + break; + case UDT_SPEED: + hd->base_status.speed = (unsigned short)val; + status->calc_misc(bl, &hd->base_status, hd->homunculus.level); + break; + case UDT_LOOKDIR: + unit->set_dir(bl, (enum unit_dir)val); + break; + case UDT_CANMOVETICK: + hd->ud.canmove_tick = val; + break; + case UDT_STR: + hd->base_status.str = (unsigned short)val; + status->calc_misc(bl, &hd->base_status, hd->homunculus.level); + break; + case UDT_AGI: + hd->base_status.agi = (unsigned short)val; + status->calc_misc(bl, &hd->base_status, hd->homunculus.level); + break; + case UDT_VIT: + hd->base_status.vit = (unsigned short)val; + status->calc_misc(bl, &hd->base_status, hd->homunculus.level); + break; + case UDT_INT: + hd->base_status.int_ = (unsigned short)val; + status->calc_misc(bl, &hd->base_status, hd->homunculus.level); + break; + case UDT_DEX: + hd->base_status.dex = (unsigned short)val; + status->calc_misc(bl, &hd->base_status, hd->homunculus.level); + break; + case UDT_LUK: + hd->base_status.luk = (unsigned short)val; + status->calc_misc(bl, &hd->base_status, hd->homunculus.level); + break; + case UDT_ATKRANGE: + hd->base_status.rhw.range = (unsigned short)val; + break; + case UDT_ATKMIN: + hd->base_status.rhw.atk = (unsigned short)val; + break; + case UDT_ATKMAX: + hd->base_status.rhw.atk2 = (unsigned short)val; + break; + case UDT_MATKMIN: + hd->base_status.matk_min = (unsigned short)val; + break; + case UDT_MATKMAX: + hd->base_status.matk_max = (unsigned short)val; + break; + case UDT_DEF: + hd->base_status.def = (defType)val; + break; + case UDT_MDEF: + hd->base_status.mdef = (defType)val; + break; + case UDT_HIT: + hd->base_status.hit = (short)val; + break; + case UDT_FLEE: + hd->base_status.flee = (short)val; + break; + case UDT_PDODGE: + hd->base_status.flee2 = (short)val; + break; + case UDT_CRIT: + hd->base_status.cri = (short)val; + break; + case UDT_RACE: + hd->base_status.race = (unsigned char)val; + break; + case UDT_ELETYPE: + hd->base_status.def_ele = (unsigned char)val; + break; + case UDT_ELELEVEL: + hd->base_status.ele_lv = (unsigned char)val; + break; + case UDT_AMOTION: + hd->base_status.amotion = (unsigned short)val; + break; + case UDT_ADELAY: + hd->base_status.adelay = (unsigned short)val; + break; + case UDT_DMOTION: + hd->base_status.dmotion = (unsigned short)val; + break; + case UDT_HUNGER: + hd->homunculus.hunger = (short)val; + clif->send_homdata(hd->master, SP_HUNGRY, hd->homunculus.hunger); + break; + case UDT_INTIMACY: + homun->add_intimacy(hd, (unsigned int)val); + clif->send_homdata(hd->master, SP_INTIMATE, hd->homunculus.intimacy / 100); + break; + default: + ShowWarning("buildin_setunitdata: Invalid data type '%d' for homunculus unit.\n", type); + script_pushint(st, 0); + return false; + } + + clif->send_homdata(hd->master, SP_ACK, 0); // Send Homunculus data. + break; + } + case BL_PET: { + struct pet_data *pd = BL_UCAST(BL_PET, bl); + + if (pd == NULL) { + ShowError("buildin_setunitdata: Can't find pet for GID %d!\n", script_getnum(st, 2)); + script_pushint(st, 0); + return false; + } + + switch (type) { + case UDT_SIZE: + pd->status.size = (unsigned char)val; + break; + case UDT_LEVEL: + pd->pet.level = (short)val; + break; + case UDT_HP: + 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, STATUS_HEAL_DEFAULT); + break; + case UDT_MAXSP: + pd->status.max_sp = (unsigned int)val; + break; + case UDT_MASTERAID: + pd->pet.account_id = val; + pd->msd = tsd; + break; + case UDT_MAPIDXY: + unit->warp(bl, (short)val, (short)val2, (short)val3, CLR_TELEPORT); + break; + case UDT_WALKTOXY: + if (unit->walk_toxy(bl, (short)val, (short)val2, 2) != 0) + unit->movepos(bl, (short)val, (short)val2, 0, 0); + break; + case UDT_SPEED: + pd->status.speed = (unsigned short)val; + status->calc_misc(bl, &pd->status, pd->pet.level); + break; + case UDT_LOOKDIR: + unit->set_dir(bl, (enum unit_dir)val); + break; + case UDT_CANMOVETICK: + pd->ud.canmove_tick = val; + break; + case UDT_STR: + pd->status.str = (unsigned short)val; + status->calc_misc(bl, &pd->status, pd->pet.level); + break; + case UDT_AGI: + pd->status.agi = (unsigned short)val; + status->calc_misc(bl, &pd->status, pd->pet.level); + break; + case UDT_VIT: + pd->status.vit = (unsigned short)val; + status->calc_misc(bl, &pd->status, pd->pet.level); + break; + case UDT_INT: + pd->status.int_ = (unsigned short)val; + status->calc_misc(bl, &pd->status, pd->pet.level); + break; + case UDT_DEX: + pd->status.dex = (unsigned short)val; + status->calc_misc(bl, &pd->status, pd->pet.level); + break; + case UDT_LUK: + pd->status.luk = (unsigned short)val; + status->calc_misc(bl, &pd->status, pd->pet.level); + break; + case UDT_ATKRANGE: + pd->status.rhw.range = (unsigned short)val; + break; + case UDT_ATKMIN: + pd->status.rhw.atk = (unsigned short)val; + break; + case UDT_ATKMAX: + pd->status.rhw.atk2 = (unsigned short)val; + break; + case UDT_MATKMIN: + pd->status.matk_min = (unsigned short)val; + break; + case UDT_MATKMAX: + pd->status.matk_max = (unsigned short)val; + break; + case UDT_DEF: + pd->status.def = (defType)val; + break; + case UDT_MDEF: + pd->status.mdef = (defType)val; + break; + case UDT_HIT: + pd->status.hit = (short)val; + break; + case UDT_FLEE: + pd->status.flee = (short)val; + break; + case UDT_PDODGE: + pd->status.flee2 = (short)val; + break; + case UDT_CRIT: + pd->status.cri = (short)val; + break; + case UDT_RACE: + pd->status.race = (unsigned char)val; + break; + case UDT_ELETYPE: + pd->status.def_ele = (unsigned char)val; + break; + case UDT_ELELEVEL: + pd->status.ele_lv = (unsigned char)val; + break; + case UDT_AMOTION: + pd->status.amotion = (unsigned short)val; + break; + case UDT_ADELAY: + pd->status.adelay = (unsigned short)val; + break; + case UDT_DMOTION: + pd->status.dmotion = (unsigned short)val; + break; + case UDT_INTIMACY: + pet->set_intimate(pd, val); + clif->send_petdata(pd->msd, pd, 1, pd->pet.intimate); + break; + case UDT_HUNGER: + pet->set_hunger(pd, val); + break; + default: + ShowWarning("buildin_setunitdata: Invalid data type '%d' for pet unit.\n", type); + script_pushint(st, 0); + return false; + } + + clif->send_petstatus(pd->msd); // Send pet data. + break; + } + case BL_MER: { + struct mercenary_data *mc = BL_UCAST(BL_MER, bl); + + if (mc == NULL) { + ShowError("buildin_setunitdata: Can't find mercenary for GID %d!\n", script_getnum(st, 2)); + script_pushint(st, 0); + return false; + } + + switch (type) { + case UDT_SIZE: + mc->base_status.size = (unsigned char)val; + break; + case UDT_HP: + 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, STATUS_HEAL_DEFAULT); + break; + case UDT_MAXSP: + mc->base_status.max_sp = (unsigned int)val; + break; + case UDT_MASTERCID: + mc->mercenary.char_id = val; + break; + case UDT_MAPIDXY: + unit->warp(bl, (short)val, (short)val2, (short)val3, CLR_TELEPORT); + break; + case UDT_WALKTOXY: + if (unit->walk_toxy(bl, (short)val, (short)val2, 2) != 0) + unit->movepos(bl, (short)val, (short)val2, 0, 0); + break; + case UDT_SPEED: + mc->base_status.size = (unsigned char)val; + status->calc_misc(bl, &mc->base_status, mc->db->lv); + break; + case UDT_LOOKDIR: + unit->set_dir(bl, (enum unit_dir)val); + break; + case UDT_CANMOVETICK: + mc->ud.canmove_tick = val; + break; + case UDT_STR: + mc->base_status.str = (unsigned short)val; + status->calc_misc(bl, &mc->base_status, mc->db->lv); + break; + case UDT_AGI: + mc->base_status.agi = (unsigned short)val; + status->calc_misc(bl, &mc->base_status, mc->db->lv); + break; + case UDT_VIT: + mc->base_status.vit = (unsigned short)val; + status->calc_misc(bl, &mc->base_status, mc->db->lv); + break; + case UDT_INT: + mc->base_status.int_ = (unsigned short)val; + status->calc_misc(bl, &mc->base_status, mc->db->lv); + break; + case UDT_DEX: + mc->base_status.dex = (unsigned short)val; + status->calc_misc(bl, &mc->base_status, mc->db->lv); + break; + case UDT_LUK: + mc->base_status.luk = (unsigned short)val; + status->calc_misc(bl, &mc->base_status, mc->db->lv); + break; + case UDT_ATKRANGE: + mc->base_status.rhw.range = (unsigned short)val; + break; + case UDT_ATKMIN: + mc->base_status.rhw.atk = (unsigned short)val; + break; + case UDT_ATKMAX: + mc->base_status.rhw.atk2 = (unsigned short)val; + break; + case UDT_MATKMIN: + mc->base_status.matk_min = (unsigned short)val; + break; + case UDT_MATKMAX: + mc->base_status.matk_max = (unsigned short)val; + break; + case UDT_DEF: + mc->base_status.def = (defType)val; + break; + case UDT_MDEF: + mc->base_status.mdef = (defType)val; + break; + case UDT_HIT: + mc->base_status.hit = (short)val; + break; + case UDT_FLEE: + mc->base_status.flee = (short)val; + break; + case UDT_PDODGE: + mc->base_status.flee2 = (short)val; + break; + case UDT_CRIT: + mc->base_status.cri = (short)val; + break; + case UDT_RACE: + mc->base_status.race = (unsigned char)val; + break; + case UDT_ELETYPE: + mc->base_status.def_ele = (unsigned char)val; + break; + case UDT_ELELEVEL: + mc->base_status.ele_lv = (unsigned char)val; + break; + case UDT_AMOTION: + mc->base_status.amotion = (unsigned short)val; + break; + case UDT_ADELAY: + mc->base_status.adelay = (unsigned short)val; + break; + case UDT_DMOTION: + mc->base_status.dmotion = (unsigned short)val; + break; + case UDT_MERC_KILLCOUNT: + mc->mercenary.kill_count = (unsigned int)val; + break; + case UDT_LIFETIME: + mc->mercenary.life_time = (unsigned int)val; + break; + default: + ShowWarning("buildin_setunitdata: Invalid data type '%d' for mercenary unit.\n", type); + script_pushint(st, 0); + return false; + } + + // Send mercenary data. + clif->mercenary_info(map->charid2sd(mc->mercenary.char_id)); + clif->mercenary_skillblock(map->charid2sd(mc->mercenary.char_id)); + break; + } + case BL_ELEM: { + struct elemental_data *ed = BL_UCAST(BL_ELEM, bl); + + if (ed == NULL) { + ShowError("buildin_setunitdata: Can't find Elemental for GID %d!\n", script_getnum(st, 2)); + script_pushint(st, 0); + return false; + } + + switch (type) { + case UDT_SIZE: + ed->base_status.size = (unsigned char)val; + break; + case UDT_HP: + 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, STATUS_HEAL_DEFAULT); + break; + case UDT_MAXSP: + ed->base_status.max_sp = (unsigned int)val; + break; + case UDT_MASTERCID: + ed->elemental.char_id = val; + break; + case UDT_MAPIDXY: + unit->warp(bl, (short)val, (short)val2, (short)val3, CLR_TELEPORT); + break; + case UDT_WALKTOXY: + if (unit->walk_toxy(bl, (short)val, (short)val2, 2) != 0) + unit->movepos(bl, (short)val, (short)val2, 0, 0); + break; + case UDT_SPEED: + ed->base_status.speed = (unsigned short)val; + status->calc_misc(bl, &ed->base_status, ed->db->lv); + break; + case UDT_LOOKDIR: + unit->set_dir(bl, (enum unit_dir)val); + break; + case UDT_CANMOVETICK: + ed->ud.canmove_tick = val; + break; + case UDT_STR: + ed->base_status.str = (unsigned short)val; + status->calc_misc(bl, &ed->base_status, ed->db->lv); + break; + case UDT_AGI: + ed->base_status.agi = (unsigned short)val; + status->calc_misc(bl, &ed->base_status, ed->db->lv); + break; + case UDT_VIT: + ed->base_status.vit = (unsigned short)val; + status->calc_misc(bl, &ed->base_status, ed->db->lv); + break; + case UDT_INT: + ed->base_status.int_ = (unsigned short)val; + status->calc_misc(bl, &ed->base_status, ed->db->lv); + break; + case UDT_DEX: + ed->base_status.dex = (unsigned short)val; + status->calc_misc(bl, &ed->base_status, ed->db->lv); + break; + case UDT_LUK: + ed->base_status.luk = (unsigned short)val; + status->calc_misc(bl, &ed->base_status, ed->db->lv); + break; + case UDT_ATKRANGE: + ed->base_status.rhw.range = (unsigned short)val; + break; + case UDT_ATKMIN: + ed->base_status.rhw.atk = (unsigned short)val; + break; + case UDT_ATKMAX: + ed->base_status.rhw.atk2 = (unsigned short)val; + break; + case UDT_MATKMIN: + ed->base_status.matk_min = (unsigned short)val; + break; + case UDT_MATKMAX: + ed->base_status.matk_max = (unsigned short)val; + break; + case UDT_DEF: + ed->base_status.def = (defType)val; + break; + case UDT_MDEF: + ed->base_status.mdef = (defType)val; + break; + case UDT_HIT: + ed->base_status.hit = (short)val; + break; + case UDT_FLEE: + ed->base_status.flee = (short)val; + break; + case UDT_PDODGE: + ed->base_status.flee2 = (short)val; + break; + case UDT_CRIT: + ed->base_status.cri = (short)val; + break; + case UDT_RACE: + ed->base_status.race = (unsigned char)val; + break; + case UDT_ELETYPE: + ed->base_status.def_ele = (unsigned char)val; + break; + case UDT_ELELEVEL: + ed->base_status.ele_lv = (unsigned char)val; + break; + case UDT_AMOTION: + ed->base_status.amotion = (unsigned short)val; + break; + case UDT_ADELAY: + ed->base_status.adelay = (unsigned short)val; + break; + case UDT_DMOTION: + ed->base_status.dmotion = (unsigned short)val; + break; + case UDT_LIFETIME: + ed->elemental.life_time = val; + break; + default: + ShowWarning("buildin_setunitdata: Invalid data type '%d' for elemental unit.\n", type); + script_pushint(st, 0); + return false; + } + + clif->elemental_info(ed->master); // Send Elemental data. + break; + } + case BL_NPC: { + struct npc_data *nd = BL_UCAST(BL_NPC, bl); + + if (nd == NULL) { + ShowError("buildin_setunitdata: Can't find NPC for GID %d!\n", script_getnum(st, 2)); + script_pushint(st, 0); + return false; + } + + switch (type) { + case UDT_SIZE: + nd->status.size = (unsigned char)val; + break; + case UDT_LEVEL: + nd->level = (unsigned short)val; + break; + case UDT_HP: + 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, STATUS_HEAL_DEFAULT); + break; + case UDT_MAXSP: + nd->status.max_sp = (unsigned int)val; + break; + case UDT_MAPIDXY: + unit->warp(bl, (short)val, (short)val2, (short)val3, CLR_TELEPORT); + break; + case UDT_WALKTOXY: + if (unit->walk_toxy(bl, (short)val, (short)val2, 2) != 0) + unit->movepos(bl, (short)val, (short)val2, 0, 0); + break; + case UDT_CLASS: + npc->setclass(nd, (short)val); + break; + case UDT_SPEED: + nd->speed = (short)val; + status->calc_misc(bl, &nd->status, nd->level); + break; + case UDT_LOOKDIR: + unit->set_dir(bl, (enum unit_dir)val); + break; + case UDT_STR: + nd->status.str = (unsigned short)val; + status->calc_misc(bl, &nd->status, nd->level); + break; + case UDT_AGI: + nd->status.agi = (unsigned short)val; + status->calc_misc(bl, &nd->status, nd->level); + break; + case UDT_VIT: + nd->status.vit = (unsigned short)val; + status->calc_misc(bl, &nd->status, nd->level); + break; + case UDT_INT: + nd->status.int_ = (unsigned short)val; + status->calc_misc(bl, &nd->status, nd->level); + break; + case UDT_DEX: + nd->status.dex = (unsigned short)val; + status->calc_misc(bl, &nd->status, nd->level); + break; + case UDT_LUK: + nd->status.luk = (unsigned short)val; + status->calc_misc(bl, &nd->status, nd->level); + break; + case UDT_STATPOINT: + nd->stat_point = (unsigned short)val; + break; + case UDT_ATKRANGE: + nd->status.rhw.range = (unsigned short)val; + break; + case UDT_ATKMIN: + nd->status.rhw.atk = (unsigned short)val; + break; + case UDT_ATKMAX: + nd->status.rhw.atk2 = (unsigned short)val; + break; + case UDT_MATKMIN: + nd->status.matk_min = (unsigned short)val; + break; + case UDT_MATKMAX: + nd->status.matk_max = (unsigned short)val; + break; + case UDT_DEF: + nd->status.def = (defType)val; + break; + case UDT_MDEF: + nd->status.mdef = (defType)val; + break; + case UDT_HIT: + nd->status.hit = (short)val; + break; + case UDT_FLEE: + nd->status.flee = (short)val; + break; + case UDT_PDODGE: + nd->status.flee2 = (short)val; + break; + case UDT_CRIT: + nd->status.cri = (short)val; + break; + case UDT_RACE: + nd->status.race = (unsigned char)val; + break; + case UDT_ELETYPE: + nd->status.def_ele = (unsigned char)val; + break; + case UDT_ELELEVEL: + nd->status.ele_lv = (unsigned char)val; + break; + case UDT_AMOTION: + nd->status.amotion = (unsigned short)val; + break; + case UDT_ADELAY: + nd->status.adelay = (unsigned short)val; + break; + case UDT_DMOTION: + nd->status.dmotion = (unsigned short)val; + break; + case UDT_SEX: + nd->vd.sex = (char)val; + npc->refresh(nd); + break; + case UDT_HAIRSTYLE: + clif->changelook(bl, LOOK_HAIR, val); + break; + case UDT_HAIRCOLOR: + clif->changelook(bl, LOOK_HAIR_COLOR, val); + break; + case UDT_HEADBOTTOM: + clif->changelook(bl, LOOK_HEAD_BOTTOM, val); + break; + case UDT_HEADMIDDLE: + clif->changelook(bl, LOOK_HEAD_MID, val); + break; + case UDT_HEADTOP: + clif->changelook(bl, LOOK_HEAD_TOP, val); + break; + case UDT_CLOTHCOLOR: + clif->changelook(bl, LOOK_CLOTHES_COLOR, val); + break; + case UDT_SHIELD: + clif->changelook(bl, LOOK_SHIELD, val); + break; + case UDT_WEAPON: + clif->changelook(bl, LOOK_WEAPON, val); + break; + case UDT_ROBE: + clif->changelook(bl, LOOK_ROBE, val); + break; + case UDT_BODY2: + clif->changelook(bl, LOOK_BODY2, val); + break; + default: + ShowWarning("buildin_setunitdata: Invalid data type '%d' for NPC unit.\n", type); + script_pushint(st, 0); + return false; + } + + break; + } + default: + ShowError("buildin_setunitdata: Unknown object!\n"); + script_pushint(st, 0); + return false; + } // End of bl->type switch. + + script_pushint(st, 1); + + return true; +} + +/** + * Retrieves real-time data for a game object. + * Getunitdata <GUID>,<DataType>{,<Variable>} + * @param1 GUID Game object unique Id. + * @param2 DataType Type of Data to be set for the unit. + * @param3 Variable array reference to store data into. (used for UDT_MAPIDXY) + * @return 0 on failure, <value> on success + + Note: Please make this script command only return ONE INTEGER value. + If the unit data having multiple arguments, or need to return in array, + please introduce a new script command. + */ +static BUILDIN(getunitdata) +{ + struct block_list *bl; + const char *udtype = NULL; + const struct map_session_data *sd = NULL; + int type = 0; + char* name = NULL; + struct script_data *data = script_hasdata(st,4)?script_getdata(st, 4):NULL; + + bl = map->id2bl(script_getnum(st, 2)); + + if (bl == NULL) { + ShowWarning("buildin_getunitdata: Error in finding object with given GID %d!\n", script_getnum(st, 2)); + script_pushint(st, -1); + return false; + } + + type = script_getnum(st, 3); + + /* Type check */ + if (type < UDT_TYPE || type >= UDT_MAX) { + ShowError("buildin_getunitdata: Invalid unit data type %d provided.\n", type); + script_pushint(st, -1); + return false; + } + + /* Argument checks. Subject to deprecate */ + if (type == UDT_MAPIDXY) { + if (data == NULL || !data_isreference(data)) { + ShowWarning("buildin_getunitdata: Error in argument 3. Please provide a reference variable to store values in.\n"); + script_pushint(st, -1); + return false; + } + + name = reference_getname(data); + + if (not_server_variable(*name)) { + sd = script->rid2sd(st); + if (sd == NULL) { + ShowWarning("buildin_getunitdata: Player not attached! Cannot use player variable %s.\n",name); + script_pushint(st, -1); + return true;// no player attached + } + } + } else if (type == UDT_GROUP) { + struct unit_data *ud = unit->bl2ud(bl); + if (ud == NULL) { + ShowError("buildin_setunitdata: ud is NULL!\n"); + script_pushint(st, -1); + return false; + } + script_pushint(st, ud->groupId); + return true; + } + +#define getunitdata_sub(idx__,var__) script->setd_sub(st,NULL,name,(idx__),(void *)h64BPTRSIZE((int)(var__)),data->ref); + + switch (bl->type) { + case BL_MOB: + { + const struct mob_data *md = BL_UCAST(BL_MOB, bl); + + nullpo_retr(false, md); + + switch (type) + { + case UDT_TYPE: script_pushint(st, BL_MOB); break; + case UDT_SIZE: script_pushint(st, md->status.size); break; + case UDT_LEVEL: script_pushint(st, md->level); break; + case UDT_HP: script_pushint(st, md->status.hp); break; + case UDT_MAXHP: script_pushint(st, md->status.max_hp); break; + case UDT_SP: script_pushint(st, md->status.sp); break; + case UDT_MAXSP: script_pushint(st, md->status.max_sp); break; + case UDT_MAPIDXY: + getunitdata_sub(0, md->bl.m); + getunitdata_sub(1, md->bl.x); + getunitdata_sub(2, md->bl.y); + break; + case UDT_SPEED: script_pushint(st, md->status.speed); break; + case UDT_MODE: script_pushint(st, md->status.mode); break; + case UDT_AI: script_pushint(st, md->special_state.ai); break; + case UDT_SCOPTION: script_pushint(st, md->sc.option); break; + case UDT_SEX: script_pushint(st, md->vd->sex); break; + case UDT_CLASS: script_pushint(st, md->vd->class); break; + case UDT_HAIRSTYLE: script_pushint(st, md->vd->hair_style); break; + case UDT_HAIRCOLOR: script_pushint(st, md->vd->hair_color); break; + case UDT_HEADBOTTOM: script_pushint(st, md->vd->head_bottom); break; + case UDT_HEADMIDDLE: script_pushint(st, md->vd->head_mid); break; + case UDT_HEADTOP: script_pushint(st, md->vd->head_top); break; + case UDT_CLOTHCOLOR: script_pushint(st, md->vd->cloth_color); break; + case UDT_SHIELD: script_pushint(st, md->vd->shield); break; + case UDT_WEAPON: script_pushint(st, md->vd->weapon); break; + case UDT_LOOKDIR: script_pushint(st, md->ud.dir); break; + case UDT_CANMOVETICK: script_pushint(st, md->ud.canmove_tick); break; + case UDT_STR: script_pushint(st, md->status.str); break; + case UDT_AGI: script_pushint(st, md->status.agi); break; + case UDT_VIT: script_pushint(st, md->status.vit); break; + case UDT_INT: script_pushint(st, md->status.int_); break; + case UDT_DEX: script_pushint(st, md->status.dex); break; + case UDT_LUK: script_pushint(st, md->status.luk); break; + case UDT_ATKRANGE: script_pushint(st, md->status.rhw.range); break; + case UDT_ATKMIN: script_pushint(st, md->status.rhw.atk); break; + case UDT_ATKMAX: script_pushint(st, md->status.rhw.atk2); break; + case UDT_MATKMIN: script_pushint(st, md->status.matk_min); break; + case UDT_MATKMAX: script_pushint(st, md->status.matk_max); break; + case UDT_DEF: script_pushint(st, md->status.def); break; + case UDT_MDEF: script_pushint(st, md->status.mdef); break; + case UDT_HIT: script_pushint(st, md->status.hit); break; + case UDT_FLEE: script_pushint(st, md->status.flee); break; + case UDT_PDODGE: script_pushint(st, md->status.flee2); break; + case UDT_CRIT: script_pushint(st, md->status.cri); break; + case UDT_RACE: script_pushint(st, md->status.race); break; + case UDT_ELETYPE: script_pushint(st, md->status.def_ele); break; + case UDT_ELELEVEL: script_pushint(st, md->status.ele_lv); break; + case UDT_AMOTION: script_pushint(st, md->status.amotion); break; + case UDT_ADELAY: script_pushint(st, md->status.adelay); break; + case UDT_DMOTION: script_pushint(st, md->status.dmotion); break; + case UDT_DAMAGE_TAKEN_RATE: script_pushint(st, md->dmg_taken_rate); break; + default: + ShowWarning("buildin_getunitdata: Invalid data type '%s' for Mob unit.\n", udtype); + script_pushint(st, -1); + return false; + } + } + break; + case BL_HOM: + { + const struct homun_data *hd = BL_UCAST(BL_HOM, bl); + + nullpo_retr(false, hd); + + switch (type) + { + case UDT_TYPE: script_pushint(st, BL_HOM); break; + case UDT_SIZE: script_pushint(st, hd->base_status.size); break; + case UDT_LEVEL: script_pushint(st, hd->homunculus.level); break; + case UDT_HP: script_pushint(st, hd->base_status.hp); break; + case UDT_MAXHP: script_pushint(st, hd->base_status.max_hp); break; + case UDT_SP: script_pushint(st, hd->base_status.sp); break; + case UDT_MAXSP: script_pushint(st, hd->base_status.max_sp); break; + case UDT_MAPIDXY: + getunitdata_sub(0, hd->bl.m); + getunitdata_sub(1, hd->bl.x); + getunitdata_sub(2, hd->bl.y); + break; + case UDT_SPEED: script_pushint(st, hd->base_status.speed); break; + case UDT_LOOKDIR: script_pushint(st, hd->ud.dir); break; + case UDT_CANMOVETICK: script_pushint(st, hd->ud.canmove_tick); break; + case UDT_MODE: script_pushint(st, hd->base_status.mode); break; + case UDT_STR: script_pushint(st, hd->base_status.str); break; + case UDT_AGI: script_pushint(st, hd->base_status.agi); break; + case UDT_VIT: script_pushint(st, hd->base_status.vit); break; + case UDT_INT: script_pushint(st, hd->base_status.int_); break; + case UDT_DEX: script_pushint(st, hd->base_status.dex); break; + case UDT_LUK: script_pushint(st, hd->base_status.luk); break; + case UDT_ATKRANGE: script_pushint(st, hd->base_status.rhw.range); break; + case UDT_ATKMIN: script_pushint(st, hd->base_status.rhw.atk); break; + case UDT_ATKMAX: script_pushint(st, hd->base_status.rhw.atk2); break; + case UDT_MATKMIN: script_pushint(st, hd->base_status.matk_min); break; + case UDT_MATKMAX: script_pushint(st, hd->base_status.matk_max); break; + case UDT_DEF: script_pushint(st, hd->base_status.def); break; + case UDT_MDEF: script_pushint(st, hd->base_status.mdef); break; + case UDT_HIT: script_pushint(st, hd->base_status.hit); break; + case UDT_FLEE: script_pushint(st, hd->base_status.flee); break; + case UDT_PDODGE: script_pushint(st, hd->base_status.flee2); break; + case UDT_CRIT: script_pushint(st, hd->base_status.cri); break; + case UDT_RACE: script_pushint(st, hd->base_status.race); break; + case UDT_ELETYPE: script_pushint(st, hd->base_status.def_ele); break; + case UDT_ELELEVEL: script_pushint(st, hd->base_status.ele_lv); break; + case UDT_AMOTION: script_pushint(st, hd->base_status.amotion); break; + case UDT_ADELAY: script_pushint(st, hd->base_status.adelay); break; + case UDT_DMOTION: script_pushint(st, hd->base_status.dmotion); break; + case UDT_MASTERCID: script_pushint(st, hd->homunculus.char_id); break; + case UDT_HUNGER: script_pushint(st, hd->homunculus.hunger); break; + case UDT_INTIMACY: script_pushint(st, hd->homunculus.intimacy); break; + default: + ShowWarning("buildin_getunitdata: Invalid data type '%s' for Homunculus unit.\n", udtype); + script_pushint(st, -1); + return false; + } + } + break; + case BL_PET: + { + const struct pet_data *pd = BL_UCAST(BL_PET, bl); + + nullpo_retr(false, pd); + + switch (type) + { + case UDT_TYPE: script_pushint(st, BL_PET); break; + case UDT_SIZE: script_pushint(st, pd->status.size); break; + case UDT_LEVEL: script_pushint(st, pd->pet.level); break; + case UDT_HP: script_pushint(st, pd->status.hp); break; + case UDT_MAXHP: script_pushint(st, pd->status.max_hp); break; + case UDT_SP: script_pushint(st, pd->status.sp); break; + case UDT_MAXSP: script_pushint(st, pd->status.max_sp); break; + case UDT_MAPIDXY: + getunitdata_sub(0, pd->bl.m); + getunitdata_sub(1, pd->bl.x); + getunitdata_sub(2, pd->bl.y); + break; + case UDT_SPEED: script_pushint(st, pd->status.speed); break; + case UDT_LOOKDIR: script_pushint(st, pd->ud.dir); break; + case UDT_CANMOVETICK: script_pushint(st, pd->ud.canmove_tick); break; + case UDT_MODE: script_pushint(st, pd->status.mode); break; + case UDT_STR: script_pushint(st, pd->status.str); break; + case UDT_AGI: script_pushint(st, pd->status.agi); break; + case UDT_VIT: script_pushint(st, pd->status.vit); break; + case UDT_INT: script_pushint(st, pd->status.int_); break; + case UDT_DEX: script_pushint(st, pd->status.dex); break; + case UDT_LUK: script_pushint(st, pd->status.luk); break; + case UDT_ATKRANGE: script_pushint(st, pd->status.rhw.range); break; + case UDT_ATKMIN: script_pushint(st, pd->status.rhw.atk); break; + case UDT_ATKMAX: script_pushint(st, pd->status.rhw.atk2); break; + case UDT_MATKMIN: script_pushint(st, pd->status.matk_min); break; + case UDT_MATKMAX: script_pushint(st, pd->status.matk_max); break; + case UDT_DEF: script_pushint(st, pd->status.def); break; + case UDT_MDEF: script_pushint(st, pd->status.mdef); break; + case UDT_HIT: script_pushint(st, pd->status.hit); break; + case UDT_FLEE: script_pushint(st, pd->status.flee); break; + case UDT_PDODGE: script_pushint(st, pd->status.flee2); break; + case UDT_CRIT: script_pushint(st, pd->status.cri); break; + case UDT_RACE: script_pushint(st, pd->status.race); break; + case UDT_ELETYPE: script_pushint(st, pd->status.def_ele); break; + case UDT_ELELEVEL: script_pushint(st, pd->status.ele_lv); break; + case UDT_AMOTION: script_pushint(st, pd->status.amotion); break; + case UDT_ADELAY: script_pushint(st, pd->status.adelay); break; + case UDT_DMOTION: script_pushint(st, pd->status.dmotion); break; + case UDT_MASTERAID: script_pushint(st, pd->pet.account_id); break; + case UDT_HUNGER: script_pushint(st, pd->pet.hungry); break; + case UDT_INTIMACY: script_pushint(st, pd->pet.intimate); break; + default: + ShowWarning("buildin_getunitdata: Invalid data type '%s' for Pet unit.\n", udtype); + script_pushint(st, -1); + return false; + } + } + break; + case BL_MER: + { + const struct mercenary_data *mc = BL_UCAST(BL_MER, bl); + + nullpo_retr(false, mc); + + switch (type) + { + case UDT_TYPE: script_pushint(st, BL_MER); break; + case UDT_SIZE: script_pushint(st, mc->base_status.size); break; + case UDT_HP: script_pushint(st, mc->base_status.hp); break; + case UDT_MAXHP: script_pushint(st, mc->base_status.max_hp); break; + case UDT_SP: script_pushint(st, mc->base_status.sp); break; + case UDT_MAXSP: script_pushint(st, mc->base_status.max_sp); break; + case UDT_MAPIDXY: + getunitdata_sub(0, mc->bl.m); + getunitdata_sub(1, mc->bl.x); + getunitdata_sub(2, mc->bl.y); + break; + case UDT_SPEED: script_pushint(st, mc->base_status.speed); break; + case UDT_LOOKDIR: script_pushint(st, mc->ud.dir); break; + case UDT_CANMOVETICK: script_pushint(st, mc->ud.canmove_tick); break; + case UDT_MODE: script_pushint(st, mc->base_status.mode); break; + case UDT_STR: script_pushint(st, mc->base_status.str); break; + case UDT_AGI: script_pushint(st, mc->base_status.agi); break; + case UDT_VIT: script_pushint(st, mc->base_status.vit); break; + case UDT_INT: script_pushint(st, mc->base_status.int_); break; + case UDT_DEX: script_pushint(st, mc->base_status.dex); break; + case UDT_LUK: script_pushint(st, mc->base_status.luk); break; + case UDT_ATKRANGE: script_pushint(st, mc->base_status.rhw.range); break; + case UDT_ATKMIN: script_pushint(st, mc->base_status.rhw.atk); break; + case UDT_ATKMAX: script_pushint(st, mc->base_status.rhw.atk2); break; + case UDT_MATKMIN: script_pushint(st, mc->base_status.matk_min); break; + case UDT_MATKMAX: script_pushint(st, mc->base_status.matk_max); break; + case UDT_DEF: script_pushint(st, mc->base_status.def); break; + case UDT_MDEF: script_pushint(st, mc->base_status.mdef); break; + case UDT_HIT: script_pushint(st, mc->base_status.hit); break; + case UDT_FLEE: script_pushint(st, mc->base_status.flee); break; + case UDT_PDODGE: script_pushint(st, mc->base_status.flee2); break; + case UDT_CRIT: script_pushint(st, mc->base_status.cri); break; + case UDT_RACE: script_pushint(st, mc->base_status.race); break; + case UDT_ELETYPE: script_pushint(st, mc->base_status.def_ele); break; + case UDT_ELELEVEL: script_pushint(st, mc->base_status.ele_lv); break; + case UDT_AMOTION: script_pushint(st, mc->base_status.amotion); break; + case UDT_ADELAY: script_pushint(st, mc->base_status.adelay); break; + case UDT_DMOTION: script_pushint(st, mc->base_status.dmotion); break; + case UDT_MASTERCID: script_pushint(st, mc->mercenary.char_id); break; + case UDT_MERC_KILLCOUNT: script_pushint(st, mc->mercenary.kill_count); break; + case UDT_LIFETIME: script_pushint(st, mc->mercenary.life_time); break; + default: + ShowWarning("buildin_getunitdata: Invalid data type '%s' for Mercenary unit.\n", udtype); + script_pushint(st, -1); + return false; + } + } + break; + case BL_ELEM: + { + const struct elemental_data *ed = BL_UCAST(BL_ELEM, bl); + + nullpo_retr(false, ed); + + switch (type) + { + case UDT_TYPE: script_pushint(st, BL_ELEM); break; + case UDT_SIZE: script_pushint(st, ed->base_status.size); break; + case UDT_HP: script_pushint(st, ed->base_status.hp); break; + case UDT_MAXHP: script_pushint(st, ed->base_status.max_hp); break; + case UDT_SP: script_pushint(st, ed->base_status.sp); break; + case UDT_MAXSP: script_pushint(st, ed->base_status.max_sp); break; + case UDT_MAPIDXY: + getunitdata_sub(0, ed->bl.m); + getunitdata_sub(1, ed->bl.x); + getunitdata_sub(2, ed->bl.y); + break; + case UDT_SPEED: script_pushint(st, ed->base_status.speed); break; + case UDT_LOOKDIR: script_pushint(st, ed->ud.dir); break; + case UDT_CANMOVETICK: script_pushint(st, ed->ud.canmove_tick); break; + case UDT_MODE: script_pushint(st, ed->base_status.mode); break; + case UDT_STR: script_pushint(st, ed->base_status.str); break; + case UDT_AGI: script_pushint(st, ed->base_status.agi); break; + case UDT_VIT: script_pushint(st, ed->base_status.vit); break; + case UDT_INT: script_pushint(st, ed->base_status.int_); break; + case UDT_DEX: script_pushint(st, ed->base_status.dex); break; + case UDT_LUK: script_pushint(st, ed->base_status.luk); break; + case UDT_ATKRANGE: script_pushint(st, ed->base_status.rhw.range); break; + case UDT_ATKMIN: script_pushint(st, ed->base_status.rhw.atk); break; + case UDT_ATKMAX: script_pushint(st, ed->base_status.rhw.atk2); break; + case UDT_MATKMIN: script_pushint(st, ed->base_status.matk_min); break; + case UDT_MATKMAX: script_pushint(st, ed->base_status.matk_max); break; + case UDT_DEF: script_pushint(st, ed->base_status.def); break; + case UDT_MDEF: script_pushint(st, ed->base_status.mdef); break; + case UDT_HIT: script_pushint(st, ed->base_status.hit); break; + case UDT_FLEE: script_pushint(st, ed->base_status.flee); break; + case UDT_PDODGE: script_pushint(st, ed->base_status.flee2); break; + case UDT_CRIT: script_pushint(st, ed->base_status.cri); break; + case UDT_RACE: script_pushint(st, ed->base_status.race); break; + case UDT_ELETYPE: script_pushint(st, ed->base_status.def_ele); break; + case UDT_ELELEVEL: script_pushint(st, ed->base_status.ele_lv); break; + case UDT_AMOTION: script_pushint(st, ed->base_status.amotion); break; + case UDT_ADELAY: script_pushint(st, ed->base_status.adelay); break; + case UDT_DMOTION: script_pushint(st, ed->base_status.dmotion); break; + case UDT_MASTERCID: script_pushint(st, ed->elemental.char_id); break; + default: + ShowWarning("buildin_getunitdata: Invalid data type '%s' for Elemental unit.\n", udtype); + script_pushint(st, -1); + return false; + } + } + break; + case BL_NPC: + { + const struct npc_data *nd = BL_UCAST(BL_NPC, bl); + + nullpo_retr(false, nd); + + switch (type) + { + case UDT_TYPE: script_pushint(st, BL_NPC); break; + case UDT_SIZE: script_pushint(st, nd->status.size); break; + case UDT_HP: script_pushint(st, nd->status.hp); break; + case UDT_MAXHP: script_pushint(st, nd->status.max_hp); break; + case UDT_SP: script_pushint(st, nd->status.sp); break; + case UDT_MAXSP: script_pushint(st, nd->status.max_sp); break; + case UDT_MAPIDXY: + getunitdata_sub(0, bl->m); + getunitdata_sub(1, bl->x); + getunitdata_sub(2, bl->y); + break; + case UDT_SPEED: script_pushint(st, nd->status.speed); break; + case UDT_LOOKDIR: script_pushint(st, nd->ud->dir); break; + case UDT_CANMOVETICK: script_pushint(st, nd->ud->canmove_tick); break; + case UDT_MODE: script_pushint(st, nd->status.mode); break; + case UDT_STR: script_pushint(st, nd->status.str); break; + case UDT_AGI: script_pushint(st, nd->status.agi); break; + case UDT_VIT: script_pushint(st, nd->status.vit); break; + case UDT_INT: script_pushint(st, nd->status.int_); break; + case UDT_DEX: script_pushint(st, nd->status.dex); break; + case UDT_LUK: script_pushint(st, nd->status.luk); break; + case UDT_ATKRANGE: script_pushint(st, nd->status.rhw.range); break; + case UDT_ATKMIN: script_pushint(st, nd->status.rhw.atk); break; + case UDT_ATKMAX: script_pushint(st, nd->status.rhw.atk2); break; + case UDT_MATKMIN: script_pushint(st, nd->status.matk_min); break; + case UDT_MATKMAX: script_pushint(st, nd->status.matk_max); break; + case UDT_DEF: script_pushint(st, nd->status.def); break; + case UDT_MDEF: script_pushint(st, nd->status.mdef); break; + case UDT_HIT: script_pushint(st, nd->status.hit); break; + case UDT_FLEE: script_pushint(st, nd->status.flee); break; + case UDT_PDODGE: script_pushint(st, nd->status.flee2); break; + case UDT_CRIT: script_pushint(st, nd->status.cri); break; + case UDT_RACE: script_pushint(st, nd->status.race); break; + case UDT_ELETYPE: script_pushint(st, nd->status.def_ele); break; + case UDT_ELELEVEL: script_pushint(st, nd->status.ele_lv); break; + case UDT_AMOTION: script_pushint(st, nd->status.amotion); break; + case UDT_ADELAY: script_pushint(st, nd->status.adelay); break; + case UDT_DMOTION: script_pushint(st, nd->status.dmotion); break; + case UDT_SEX: script_pushint(st, nd->vd.sex); break; + case UDT_CLASS: script_pushint(st, nd->vd.class); break; + case UDT_HAIRSTYLE: script_pushint(st, nd->vd.hair_style); break; + case UDT_HAIRCOLOR: script_pushint(st, nd->vd.hair_color); break; + case UDT_HEADBOTTOM: script_pushint(st, nd->vd.head_bottom); break; + case UDT_HEADMIDDLE: script_pushint(st, nd->vd.head_mid); break; + case UDT_HEADTOP: script_pushint(st, nd->vd.head_top); break; + case UDT_CLOTHCOLOR: script_pushint(st, nd->vd.cloth_color); break; + case UDT_SHIELD: script_pushint(st, nd->vd.shield); break; + case UDT_WEAPON: script_pushint(st, nd->vd.weapon); break; + case UDT_ROBE: script_pushint(st, nd->vd.robe); break; + case UDT_BODY2: script_pushint(st, nd->vd.body_style); break; + default: + ShowWarning("buildin_getunitdata: Invalid data type '%s' for NPC unit.\n", udtype); + script_pushint(st, -1); + return false; + } + } + break; + default: + ShowError("buildin_getunitdata: Unknown object!\n"); + script_pushint(st, -1); + return false; + } // end of bl->type switch + +#undef getunitdata_sub + + return true; +} + +/** + * Gets the name of a Unit. + * Supported types are [MOB|HOM|PET|NPC]. + * MER and ELEM don't support custom names. + * + * @command getunitname <GUID>; + * @param GUID Game Object Unique ID. + * @return boolean or Name of the game object. + */ +static BUILDIN(getunitname) +{ + const struct block_list* bl = NULL; + + bl = map->id2bl(script_getnum(st, 2)); + + if (bl == NULL) { + ShowWarning("buildin_getunitname: Error in finding object with given game ID %d!\n", script_getnum(st, 2)); + script_pushconststr(st, "Unknown"); + return false; + } + + script_pushstrcopy(st, status->get_name(bl)); + + return true; +} + +/** + * Changes the name of a bl. + * Supported types are [MOB|HOM|PET]. + * For NPC see 'setnpcdisplay', MER and ELEM don't support custom names. + * + * @command setunitname <GUID>,<name>; + * @param GUID Game object unique ID. + * @param Name as string. + * @return boolean. + */ +static BUILDIN(setunitname) +{ + struct block_list* bl = map->id2bl(script_getnum(st, 2)); + + if (bl == NULL) { + ShowWarning("buildin_setunitname: Game object with ID %d was not found!\n", script_getnum(st, 2)); + script_pushint(st, 0); + return false; + } + + switch (bl->type) { + case BL_MOB: + { + struct mob_data *md = BL_UCAST(BL_MOB, bl); + if (md == NULL) { + ShowWarning("buildin_setunitname: Error in finding object BL_MOB!\n"); + script_pushint(st, 0); + return false; + } + safestrncpy(md->name, script_getstr(st, 3), NAME_LENGTH); + } + break; + case BL_HOM: + { + struct homun_data *hd = BL_UCAST(BL_HOM, bl); + if (hd == NULL) { + ShowWarning("buildin_setunitname: Error in finding object BL_HOM!\n"); + script_pushint(st, 0); + return false; + } + safestrncpy(hd->homunculus.name, script_getstr(st, 3), NAME_LENGTH); + } + break; + case BL_PET: + { + struct pet_data *pd = BL_UCAST(BL_PET, bl); + if (pd == NULL) { + ShowWarning("buildin_setunitname: Error in finding object BL_PET!\n"); + script_pushint(st, 0); + return false; + } + safestrncpy(pd->pet.name, script_getstr(st, 3), NAME_LENGTH); + } + break; + default: + script_pushint(st, 0); + ShowWarning("buildin_setunitname: Unknown object type!\n"); + return false; + } + + script_pushint(st, 1); + clif->blname_ack(0, bl); // Send update to client. + + return true; +} + +static BUILDIN(setunittitle) +{ + struct block_list *bl = map->id2bl(script_getnum(st, 2)); + if (bl == NULL) { + ShowWarning("buildin_setunittitle: Error in finding object with given game ID %d!\n", script_getnum(st, 2)); + return false; + } + + struct unit_data *ud = unit->bl2ud2(bl); + if (ud == NULL) { + ShowWarning("buildin_setunittitle: Error in finding unit_data for given game ID %d!\n", script_getnum(st, 2)); + return false; + } + + safestrncpy(ud->title, script_getstr(st, 3), NAME_LENGTH); + clif->blname_ack(0, bl); // Send update to client. + + return true; +} + +static BUILDIN(getunittitle) +{ + struct block_list *bl = map->id2bl(script_getnum(st, 2)); + if (bl == NULL) { + ShowWarning("buildin_getunitname: Error in finding object with given game ID %d!\n", script_getnum(st, 2)); + script_pushconststr(st, "Unknown"); + return false; + } + + struct unit_data *ud = unit->bl2ud(bl); + if (ud == NULL) { + ShowWarning("buildin_setunittitle: Error in finding unit_data for given game ID %d!\n", script_getnum(st, 2)); + return false; + } + + script_pushstrcopy(st, ud->title); + + return true; +} + /// Makes the unit walk to target position or target id /// Returns if it was successfull /// /// unitwalk(<unit_id>,<x>,<y>) -> <bool> /// unitwalk(<unit_id>,<target_id>) -> <bool> -BUILDIN(unitwalk) { - struct block_list* bl; +static BUILDIN(unitwalk) +{ + struct block_list *bl = map->id2bl(script_getnum(st, 2)); - bl = map->id2bl(script_getnum(st,2)); - if( bl == NULL ) { + if (bl == NULL) { script_pushint(st, 0); return true; } - if( bl->type == BL_NPC ) { - unit->bl2ud2(bl); // ensure the ((struct npc_data*)bl)->ud is safe to edit + if (bl->type == BL_NPC) { + struct unit_data *ud = unit->bl2ud2(bl); // ensure the ((struct npc_data*)bl)->ud is safe to edit + if (ud == NULL) { + ShowWarning("buildin_unitwalk: floating NPC don't have unit data.\n"); + return false; + } } - if( script_hasdata(st,4) ) { - int x = script_getnum(st,3); - int y = script_getnum(st,4); - script_pushint(st, unit->walktoxy(bl,x,y,0));// We'll use harder calculations. - } else { - int target_id = script_getnum(st,3); - script_pushint(st, unit->walktobl(bl,map->id2bl(target_id),1,1)); + if (script_hasdata(st, 4)) { + int x = script_getnum(st, 3); + int y = script_getnum(st, 4); + if (unit->walk_toxy(bl, x, y, 0) == 0) // We'll use harder calculations. + script_pushint(st, 1); + else + script_pushint(st, 0); + } + else { + int target_id = script_getnum(st, 3); + script_pushint(st, unit->walktobl(bl, map->id2bl(target_id), 1, 1)); + } + + return true; +} + +/** + * Checks if a unit is walking. + * + * Returns 1 if unit is walking, 0 if unit is not walking and -1 on error. + * + * @code{.herc} + * unitiswalking({<GID>}); + * @endcode + * + **/ +static BUILDIN(unitiswalking) +{ + int gid = script_hasdata(st, 2) ? script_getnum(st, 2) : st->rid; + struct block_list *bl = map->id2bl(gid); + + if (bl == NULL) { + ShowWarning("buildin_unitiswalking: Error in finding object for GID %d!\n", gid); + script_pushint(st, -1); + return false; + } + + if (unit->bl2ud(bl) == NULL) { + ShowWarning("buildin_unitiswalking: Error in finding unit_data for GID %d!\n", gid); + script_pushint(st, -1); + return false; } + script_pushint(st, unit->is_walking(bl)); + return true; } /// Kills the unit /// /// unitkill <unit_id>; -BUILDIN(unitkill) +static BUILDIN(unitkill) { struct block_list* bl = map->id2bl(script_getnum(st,2)); if( bl != NULL ) @@ -16719,33 +21270,36 @@ BUILDIN(unitkill) /// Returns if it was successfull /// /// unitwarp(<unit_id>,"<map name>",<x>,<y>) -> <bool> -BUILDIN(unitwarp) { - int unit_id; +static BUILDIN(unitwarp) +{ + int unit_id = script_getnum(st, 2); + const char *mapname = script_getstr(st, 3); + short x = (short)script_getnum(st, 4); + short y = (short)script_getnum(st, 5); int mapid; - short x; - short y; - struct block_list* bl; - const char *mapname; - - unit_id = script_getnum(st,2); - mapname = script_getstr(st, 3); - x = (short)script_getnum(st,4); - y = (short)script_getnum(st,5); + struct block_list *bl; if (!unit_id) //Warp the script's runner bl = map->id2bl(st->rid); else bl = map->id2bl(unit_id); - if( strcmp(mapname,"this") == 0 ) - mapid = bl?bl->m:-1; + if (strcmp(mapname, "this") == 0) + mapid = bl ? bl->m : -1; else mapid = map->mapname2mapid(mapname); - if( mapid >= 0 && bl != NULL ) { - unit->bl2ud2(bl); // ensure ((struct npc_data *)bl)->ud is safe to edit - script_pushint(st, unit->warp(bl,mapid,x,y,CLR_OUTSIGHT)); - } else { + if (mapid >= 0 && bl != NULL) { + struct unit_data *ud = unit->bl2ud2(bl); // ensure ((struct npc_data *)bl)->ud is safe to edit + if (bl->type == BL_NPC) { + if (ud == NULL) { + ShowWarning("buildin_unitwarp: floating NPC don't have unit data.\n"); + return false; + } + } + script_pushint(st, unit->warp(bl, mapid, x, y, CLR_OUTSIGHT)); + } + else { script_pushint(st, 0); } @@ -16759,7 +21313,8 @@ BUILDIN(unitwarp) { /// /// unitattack(<unit_id>,"<target name>"{,<action type>}) -> <bool> /// unitattack(<unit_id>,<target_id>{,<action type>}) -> <bool> -BUILDIN(unitattack) { +static BUILDIN(unitattack) +{ struct block_list* unit_bl; struct block_list* target_bl = NULL; int actiontype = 0; @@ -16813,18 +21368,21 @@ BUILDIN(unitattack) { /// Makes the unit stop attacking and moving /// /// unitstop <unit_id>; -BUILDIN(unitstop) { - int unit_id; - struct block_list* bl; - - unit_id = script_getnum(st,2); +static BUILDIN(unitstop) +{ + struct block_list *bl = map->id2bl(script_getnum(st, 2)); - bl = map->id2bl(unit_id); - if( bl != NULL ) { - unit->bl2ud2(bl); // ensure ((struct npc_data *)bl)->ud is safe to edit + if (bl != NULL) { + struct unit_data *ud = unit->bl2ud2(bl); // ensure ((struct npc_data *)bl)->ud is safe to edit + if (bl->type == BL_NPC) { + if (ud == NULL) { + ShowWarning("buildin_unitstop: floating NPC don't have unit data.\n"); + return false; + } + } unit->stop_attack(bl); unit->stop_walking(bl, STOPWALKING_FLAG_NEXTCELL); - if( bl->type == BL_MOB ) + if (bl->type == BL_MOB) BL_UCAST(BL_MOB, bl)->target_id = 0; } @@ -16833,15 +21391,30 @@ BUILDIN(unitstop) { /// Makes the unit say the message /// -/// unittalk <unit_id>,"<message>"; -BUILDIN(unittalk) { +/// unittalk(<unit_id>,"<message>"{, show_name{, <send_target>{, <target_id>}}}); +static BUILDIN(unittalk) +{ int unit_id; const char* message; - struct block_list* bl; + struct block_list *bl, *target_bl = NULL; + bool show_name = true; + enum send_target target = AREA_CHAT_WOC; unit_id = script_getnum(st,2); message = script_getstr(st, 3); + if (script_hasdata(st, 4)) { + show_name = (script_getnum(st, 4) != 0) ? true : false; + } + + if (script_hasdata(st, 5)) { + target = script_getnum(st, 5); + } + + if (script_hasdata(st, 6)) { + target_bl = map->id2bl(script_getnum(st, 6)); + } + bl = map->id2bl(unit_id); if( bl != NULL ) { struct StringBuf sbuf; @@ -16850,8 +21423,17 @@ BUILDIN(unittalk) { safestrncpy(blname, clif->get_bl_name(bl), sizeof(blname)); if(bl->type == BL_NPC) strtok(blname, "#"); - StrBuf->Printf(&sbuf, "%s : %s", blname, message); - clif->disp_overhead(bl, StrBuf->Value(&sbuf)); + if (show_name) { + StrBuf->Printf(&sbuf, "%s : %s", blname, message); + } else { + StrBuf->Printf(&sbuf, "%s", message); + } + + if (bl->type == BL_PC && target == SELF && (target_bl == NULL || bl == target_bl)) { + clif->notify_playerchat(bl, StrBuf->Value(&sbuf)); + } else { + clif->disp_overhead(bl, StrBuf->Value(&sbuf), target, target_bl); + } StrBuf->Destroy(&sbuf); } @@ -16863,7 +21445,8 @@ BUILDIN(unittalk) { /// unitemote <unit_id>,<emotion>; /// /// @see e_* in const.txt -BUILDIN(unitemote) { +static BUILDIN(unitemote) +{ int unit_id; int emotion; struct block_list* bl; @@ -16881,7 +21464,8 @@ BUILDIN(unitemote) { /// /// unitskilluseid <unit_id>,<skill_id>,<skill_lv>{,<target_id>}; /// unitskilluseid <unit_id>,"<skill name>",<skill_lv>{,<target_id>}; -BUILDIN(unitskilluseid) { +static BUILDIN(unitskilluseid) +{ int unit_id; uint16 skill_id; uint16 skill_lv; @@ -16903,7 +21487,10 @@ BUILDIN(unitskilluseid) { } else { status_calc_npc(nd, SCO_NONE); } + } else if (bl->type == BL_PC) { + pc->autocast_clear(BL_UCAST(BL_PC, bl)); } + unit->skilluse_id(bl, target_id, skill_id, skill_lv); } @@ -16914,7 +21501,8 @@ BUILDIN(unitskilluseid) { /// /// unitskillusepos <unit_id>,<skill_id>,<skill_lv>,<target_x>,<target_y>; /// unitskillusepos <unit_id>,"<skill name>",<skill_lv>,<target_x>,<target_y>; -BUILDIN(unitskillusepos) { +static BUILDIN(unitskillusepos) +{ int unit_id; uint16 skill_id; uint16 skill_lv; @@ -16938,7 +21526,10 @@ BUILDIN(unitskillusepos) { } else { status_calc_npc(nd, SCO_NONE); } + } else if (bl->type == BL_PC) { + pc->autocast_clear(BL_UCAST(BL_PC, bl)); } + unit->skilluse_pos(bl, skill_x, skill_y, skill_id, skill_lv); } @@ -16950,7 +21541,7 @@ BUILDIN(unitskillusepos) { /// Pauses the execution of the script, detaching the player /// /// sleep <mili seconds>; -BUILDIN(sleep) +static BUILDIN(sleep) { int ticks; @@ -16979,7 +21570,8 @@ BUILDIN(sleep) /// Returns if a player is still attached /// /// sleep2(<mili secconds>) -> <bool> -BUILDIN(sleep2) { +static BUILDIN(sleep2) +{ int ticks; ticks = script_getnum(st,2); @@ -17003,7 +21595,7 @@ BUILDIN(sleep2) { /// Awakes all the sleep timers of the target npc /// /// awake "<npc name>"; -BUILDIN(awake) +static BUILDIN(awake) { struct DBIterator *iter; struct script_state *tst; @@ -17046,7 +21638,7 @@ BUILDIN(awake) /// Returns 0 if an error occurs. /// /// getvariableofnpc(<variable>, "<npc name>") -> <reference> -BUILDIN(getvariableofnpc) +static BUILDIN(getvariableofnpc) { struct script_data* data; const char* name; @@ -17088,13 +21680,62 @@ BUILDIN(getvariableofnpc) return true; } +static BUILDIN(getvariableofpc) +{ + const char* name; + struct script_data* data = script_getdata(st, 2); + struct map_session_data *sd = map->id2sd(script_getnum(st, 3)); + + if (!data_isreference(data)) { + ShowError("script:getvariableofpc: not a variable\n"); + script->reportdata(data); + script_pushnil(st); + st->state = END; + return false; + } + + name = reference_getname(data); + + switch (*name) + { + case '$': + case '.': + case '\'': + ShowError("script:getvariableofpc: illegal scope (not pc variable)\n"); + script->reportdata(data); + script_pushnil(st); + st->state = END; + return false; + } + + if (sd == NULL) + { + // player not found, return default value + if (script_hasdata(st, 4)) { + script_pushcopy(st, 4); + } else if (is_string_variable(name)) { + script_pushconststr(st, ""); + } else { + script_pushint(st, 0); + } + return true; + } + + if (!sd->regs.vars) + sd->regs.vars = i64db_alloc(DB_OPT_BASE); + + script->push_val(st->stack, C_NAME, reference_getuid(data), &sd->regs); + return true; +} + /// Opens a warp portal. /// Has no "portal opening" effect/sound, it opens the portal immediately. /// /// warpportal <source x>,<source y>,"<target map>",<target x>,<target y>; /// /// @author blackhole89 -BUILDIN(warpportal) { +static BUILDIN(warpportal) +{ int spx; int spy; unsigned short map_index; @@ -17131,7 +21772,7 @@ BUILDIN(warpportal) { return true; } -BUILDIN(openmail) +static BUILDIN(openmail) { struct map_session_data *sd = script->rid2sd(st); if (sd == NULL) @@ -17142,7 +21783,7 @@ BUILDIN(openmail) return true; } -BUILDIN(openauction) +static BUILDIN(openauction) { struct map_session_data *sd = script->rid2sd(st); if (sd == NULL) @@ -17158,7 +21799,8 @@ BUILDIN(openauction) /// checkcell("<map name>",<x>,<y>,<type>) -> <bool> /// /// @see cell_chk* constants in const.txt for the types -BUILDIN(checkcell) { +static BUILDIN(checkcell) +{ int16 m = map->mapname2mapid(script_getstr(st,2)); int16 x = script_getnum(st,3); int16 y = script_getnum(st,4); @@ -17179,7 +21821,8 @@ BUILDIN(checkcell) { /// setcell "<map name>",<x1>,<y1>,<x2>,<y2>,<type>,<flag>; /// /// @see cell_* constants in const.txt for the types -BUILDIN(setcell) { +static BUILDIN(setcell) +{ int16 m = map->mapname2mapid(script_getstr(st,2)); int16 x1 = script_getnum(st,3); int16 y1 = script_getnum(st,4); @@ -17208,7 +21851,7 @@ BUILDIN(setcell) { /*========================================== * Mercenary Commands *------------------------------------------*/ -BUILDIN(mercenary_create) +static BUILDIN(mercenary_create) { struct map_session_data *sd; int class_, contract_time; @@ -17226,7 +21869,8 @@ BUILDIN(mercenary_create) return true; } -BUILDIN(mercenary_heal) { +static BUILDIN(mercenary_heal) +{ struct map_session_data *sd = script->rid2sd(st); int hp, sp; @@ -17235,11 +21879,12 @@ 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; } -BUILDIN(mercenary_sc_start) { +static BUILDIN(mercenary_sc_start) +{ struct map_session_data *sd = script->rid2sd(st); enum sc_type type; int tick, val1; @@ -17255,7 +21900,8 @@ BUILDIN(mercenary_sc_start) { return true; } -BUILDIN(mercenary_get_calls) { +static BUILDIN(mercenary_get_calls) +{ struct map_session_data *sd = script->rid2sd(st); int guild_id; @@ -17281,7 +21927,8 @@ BUILDIN(mercenary_get_calls) { return true; } -BUILDIN(mercenary_set_calls) { +static BUILDIN(mercenary_set_calls) +{ struct map_session_data *sd = script->rid2sd(st); int guild_id, value, *calls; @@ -17311,7 +21958,8 @@ BUILDIN(mercenary_set_calls) { return true; } -BUILDIN(mercenary_get_faith) { +static BUILDIN(mercenary_get_faith) +{ struct map_session_data *sd = script->rid2sd(st); int guild_id; @@ -17337,7 +21985,8 @@ BUILDIN(mercenary_get_faith) { return true; } -BUILDIN(mercenary_set_faith) { +static BUILDIN(mercenary_set_faith) +{ struct map_session_data *sd = script->rid2sd(st); int guild_id, value, *calls; @@ -17372,7 +22021,7 @@ BUILDIN(mercenary_set_faith) { /*------------------------------------------ * Book Reading *------------------------------------------*/ -BUILDIN(readbook) +static BUILDIN(readbook) { struct map_session_data *sd; int book_id, page; @@ -17391,89 +22040,214 @@ BUILDIN(readbook) * Questlog script commands * ****************************/ -BUILDIN(questinfo) +static BUILDIN(questinfo) { struct npc_data *nd = map->id2nd(st->oid); - int quest_id, icon; - struct questinfo qi; + struct questinfo qi = { 0 }; + int icon = script_getnum(st, 2); - if( nd == NULL || nd->bl.m == -1 ) + if (nd == NULL) return true; - quest_id = script_getnum(st, 2); - icon = script_getnum(st, 3); - - #if 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 - - qi.quest_id = quest_id; - qi.icon = (unsigned char)icon; - qi.nd = nd; + if (nd->bl.m == -1) { + ShowWarning("buildin_questinfo: questinfo cannot be set for an npc with no valid map.\n"); + return false; + } - if (script_hasdata(st, 4)) { - int color = script_getnum(st, 4); + qi.icon = quest->questinfo_validate_icon(icon); + if (script_hasdata(st, 3)) { + int color = script_getnum(st, 3); if (color < 0 || color > 3) { - ShowWarning("buildin_questinfo: invalid color '%d', changing to 0\n",color); + ShowWarning("buildin_questinfo: invalid color '%d', defaulting to 0.\n", color); script->reportfunc(st); color = 0; } qi.color = (unsigned char)color; } - qi.hasJob = false; + VECTOR_ENSURE(nd->qi_data, 1, 1); + VECTOR_PUSH(nd->qi_data, qi); + map->add_questinfo(nd->bl.m, nd); + return true; +} - if (script_hasdata(st, 5)) { - int job = script_getnum(st, 5); +static BUILDIN(setquestinfo) +{ + struct npc_data *nd = map->id2nd(st->oid); + struct questinfo *qi = NULL; + uint32 type = script_getnum(st, 2); - if (!pc->db_checkid(job)) { - ShowError("buildin_questinfo: Nonexistant Job Class.\n"); - } else { - qi.hasJob = true; - qi.job = (unsigned short)job; + if (nd == NULL) + return true; + + if (nd->bl.m == -1) { + ShowWarning("buildin_setquestinfo: questinfo cannot be set for an npc with no valid map.\n"); + return false; + } + + if (VECTOR_LENGTH(nd->qi_data) == 0) { + ShowWarning("buildin_setquestinfo: no valide questinfo data has been found for this npc.\n"); + return false; + } + + qi = &VECTOR_LAST(nd->qi_data); + + switch (type) { + case QINFO_JOB: + { + int jobid = script_getnum(st, 3); + if (!pc->db_checkid(jobid)) { + ShowWarning("buildin_setquestinfo: invalid job id given (%d).\n", jobid); + return false; + } + qi->hasJob = true; + qi->job = jobid; + break; + } + case QINFO_SEX: + { + int sex = script_getnum(st, 3); + if (sex != SEX_MALE && sex != SEX_FEMALE) { + ShowWarning("buildin_setquestinfo: unsupported sex has been given (%d).\n", sex); + return false; } + qi->sex_enabled = true; + qi->sex = sex; + break; + } + case QINFO_BASE_LEVEL: + { + int min = script_getnum(st, 3); + int max = script_getnum(st, 4); + if (min > max) { + ShowWarning("buildin_setquestinfo: minimal level (%d) is bigger than the maximal level (%d).\n", min, max); + return false; + } + qi->base_level.min = min; + qi->base_level.max = max; + break; } + case QINFO_JOB_LEVEL: + { + int min = script_getnum(st, 3); + int max = script_getnum(st, 4); + if (min > max) { + ShowWarning("buildin_setquestinfo: minimal level (%d) is bigger than the maximal level (%d).\n", min, max); + return false; + } + qi->job_level.min = min; + qi->job_level.max = max; + break; + } + case QINFO_ITEM: + { + struct questinfo_itemreq item = { 0 }; - map->add_questinfo(nd->bl.m,&qi); + item.nameid = script_getnum(st, 3); + 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.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) + VECTOR_INIT(qi->items); + VECTOR_ENSURE(qi->items, 1, 1); + VECTOR_PUSH(qi->items, item); + break; + } + case QINFO_HOMUN_LEVEL: + { + int min = script_getnum(st, 3); + if (min > battle_config.hom_max_level && min > battle_config.hom_S_max_level) { + ShowWarning("buildin_setquestinfo: minimum homunculus level given (%d) is bigger than the max possible level.\n", min); + return false; + } + qi->homunculus.level = min; + break; + } + case QINFO_HOMUN_TYPE: + { + int hom_type = script_getnum(st, 3); + if (hom_type < HT_REG || hom_type > HT_S) { + ShowWarning("buildin_setquestinfo: invalid homunculus type (%d).\n", hom_type); + return false; + } + qi->homunculus_type = hom_type; + break; + } + case QINFO_QUEST: + { + struct questinfo_qreq quest_req = { 0 }; + struct quest_db *quest_data = NULL; + + quest_req.id = script_getnum(st, 3); + quest_req.state = script_getnum(st, 4); + + quest_data = quest->db(quest_req.id); + if (quest_data == &quest->dummy) { + ShowWarning("buildin_setquestinfo: invalid quest given (%d).\n", quest_req.id); + return false; + } + if (quest_req.state < Q_INACTIVE || quest_req.state > Q_COMPLETE) { + ShowWarning("buildin_setquestinfo: invalid quest state given (%d).\n", quest_req.state); + return false; + } + + if (VECTOR_LENGTH(qi->quest_requirement) == 0) + VECTOR_INIT(qi->quest_requirement); + VECTOR_ENSURE(qi->quest_requirement, 1, 1); + 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; + } return true; } -BUILDIN(setquest) +static BUILDIN(setquest) { - unsigned short i; int quest_id; + unsigned int time_limit; struct map_session_data *sd = script->rid2sd(st); if (sd == NULL) return true; quest_id = script_getnum(st, 2); + time_limit = script_hasdata(st, 3) ? script_getnum(st, 3) : 0; - quest->add(sd, quest_id); - - // If questinfo is set, remove quest bubble once quest is set. - for(i = 0; i < map->list[sd->bl.m].qi_count; i++) { - struct questinfo *qi = &map->list[sd->bl.m].qi_data[i]; - if( qi->quest_id == quest_id ) { -#if PACKETVER >= 20120410 - clif->quest_show_event(sd, &qi->nd->bl, 9999, 0); -#else - clif->quest_show_event(sd, &qi->nd->bl, 0, 0); -#endif - } - } - + quest->add(sd, quest_id, time_limit); return true; } -BUILDIN(erasequest) +static BUILDIN(erasequest) { struct map_session_data *sd = script->rid2sd(st); @@ -17496,7 +22270,7 @@ BUILDIN(erasequest) return true; } -BUILDIN(completequest) +static BUILDIN(completequest) { struct map_session_data *sd = script->rid2sd(st); @@ -17519,7 +22293,7 @@ BUILDIN(completequest) return true; } -BUILDIN(changequest) +static BUILDIN(changequest) { struct map_session_data *sd = script->rid2sd(st); @@ -17530,7 +22304,7 @@ BUILDIN(changequest) return true; } -BUILDIN(questactive) +static BUILDIN(questactive) { struct map_session_data *sd = script->rid2sd(st); int qid, i; @@ -17556,7 +22330,7 @@ BUILDIN(questactive) return true; } -BUILDIN(questprogress) +static BUILDIN(questprogress) { struct map_session_data *sd = script->rid2sd(st); enum quest_check_type type = HAVEQUEST; @@ -17584,7 +22358,7 @@ BUILDIN(questprogress) return true; } -BUILDIN(showevent) +static BUILDIN(showevent) { struct map_session_data *sd = script->rid2sd(st); struct npc_data *nd = map->id2nd(st->oid); @@ -17603,24 +22377,106 @@ BUILDIN(showevent) } } - #if 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; +} + +static BUILDIN(achievement_iscompleted) +{ + struct map_session_data *sd = script_hasdata(st, 3) ? map->id2sd(script_getnum(st, 3)) : script->rid2sd(st); + if (sd == NULL) + return false; + + int aid = script_getnum(st, 2); + const struct achievement_data *ad = achievement->get(aid); + if (ad == NULL) { + ShowError("buildin_achievement_iscompleted: Invalid Achievement %d provided.\n", aid); + return false; + } + + script_pushint(st, achievement->check_complete(sd, ad)); + return true; +} + +/*========================================== * BattleGround System *------------------------------------------*/ -BUILDIN(waitingroom2bg) { +static BUILDIN(waitingroom2bg) +{ struct npc_data *nd; struct chat_data *cd; const char *map_name, *ev = "", *dev = ""; @@ -17664,17 +22520,18 @@ 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; } -BUILDIN(waitingroom2bg_single) { +static BUILDIN(waitingroom2bg_single) +{ const char* map_name; struct npc_data *nd; struct chat_data *cd; @@ -17707,7 +22564,7 @@ BUILDIN(waitingroom2bg_single) { return true; } -BUILDIN(bg_team_setxy) +static BUILDIN(bg_team_setxy) { struct battleground_data *bgd; int bg_id; @@ -17721,7 +22578,7 @@ BUILDIN(bg_team_setxy) return true; } -BUILDIN(bg_warp) +static BUILDIN(bg_warp) { int x, y, map_index, bg_id; const char* map_name; @@ -17736,24 +22593,35 @@ BUILDIN(bg_warp) return true; } -BUILDIN(bg_monster) +/** + * Spawns a mob with allegiance to the given battle group. + * + * @code{.herc} + * bg_monster(<battle group>, "<map name>", <x>, <y>, "<name to show>", <mob id>{, "<event label>"}); + * @endcode + * + **/ +static BUILDIN(bg_monster) { - int class_ = 0, x = 0, y = 0, bg_id = 0; - const char *str, *mapname, *evt=""; + const char *event = ""; + + if (script_hasdata(st, 8)) { + event = script_getstr(st, 8); + script->check_event(st, event); + } - bg_id = script_getnum(st,2); - mapname = script_getstr(st,3); - x = script_getnum(st,4); - y = script_getnum(st,5); - str = script_getstr(st,6); - class_ = script_getnum(st,7); - if( script_hasdata(st,8) ) evt = script_getstr(st,8); - script->check_event(st, evt); - script_pushint(st, mob->spawn_bg(mapname,x,y,str,class_,evt,bg_id)); + const char *mapname = script_getstr(st, 3); + const char *name = script_getstr(st, 6); + const int bg_id = script_getnum(st, 2); + const int x = script_getnum(st, 4); + const int y = script_getnum(st, 5); + const int mob_id = script_getnum(st, 7); + + script_pushint(st, mob->spawn_bg(mapname, x, y, name, mob_id, event, bg_id, st->oid)); return true; } -BUILDIN(bg_monster_set_team) +static BUILDIN(bg_monster_set_team) { int id = script_getnum(st,2), bg_id = script_getnum(st,3); @@ -17767,12 +22635,12 @@ BUILDIN(bg_monster_set_team) mob_stop_attack(md); mob_stop_walking(md, STOPWALKING_FLAG_NONE); md->target_id = md->attacked_id = 0; - clif->charnameack(0, &md->bl); + clif->blname_ack(0, &md->bl); return true; } -BUILDIN(bg_leave) +static BUILDIN(bg_leave) { struct map_session_data *sd = script->rid2sd(st); if( sd == NULL || !sd->bg_id ) @@ -17782,14 +22650,14 @@ BUILDIN(bg_leave) return true; } -BUILDIN(bg_destroy) +static BUILDIN(bg_destroy) { int bg_id = script_getnum(st,2); bg->team_delete(bg_id); return true; } -BUILDIN(bg_getareausers) +static BUILDIN(bg_getareausers) { const char *str; int16 m, x0, y0, x1, y1; @@ -17823,7 +22691,8 @@ BUILDIN(bg_getareausers) return true; } -BUILDIN(bg_updatescore) { +static BUILDIN(bg_updatescore) +{ const char *str; int16 m; @@ -17838,7 +22707,7 @@ BUILDIN(bg_updatescore) { return true; } -BUILDIN(bg_get_data) +static BUILDIN(bg_get_data) { struct battleground_data *bgd; int bg_id = script_getnum(st,2), @@ -17865,10 +22734,12 @@ BUILDIN(bg_get_data) * Instancing Script Commands *------------------------------------------*/ -BUILDIN(instance_create) { +static BUILDIN(instance_create) +{ const char *name; int owner_id, res; int type = IOT_PARTY; + struct map_session_data *sd = map->id2sd(st->rid); name = script_getstr(st, 2); owner_id = script_getnum(st, 3); @@ -17881,27 +22752,49 @@ BUILDIN(instance_create) { } res = instance->create(owner_id, name, (enum instance_owner_type) type); - if( res == -4 ) { // Already exists - script_pushint(st, -1); - return true; - } else if( res < 0 ) { + if (sd != NULL) { + switch (res) { + case -4: // Already exists + clif->msgtable_str(sd, MSG_MDUNGEON_SUBSCRIPTION_ERROR_DUPLICATE, name); + break; + case -3: // No free instances + clif->msgtable_str(sd, MSG_MDUNGEON_SUBSCRIPTION_ERROR_EXIST, name); + break; + case -2: // Invalid type + clif->msgtable_str(sd, MSG_MDUNGEON_SUBSCRIPTION_ERROR_RIGHT, name); + break; + case -1: // Unknown + clif->msgtable_str(sd, MSG_MDUNGEON_SUBSCRIPTION_ERROR_UNKNOWN, name); + break; + default: + if (res < 0) + ShowError("buildin_instance_create: failed to unknown reason [%d].\n", res); + } + } else { const char *err; - switch(res) { - case -3: err = "No free instances"; break; - case -2: err = "Invalid party ID"; break; - case -1: err = "Invalid type"; break; - default: err = "Unknown"; break; + switch (res) { + case -3: + err = "No free instances"; + break; + case -2: + err = "Invalid party ID"; + break; + case -1: + err = "Invalid type"; + break; + default: + err = "Unknown"; + break; } - ShowError("buildin_instance_create: %s [%d].\n", err, res); - script_pushint(st, -2); - return true; + if (res < 0) + ShowError("buildin_instance_create: %s [%d].\n", err, res); } - script_pushint(st, res); return true; } -BUILDIN(instance_destroy) { +static BUILDIN(instance_destroy) +{ int instance_id = -1; if( script_hasdata(st, 2) ) @@ -17919,7 +22812,7 @@ BUILDIN(instance_destroy) { return true; } -BUILDIN(instance_attachmap) +static BUILDIN(instance_attachmap) { const char *map_name = NULL; int16 m; @@ -17943,7 +22836,8 @@ BUILDIN(instance_attachmap) return true; } -BUILDIN(instance_detachmap) { +static BUILDIN(instance_detachmap) +{ const char *str; int16 m; int instance_id = -1; @@ -17964,7 +22858,7 @@ BUILDIN(instance_detachmap) { return true; } -BUILDIN(instance_attach) +static BUILDIN(instance_attach) { int instance_id = script_getnum(st, 2); @@ -17975,12 +22869,13 @@ BUILDIN(instance_attach) return true; } -BUILDIN(instance_id) { +static BUILDIN(instance_id) +{ script_pushint(st, st->instance_id); return true; } -BUILDIN(instance_set_timeout) +static BUILDIN(instance_set_timeout) { int progress_timeout, idle_timeout; int instance_id = -1; @@ -18000,7 +22895,8 @@ BUILDIN(instance_set_timeout) return true; } -BUILDIN(instance_init) { +static BUILDIN(instance_init) +{ int instance_id = script_getnum(st, 2); if( !instance->valid(instance_id) ) { @@ -18017,7 +22913,8 @@ BUILDIN(instance_init) { return true; } -BUILDIN(instance_announce) { +static BUILDIN(instance_announce) +{ int instance_id = script_getnum(st,2); const char *mes = script_getstr(st,3); int flag = script_getnum(st,4); @@ -18026,8 +22923,9 @@ BUILDIN(instance_announce) { int fontSize = script_hasdata(st,7) ? script_getnum(st,7) : 12; // default fontSize int fontAlign = script_hasdata(st,8) ? script_getnum(st,8) : 0; // default fontAlign int fontY = script_hasdata(st,9) ? script_getnum(st,9) : 0; // default fontY - int i; + size_t len = strlen(mes); + Assert_retr(false, len < INT_MAX); if( instance_id == -1 ) { if( st->instance_id >= 0 ) @@ -18041,12 +22939,13 @@ BUILDIN(instance_announce) { for( i = 0; i < instance->list[instance_id].num_map; i++ ) map->foreachinmap(script->buildin_announce_sub, instance->list[instance_id].map[i], BL_PC, - mes, strlen(mes)+1, flag&BC_COLOR_MASK, fontColor, fontType, fontSize, fontAlign, fontY); + mes, (int)len+1, flag&BC_COLOR_MASK, fontColor, fontType, fontSize, fontAlign, fontY); return true; } -BUILDIN(instance_npcname) { +static BUILDIN(instance_npcname) +{ const char *str; int instance_id = -1; struct npc_data *nd; @@ -18070,81 +22969,100 @@ BUILDIN(instance_npcname) { return true; } -BUILDIN(has_instance) { +static BUILDIN(has_instance) +{ struct map_session_data *sd; const char *str; int16 m; int instance_id = -1; + int i = 0, j = 0; bool type = strcmp(script->getfuncname(st),"has_instance2") == 0 ? true : false; str = script_getstr(st, 2); - if( (m = map->mapname2mapid(str)) < 0 ) { - if( type ) + if ((m = map->mapname2mapid(str)) < 0) { + if (type) { script_pushint(st, -1); - else + } else { script_pushconststr(st, ""); + } return true; } - if( script_hasdata(st, 3) ) + if (script_hasdata(st, 3)) instance_id = script_getnum(st, 3); - else if( st->instance_id >= 0 ) + else if (st->instance_id >= 0) instance_id = st->instance_id; - else if( (sd = script->rid2sd(st)) != NULL ) { + else if ((sd = script->rid2sd(st)) != NULL) { struct party_data *p; - int i = 0, j = 0; - if( sd->instances ) { - for( i = 0; i < sd->instances; i++ ) { - if( sd->instance[i] >= 0 ) { + if (sd->instances) { + for (i = 0; i < sd->instances; i++) { + if (sd->instance[i] >= 0) { ARR_FIND(0, instance->list[sd->instance[i]].num_map, j, map->list[instance->list[sd->instance[i]].map[j]].instance_src_map == m); - if( j != instance->list[sd->instance[i]].num_map ) + if (j != instance->list[sd->instance[i]].num_map) break; } } - if( i != sd->instances ) + if (i != sd->instances) { instance_id = sd->instance[i]; + } } if (instance_id == -1 && sd->status.party_id && (p = party->search(sd->status.party_id)) != NULL && p->instances) { - for( i = 0; i < p->instances; i++ ) { - if( p->instance[i] >= 0 ) { + for (i = 0; i < p->instances; i++) { + if (p->instance[i] >= 0) { ARR_FIND(0, instance->list[p->instance[i]].num_map, j, map->list[instance->list[p->instance[i]].map[j]].instance_src_map == m); - if( j != instance->list[p->instance[i]].num_map ) + if (j != instance->list[p->instance[i]].num_map) break; } } - if( i != p->instances ) + if (i != p->instances) { instance_id = p->instance[i]; + } } - if( instance_id == -1 && sd->guild && sd->guild->instances ) { - for( i = 0; i < sd->guild->instances; i++ ) { - if( sd->guild->instance[i] >= 0 ) { + if (instance_id == -1 && sd->guild && sd->guild->instances) { + for (i = 0; i < sd->guild->instances; i++) { + if (sd->guild->instance[i] >= 0) { ARR_FIND(0, instance->list[sd->guild->instance[i]].num_map, j, map->list[instance->list[sd->guild->instance[i]].map[j]].instance_src_map == m); - if( j != instance->list[sd->guild->instance[i]].num_map ) + if (j != instance->list[sd->guild->instance[i]].num_map) break; } } - if( i != sd->guild->instances ) + if (i != sd->guild->instances) instance_id = sd->guild->instance[i]; } } - if( !instance->valid(instance_id) || (m = instance->map2imap(m, instance_id)) < 0 ) { - if( type ) + if (instance_id == -1) { + for (i = 0; i < instance->instances; i++) { + if (instance->list[i].state != INSTANCE_FREE && instance->list[i].owner_type == IOT_NONE && instance->list[i].num_map > 0) { + ARR_FIND(0, instance->list[i].num_map, j, map->list[instance->list[i].map[j]].instance_src_map == m); + if (j != instance->list[i].num_map) + break; + } + } + if (i != instance->instances) { + instance_id = instance->list[i].id; + } + } + + if (!instance->valid(instance_id) || (m = instance->map2imap(m, instance_id)) < 0) { + if (type) { script_pushint(st, -1); - else + } else { script_pushconststr(st, ""); + } return true; } - if( type ) + if (type) { script_pushint(st, instance_id); - else + } else { script_pushconststr(st, map->list[m].name); + } return true; } -int buildin_instance_warpall_sub(struct block_list *bl, va_list ap) +static int buildin_instance_warpall_sub(struct block_list *bl, va_list ap) { struct map_session_data *sd = NULL; int map_index = va_arg(ap,int); @@ -18159,7 +23077,9 @@ int buildin_instance_warpall_sub(struct block_list *bl, va_list ap) return 0; } -BUILDIN(instance_warpall) { + +static BUILDIN(instance_warpall) +{ int16 m; int instance_id = -1; const char *mapn; @@ -18197,7 +23117,7 @@ BUILDIN(instance_warpall) { * Example: instance_check_party (getcharid(1){,amount}{,min}{,max}); * Example 2: instance_check_party (getcharid(1),1,1,99); *------------------------------------------*/ -BUILDIN(instance_check_party) +static BUILDIN(instance_check_party) { int amount, min, max, i, party_id, c = 0; struct party_data *p = NULL; @@ -18256,7 +23176,7 @@ BUILDIN(instance_check_party) * Example: instance_check_guild (getcharid(2){,amount}{,min}{,max}); * Example 2: instance_check_guild (getcharid(2),1,1,99); *------------------------------------------*/ -BUILDIN(instance_check_guild) +static BUILDIN(instance_check_guild) { int amount, min, max, i, guild_id, c = 0; struct guild *g = NULL; @@ -18308,7 +23228,7 @@ BUILDIN(instance_check_guild) /*========================================== * Custom Fonts *------------------------------------------*/ -BUILDIN(setfont) +static BUILDIN(setfont) { struct map_session_data *sd = script->rid2sd(st); int font = script_getnum(st,2); @@ -18324,7 +23244,20 @@ BUILDIN(setfont) return true; } -int buildin_mobuseskill_sub(struct block_list *bl, va_list ap) +static BUILDIN(getfont) +{ + struct map_session_data *sd = script->rid2sd(st); + + if (sd == NULL) { + script_pushint(st, 0); + return true; + } + + script_pushint(st, sd->status.font); + return true; +} + +static int buildin_mobuseskill_sub(struct block_list *bl, va_list ap) { struct mob_data *md = NULL; struct block_list *tbl; @@ -18367,10 +23300,12 @@ int buildin_mobuseskill_sub(struct block_list *bl, va_list ap) return 0; } + /*========================================== * areamobuseskill "Map Name",<x>,<y>,<range>,<Mob ID>,"Skill Name"/<Skill ID>,<Skill Lv>,<Cast Time>,<Cancelable>,<Emotion>,<Target Type>; *------------------------------------------*/ -BUILDIN(areamobuseskill) { +static BUILDIN(areamobuseskill) +{ struct block_list center; int16 m; int range,mobid,skill_id,skill_lv,casttime,emotion,target,cancel; @@ -18399,7 +23334,7 @@ BUILDIN(areamobuseskill) { return true; } -BUILDIN(progressbar) +static BUILDIN(progressbar) { struct map_session_data * sd = script->rid2sd(st); const char * color; @@ -18420,10 +23355,31 @@ BUILDIN(progressbar) clif->progressbar(sd, (unsigned int)strtoul(color, (char **)NULL, 0), second); return true; } +static BUILDIN(progressbar_unit) +{ + const char *color = script_getstr(st, 2); + uint32 second = script_getnum(st, 3); + + if (script_hasdata(st, 4)) { + struct block_list *bl = map->id2bl(script_getnum(st, 4)); -BUILDIN(pushpc) + if (bl == NULL) { + ShowWarning("buildin_progressbar_unit: Error in finding object with given GID %d!\n", script_getnum(st, 4)); + return true; + } + clif->progressbar_unit(bl, (unsigned int)strtoul(color, (char **)NULL, 0), second); + } else { + struct map_session_data *sd = script->rid2sd(st); + + if (sd == NULL) + return false; + + clif->progressbar_unit(&sd->bl, (unsigned int)strtoul(color, (char **)NULL, 0), second); + } + return true; +} +static BUILDIN(pushpc) { - uint8 dir; int cells, dx, dy; struct map_session_data* sd; @@ -18432,15 +23388,14 @@ BUILDIN(pushpc) return true; } - dir = script_getnum(st,2); - cells = script_getnum(st,3); + enum unit_dir dir = script_getnum(st, 2); + cells = script_getnum(st,3); - if(dir>7) - { + if (dir >= UNIT_DIR_MAX) { ShowWarning("buildin_pushpc: Invalid direction %d specified.\n", dir); script->reportsrc(st); - dir%= 8; // trim spin-over + dir %= UNIT_DIR_MAX; // trim spin-over } if(!cells) @@ -18449,10 +23404,11 @@ BUILDIN(pushpc) } else if(cells<0) {// pushing backwards - dir = (dir+4)%8; // turn around - cells = -cells; + dir = unit_get_opposite_dir(dir); + cells = -cells; } + Assert_retr(false, dir >= UNIT_DIR_FIRST && dir < UNIT_DIR_MAX); dx = dirx[dir]; dy = diry[dir]; @@ -18462,7 +23418,7 @@ BUILDIN(pushpc) /// Invokes buying store preparation window /// buyingstore <slots>; -BUILDIN(buyingstore) +static BUILDIN(buyingstore) { struct map_session_data* sd; @@ -18476,7 +23432,7 @@ BUILDIN(buyingstore) /// Invokes search store info window /// searchstores <uses>,<effect>; -BUILDIN(searchstores) +static BUILDIN(searchstores) { unsigned short effect; unsigned int uses; @@ -18507,7 +23463,7 @@ BUILDIN(searchstores) } /// Displays a number as large digital clock. /// showdigit <value>[,<type>]; -BUILDIN(showdigit) +static BUILDIN(showdigit) { unsigned int type = 0; int value; @@ -18537,7 +23493,7 @@ BUILDIN(showdigit) /** * Rune Knight **/ -BUILDIN(makerune) +static BUILDIN(makerune) { struct map_session_data *sd = script->rid2sd(st); if (sd == NULL) @@ -18550,15 +23506,19 @@ BUILDIN(makerune) /** * hascashmount() returns 1 if mounting a cash mount or 0 otherwise **/ -BUILDIN(hascashmount) +static BUILDIN(hascashmount) { struct map_session_data *sd = script->rid2sd(st); + if (sd == NULL) return true; - if( sd->sc.data[SC_ALL_RIDING] ) - script_pushint(st,1); - else - script_pushint(st,0); + + if (sd->sc.data[SC_ALL_RIDING]) { + script_pushint(st, 1); + } else { + script_pushint(st, 0); + } + return true; } @@ -18569,21 +23529,27 @@ BUILDIN(hascashmount) * - Will fail if the player is already riding a standard mount e.g. dragon, peco, wug, mado, etc. * - Will unmount the player is he is already mounting a cash mount **/ -BUILDIN(setcashmount) +static BUILDIN(setcashmount) { struct map_session_data *sd = script->rid2sd(st); + if (sd == NULL) return true; + if (pc_hasmount(sd)) { - clif->msgtable(sd, MSG_REINS_CANT_USE_MOUNTED); - script_pushint(st,0);//can't mount with one of these +#if PACKETVER >= 20110531 + clif->msgtable(sd, MSG_FAIELD_RIDING_OVERLAPPED); +#endif + script_pushint(st, 0); // Can't mount with one of these } else { - if (sd->sc.data[SC_ALL_RIDING]) + if (sd->sc.data[SC_ALL_RIDING]) { status_change_end(&sd->bl, SC_ALL_RIDING, INVALID_TIMER); - else - sc_start(NULL, &sd->bl, SC_ALL_RIDING, 100, 25, INFINITE_DURATION); - script_pushint(st,1);//in both cases, return 1. + } else { + sc_start(NULL, &sd->bl, SC_ALL_RIDING, 100, battle_config.boarding_halter_speed, INFINITE_DURATION); + } + script_pushint(st, 1); // In both cases, return 1. } + return true; } @@ -18591,7 +23557,8 @@ BUILDIN(setcashmount) * Retrieves quantity of arguments provided to callfunc/callsub. * getargcount() -> amount of arguments received in a function **/ -BUILDIN(getargcount) { +static BUILDIN(getargcount) +{ struct script_retinfo* ri; if( st->stack->defsp < 1 || st->stack->stack_data[st->stack->defsp - 1].type != C_RETINFO ) { @@ -18605,16 +23572,18 @@ BUILDIN(getargcount) { return true; } + /** * getcharip(<account ID>/<character ID>/<character name>) **/ -BUILDIN(getcharip) { +static BUILDIN(getcharip) +{ struct map_session_data* sd = NULL; /* check if a character name is specified */ if (script_hasdata(st, 2)) { if (script_isstringtype(st, 2)) { - sd = map->nick2sd(script_getstr(st, 2)); + sd = map->nick2sd(script_getstr(st, 2), false); } else { int id = script_getnum(st, 2); sd = (map->id2sd(id) ? map->id2sd(id) : map->charid2sd(id)); @@ -18631,45 +23600,71 @@ BUILDIN(getcharip) { return false; } - /* check for IP */ - if (!sockt->session[sd->fd]->client_addr) { + if (sd->fd <= 0 || sockt->session[sd->fd] == NULL || sockt->session[sd->fd]->client_addr == 0) { script_pushconststr(st, ""); - return true; - } - - /* return the client ip_addr converted for output */ - if (sd && sd->fd && sockt->session[sd->fd]) - { - /* initiliaze */ - const char *ip_addr = NULL; - uint32 ip; - - /* set ip, ip_addr and convert to ip and push str */ - ip = sockt->session[sd->fd]->client_addr; - ip_addr = sockt->ip2str(ip, NULL); + } else { + uint32 ip = sockt->session[sd->fd]->client_addr; + const char *ip_addr = sockt->ip2str(ip, NULL); script_pushstrcopy(st, ip_addr); } 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>) **/ -BUILDIN(is_function) { - const char* str = script_getstr(st,2); +static BUILDIN(is_function) +{ + 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; } + /** * freeloop(<toggle>) -> toggles this script instance's looping-check ability **/ -BUILDIN(freeloop) { - +static BUILDIN(freeloop) +{ if( script_getnum(st,2) ) st->freeloop = 1; else @@ -18680,7 +23675,8 @@ BUILDIN(freeloop) { return true; } -BUILDIN(sit) { +static BUILDIN(sit) +{ struct map_session_data *sd = NULL; if (script_hasdata(st, 2)) @@ -18700,7 +23696,8 @@ BUILDIN(sit) { return true; } -BUILDIN(stand) { +static BUILDIN(stand) +{ struct map_session_data *sd = NULL; if (script_hasdata(st, 2)) @@ -18720,7 +23717,8 @@ BUILDIN(stand) { return true; } -BUILDIN(issit) { +static BUILDIN(issit) +{ struct map_session_data *sd = NULL; if (script_hasdata(st, 2)) @@ -18738,10 +23736,48 @@ BUILDIN(issit) { return true; } +static BUILDIN(add_group_command) +{ + AtCommandInfo *acmd_d; + struct atcmd_binding_data *bcmd_d; + GroupSettings *group; + int group_index; + const char *atcmd = script_getstr(st, 2); + int group_id = script_getnum(st, 3); + bool self_perm = (script_getnum(st, 4) == 1); + bool char_perm = (script_getnum(st, 5) == 1); + + if (!pcg->exists(group_id)) { + ShowWarning("script:add_group_command: group does not exist: %i\n", group_id); + script_pushint(st, 0); + return false; + } + + group = pcg->id2group(group_id); + group_index = pcg->get_idx(group); + + if ((bcmd_d = atcommand->get_bind_byname(atcmd)) != NULL) { + bcmd_d->at_groups[group_index] = self_perm; + bcmd_d->char_groups[group_index] = char_perm; + script_pushint(st, 1); + return true; + } else if ((acmd_d = atcommand->get_info_byname(atcmd)) != NULL) { + acmd_d->at_groups[group_index] = self_perm; + acmd_d->char_groups[group_index] = char_perm; + script_pushint(st, 1); + return true; + } + + ShowWarning("script:add_group_command: command does not exist: %s\n", atcmd); + script_pushint(st, 0); + return false; +} + /** * @commands (script based) **/ -BUILDIN(bindatcmd) { +static BUILDIN(bindatcmd) +{ const char* atcmd; const char* eventName; int i, group_lv = 0, group_lv_char = 99; @@ -18786,12 +23822,15 @@ BUILDIN(bindatcmd) { atcommand->binding[i]->group_lv = group_lv; atcommand->binding[i]->group_lv_char = group_lv_char; atcommand->binding[i]->log = log; + CREATE(atcommand->binding[i]->at_groups, char, db_size(pcg->db)); + CREATE(atcommand->binding[i]->char_groups, char, db_size(pcg->db)); } return true; } -BUILDIN(unbindatcmd) { +static BUILDIN(unbindatcmd) +{ const char* atcmd; int i = 0; @@ -18808,6 +23847,8 @@ BUILDIN(unbindatcmd) { ARR_FIND(0, atcommand->binding_count, i, strcmp(atcommand->binding[i]->command, atcmd) == 0); if( i < atcommand->binding_count ) { int cursor = 0; + aFree(atcommand->binding[i]->at_groups); + aFree(atcommand->binding[i]->char_groups); aFree(atcommand->binding[i]); atcommand->binding[i] = NULL; /* compact the list now that we freed a slot somewhere */ @@ -18832,7 +23873,8 @@ BUILDIN(unbindatcmd) { return true; } -BUILDIN(useatcmd) { +static BUILDIN(useatcmd) +{ struct map_session_data *sd, *dummy_sd = NULL; int fd; const char* cmd; @@ -18869,70 +23911,70 @@ BUILDIN(useatcmd) { return true; } -BUILDIN(checkre) +static BUILDIN(has_permission) { - int num; + struct map_session_data *sd; + enum e_pc_permission perm; - num=script_getnum(st,2); - switch(num) { - case 0: -#ifdef RENEWAL - script_pushint(st, 1); -#else - script_pushint(st, 0); -#endif - break; - case 1: -#ifdef RENEWAL_CAST - script_pushint(st, 1); -#else - script_pushint(st, 0); -#endif - break; - case 2: -#ifdef RENEWAL_DROP - script_pushint(st, 1); -#else - script_pushint(st, 0); -#endif - break; - case 3: -#ifdef RENEWAL_EXP - script_pushint(st, 1); -#else - script_pushint(st, 0); -#endif - break; - case 4: -#ifdef RENEWAL_LVDMG - script_pushint(st, 1); -#else - script_pushint(st, 0); -#endif - break; - case 5: -#ifdef RENEWAL_EDP - script_pushint(st, 1); -#else - script_pushint(st, 0); -#endif - break; - case 6: -#ifdef RENEWAL_ASPD - script_pushint(st, 1); -#else + if (script_hasdata(st, 3)) { + sd = map->id2sd(script_getnum(st, 3)); + } else { + sd = script->rid2sd(st); + } + + if (sd == NULL) { + script_pushint(st, 0); + return false; + } + + if (script_isstringtype(st, 2)) { + // to check for plugin permissions + int i = 0, j = -1; + const char *name = script_getstr(st, 2); + for (; i < pcg->permission_count; ++i) { + if (strcmp(pcg->permissions[i].name, name) == 0) { + j = i; + break; + } + } + if (j < 0) { + ShowError("script:has_permission: unknown permission: %s\n", name); script_pushint(st, 0); -#endif - break; - default: - ShowWarning("buildin_checkre: unknown parameter.\n"); - break; + return false; + } + script_pushint(st, pc_has_permission(sd, pcg->permissions[j].permission)); + return true; } + + // to ckeck for built-in permission + perm = script_getnum(st, 2); + script_pushint(st, pc_has_permission(sd, perm)); + return true; +} + +static BUILDIN(can_use_command) +{ + struct map_session_data *sd; + const char *cmd = script_getstr(st, 2); + + if (script_hasdata(st, 3)) { + sd = map->id2sd(script_getnum(st, 3)); + } else { + sd = script->rid2sd(st); + } + + if (sd == NULL) { + script_pushint(st, 0); + return false; + } + + script_pushint(st, pc->can_use_command(sd, cmd)); return true; } /* getrandgroupitem <container_item_id>,<quantity> */ -BUILDIN(getrandgroupitem) { +static BUILDIN(getrandgroupitem) +{ struct item_data *data = NULL; struct map_session_data *sd = NULL; int nameid = script_getnum(st, 2); @@ -18972,7 +24014,7 @@ BUILDIN(getrandgroupitem) { if ((flag = pc->additem(sd, &it, get_count, LOG_TYPE_SCRIPT))) { clif->additem(sd, 0, 0, flag); if( pc->candrop(sd,&it) ) - map->addflooritem(&sd->bl, &it, get_count, sd->bl.m, sd->bl.x, sd->bl.y, 0, 0, 0, 0); + map->addflooritem(&sd->bl, &it, get_count, sd->bl.m, sd->bl.x, sd->bl.y, 0, 0, 0, 0, false); } } } @@ -18985,14 +24027,14 @@ BUILDIN(getrandgroupitem) { /* cleanmap <map_name>; * cleanarea <map_name>, <x0>, <y0>, <x1>, <y1>; */ -int script_cleanfloor_sub(struct block_list *bl, va_list ap) { - nullpo_ret(bl); +static int script_cleanfloor_sub(struct block_list *bl, va_list ap) +{ map->clearflooritem(bl); return 0; } -BUILDIN(cleanmap) +static BUILDIN(cleanmap) { const char *mapname = script_getstr(st, 2); int16 m = map->mapname2mapid(mapname); @@ -19017,10 +24059,11 @@ BUILDIN(cleanmap) return true; } + /* Cast a skill on the attached player. * npcskill <skill id>, <skill lvl>, <stat point>, <NPC level>; * npcskill "<skill name>", <skill lvl>, <stat point>, <NPC level>; */ -BUILDIN(npcskill) +static BUILDIN(npcskill) { struct npc_data *nd; uint16 skill_id = script_isstringtype(st, 2) ? skill->name2id(script_getstr(st, 2)) : script_getnum(st, 2); @@ -19066,7 +24109,8 @@ BUILDIN(npcskill) /* Turns a player into a monster and grants SC attribute effect. [malufett/Hercules] * montransform <monster name/id>, <duration>, <sc type>, <val1>, <val2>, <val3>, <val4>; */ -BUILDIN(montransform) { +static BUILDIN(montransform) +{ int tick; enum sc_type type; struct block_list* bl; @@ -19149,7 +24193,7 @@ BUILDIN(montransform) { * * @return The queue, or NULL if it doesn't exist. */ -struct script_queue *script_hqueue_get(int idx) +static struct script_queue *script_hqueue_get(int idx) { if (idx < 0 || idx >= VECTOR_LENGTH(script->hq) || !VECTOR_INDEX(script->hq, idx).valid) return NULL; @@ -19161,7 +24205,7 @@ struct script_queue *script_hqueue_get(int idx) * * @return The index of the created queue. */ -int script_hqueue_create(void) +static int script_hqueue_create(void) { struct script_queue *queue = NULL; int i; @@ -19188,7 +24232,7 @@ int script_hqueue_create(void) * .@queue_id = queue(); * @endcode */ -BUILDIN(queue) +static BUILDIN(queue) { script_pushint(st,script->queue_create()); return true; @@ -19203,7 +24247,7 @@ BUILDIN(queue) * .@size = queuesize(<queue id>); * \endcode */ -BUILDIN(queuesize) +static BUILDIN(queuesize) { int idx = script_getnum(st, 2); @@ -19225,7 +24269,7 @@ BUILDIN(queuesize) * @retval false if the queue is invalid or the entry is already in the queue. * @retval true in case of success. */ -bool script_hqueue_add(int idx, int var) +static bool script_hqueue_add(int idx, int var) { int i; struct map_session_data *sd = NULL; @@ -19263,7 +24307,7 @@ bool script_hqueue_add(int idx, int var) * .@size = queuesize(.@queue_id); * @endcode */ -BUILDIN(queueadd) +static BUILDIN(queueadd) { int idx = script_getnum(st, 2); int var = script_getnum(st, 3); @@ -19284,7 +24328,7 @@ BUILDIN(queueadd) * @retval true if the entry was removed. * @retval false if the entry wasn't in queue. */ -bool script_hqueue_remove(int idx, int var) +static bool script_hqueue_remove(int idx, int var) { int i; struct map_session_data *sd = NULL; @@ -19321,7 +24365,7 @@ bool script_hqueue_remove(int idx, int var) * queueremove(.@queue_id, .@value); * @endcode */ -BUILDIN(queueremove) +static BUILDIN(queueremove) { int idx = script_getnum(st, 2); int var = script_getnum(st, 3); @@ -19337,7 +24381,7 @@ BUILDIN(queueremove) /** * Script command queueopt: Modifies the options of a queue. * - * When the option value isn't provided, the option is removed. + * When the <event label> isn't provided, the option is removed. * * Returns 1 (true) on success, 0 (false) on failure. * @@ -19347,13 +24391,13 @@ BUILDIN(queueremove) * - QUEUEOPT_MAPCHANGE * * When the QUEUEOPT_MAPCHANGE event is triggered, it sets a temporary - * character variable \c @Queue_Destination_Map$ with the destination map name. + * character variable @Queue_Destination_Map$ with the destination map name. * * @code{.herc} - * queueopt(.@queue_id, optionType, <optional val>); + * queueopt(.@queue_id, optionType{, <event label>}); * @endcode */ -BUILDIN(queueopt) +static BUILDIN(queueopt) { int idx = script_getnum(st, 2); int var = script_getnum(st, 3); @@ -19403,7 +24447,7 @@ BUILDIN(queueopt) * @retval true if the queue was correctly deleted. * @retval false if the queue didn't exist. */ -bool script_hqueue_del(int idx) +static bool script_hqueue_del(int idx) { if (!script->queue_clear(idx)) return false; @@ -19422,7 +24466,7 @@ bool script_hqueue_del(int idx) * queuedel(.@queue_id); * @endcode */ -BUILDIN(queuedel) +static BUILDIN(queuedel) { int idx = script_getnum(st, 2); @@ -19442,7 +24486,7 @@ BUILDIN(queuedel) * @retval true if the queue was correctly cleared. * @retval false if the queue didn't exist. */ -bool script_hqueue_clear(int idx) +static bool script_hqueue_clear(int idx) { struct script_queue *queue = NULL; @@ -19479,7 +24523,7 @@ bool script_hqueue_clear(int idx) * .@id = queueiterator(.@queue_id); * @endcode */ -BUILDIN(queueiterator) +static BUILDIN(queueiterator) { int qid = script_getnum(st, 2); struct script_queue *queue = NULL; @@ -19522,7 +24566,7 @@ BUILDIN(queueiterator) * } * @endcode */ -BUILDIN(qiget) +static BUILDIN(qiget) { int idx = script_getnum(st, 2); struct script_queue_iterator *it = NULL; @@ -19556,7 +24600,7 @@ BUILDIN(qiget) * } * @endcode */ -BUILDIN(qicheck) +static BUILDIN(qicheck) { int idx = script_getnum(st, 2); struct script_queue_iterator *it = NULL; @@ -19587,7 +24631,7 @@ BUILDIN(qicheck) * qiclear(.@iter); * @endcode */ -BUILDIN(qiclear) +static BUILDIN(qiclear) { int idx = script_getnum(st, 2); struct script_queue_iterator *it = NULL; @@ -19612,7 +24656,8 @@ BUILDIN(qiclear) * packageitem({<optional container_item_id>}) * when no item id is provided it tries to assume it comes from the current item id being processed (if any) **/ -BUILDIN(packageitem) { +static BUILDIN(packageitem) +{ struct item_data *data = NULL; struct map_session_data *sd = NULL; int nameid; @@ -19646,7 +24691,8 @@ BUILDIN(packageitem) { /* New Battlegrounds Stuff */ /* bg_team_create(map_name,respawn_x,respawn_y) */ /* returns created team id or -1 when fails */ -BUILDIN(bg_create_team) { +static BUILDIN(bg_create_team) +{ const char *map_name, *ev = "", *dev = "";//ev and dev will be dropped. int x, y, map_index = 0, bg_id; @@ -19654,7 +24700,7 @@ BUILDIN(bg_create_team) { if( strcmp(map_name,"-") != 0 ) { map_index = script->mapindexname2id(st,map_name); if( map_index == 0 ) { // Invalid Map - script_pushint(st,0); + script_pushint(st, -1); return true; } } @@ -19673,7 +24719,8 @@ BUILDIN(bg_create_team) { /* bg_join_team(team_id{,optional account id}) */ /* when account id is not present it tries to autodetect from the attached player (if any) */ /* returns 0 when successful, 1 otherwise */ -BUILDIN(bg_join_team) { +static BUILDIN(bg_join_team) +{ struct map_session_data *sd; int team_id = script_getnum(st, 2); @@ -19699,9 +24746,9 @@ BUILDIN(bg_join_team) { * 3 - Party Bound * 4 - Character Bound *------------------------------------------*/ -BUILDIN(countbound) +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) @@ -19709,12 +24756,12 @@ 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; } @@ -19734,7 +24781,7 @@ BUILDIN(countbound) * 3 - Party Bound * 4 - Character Bound *------------------------------------------*/ -BUILDIN(checkbound) +static BUILDIN(checkbound) { int i, nameid = script_getnum(st,2); int bound_type = 0; @@ -19755,27 +24802,28 @@ 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; } /* bg_match_over( arena_name {, optional canceled } ) */ /* returns 0 when successful, 1 otherwise */ -BUILDIN(bg_match_over) +static BUILDIN(bg_match_over) { bool canceled = script_hasdata(st,3) ? true : false; struct bg_arena *arena = bg->name2arena(script_getstr(st, 2)); @@ -19789,7 +24837,8 @@ BUILDIN(bg_match_over) return true; } -BUILDIN(instance_mapname) { +static BUILDIN(instance_mapname) +{ const char *map_name; int m; short instance_id = -1; @@ -19809,10 +24858,12 @@ BUILDIN(instance_mapname) { return true; } + /* modify an instances' reload-spawn point */ /* instance_set_respawn <map_name>,<x>,<y>{,<instance_id>} */ /* returns 1 when successful, 0 otherwise. */ -BUILDIN(instance_set_respawn) { +static BUILDIN(instance_set_respawn) +{ const char *map_name; short instance_id = -1; short mid; @@ -19853,12 +24904,13 @@ BUILDIN(instance_set_respawn) { } return true; } + /** * @call openshop({NPC Name}); * * @return 1 on success, 0 otherwise. **/ -BUILDIN(openshop) +static BUILDIN(openshop) { struct npc_data *nd; struct map_session_data *sd; @@ -19888,17 +24940,147 @@ BUILDIN(openshop) return true; } + +static bool script_sellitemcurrency_add(struct npc_data *nd, struct script_state* st, int argIndex) +{ + nullpo_retr(false, nd); + nullpo_retr(false, st); + + if (!script_hasdata(st, argIndex + 1)) + return false; + + int id = script_getnum(st, argIndex); + struct item_data *it; + if (!(it = itemdb->exists(id))) { + ShowWarning("buildin_sellitemcurrency: unknown item id '%d'!\n", id); + return false; + } + int qty = 0; + if ((qty = script_getnum(st, argIndex + 1)) <= 0) { + ShowError("buildin_sellitemcurrency: invalid 'qty'!\n"); + return false; + } + int refine_level = -1; + if (script_hasdata(st, argIndex + 2)) { + refine_level = script_getnum(st, argIndex + 2); + } + int items = nd->u.scr.shop->items; + if (nd->u.scr.shop == NULL || items == 0) { + ShowWarning("buildin_sellitemcurrency: shop not have items!\n"); + return false; + } + if (nd->u.scr.shop->shop_last_index >= items || nd->u.scr.shop->shop_last_index < 0) { + ShowWarning("buildin_sellitemcurrency: wrong selected shop index!\n"); + return false; + } + + struct npc_item_list *item_list = &nd->u.scr.shop->item[nd->u.scr.shop->shop_last_index]; + int index = item_list->value2; + if (item_list->currency == NULL) { + CREATE(item_list->currency, struct npc_barter_currency, 1); + item_list->value2 ++; + } else { + RECREATE(item_list->currency, struct npc_barter_currency, ++item_list->value2); + } + struct npc_barter_currency *currency = &item_list->currency[index]; + currency->nameid = id; + currency->refine = refine_level; + currency->amount = qty; + return true; +} + +/** + * @call sellitemcurrency <Item_ID>,qty{,refine}}; + * + * adds <Item_ID> to last item in expanded barter shop + **/ +static BUILDIN(sellitemcurrency) +{ + struct npc_data *nd; + if ((nd = map->id2nd(st->oid)) == NULL) { + ShowWarning("buildin_sellitemcurrency: trying to run without a proper NPC!\n"); + return false; + } + if (nd->u.scr.shop == NULL || nd->u.scr.shop->type != NST_EXPANDED_BARTER) { + ShowWarning("buildin_sellitemcurrency: this command can be used only with expanded barter shops!\n"); + return false; + } + + script->sellitemcurrency_add(nd, st, 2); + return true; +} + +/** + * @call endsellitem; + * + * complete sell item in expanded barter shop (NST_EXPANDED_BARTER) + **/ +static BUILDIN(endsellitem) +{ + struct npc_data *nd; + if ((nd = map->id2nd(st->oid)) == NULL) { + ShowWarning("buildin_endsellitem: trying to run without a proper NPC!\n"); + return false; + } + if (nd->u.scr.shop == NULL || nd->u.scr.shop->type != NST_EXPANDED_BARTER) { + ShowWarning("buildin_endsellitem: this command can be used only with expanded barter shops!\n"); + return false; + } + + int newIndex = nd->u.scr.shop->shop_last_index; + const struct npc_item_list *const newItem = &nd->u.scr.shop->item[newIndex]; + int i = 0; + for (i = 0; i < nd->u.scr.shop->items - 1; i++) { + const struct npc_item_list *const item = &nd->u.scr.shop->item[i]; + if (item->nameid != newItem->nameid || item->value != newItem->value) + continue; + if (item->value2 != newItem->value2) + continue; + bool found = true; + for (int k = 0; k < item->value2; k ++) { + struct npc_barter_currency *currency = &item->currency[k]; + struct npc_barter_currency *newCurrency = &newItem->currency[k]; + if (currency->nameid != newCurrency->nameid || + currency->amount != newCurrency->amount || + currency->refine != newCurrency->refine) { + found = false; + break; + } + } + if (!found) + continue; + break; + } + + if (i != nd->u.scr.shop->items - 1) { + if (nd->u.scr.shop->item[i].qty != -1) { + nd->u.scr.shop->item[i].qty += nd->u.scr.shop->item[newIndex].qty; + npc->expanded_barter_tosql(nd, i); + } + nd->u.scr.shop->shop_last_index --; + nd->u.scr.shop->items--; + if (nd->u.scr.shop->item[newIndex].currency != NULL) { + aFree(nd->u.scr.shop->item[newIndex].currency); + nd->u.scr.shop->item[newIndex].currency = NULL; + } + } + + return true; +} + /** * @call sellitem <Item_ID>,{,price{,qty}}; * * adds <Item_ID> (or modifies if present) to shop * if price not provided (or -1) uses the item's value_sell **/ -BUILDIN(sellitem) { +static BUILDIN(sellitem) +{ struct npc_data *nd; 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)) ) { @@ -19909,16 +25091,71 @@ BUILDIN(sellitem) { return false; } - value = script_hasdata(st,3) ? script_getnum(st, 3) : it->value_buy; - if( value == -1 ) - value = it->value_buy; + const bool have_shop = (nd->u.scr.shop != NULL); + if (!have_shop) { + npc->trader_update(nd->src_id ? nd->src_id : nd->bl.id); + } - 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 ) + 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_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 if (nd->u.scr.shop->type == NST_EXPANDED_BARTER) { + if (!script_hasdata(st, 4)) { + ShowError("buildin_sellitem: invalid number of parameters for expanded barter type shop!\n"); + return false; + } + if ((qty = script_getnum(st, 4)) <= 0 && qty != -1) { + ShowError("buildin_sellitem: invalid 'qty' for expanded barter type shop!\n"); + return false; + } + } + + if (have_shop) { + if (nd->u.scr.shop->type == NST_BARTER) { + 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 if (nd->u.scr.shop->type == NST_EXPANDED_BARTER) { + 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) + continue; + if (item->value2 != (script_lastdata(st) - 4) / 3) + continue; + bool found = true; + for (int k = 0; k < item->value2; k ++) { + const int scriptOffset = k * 3 + 5; + struct npc_barter_currency *currency = &item->currency[k]; + if (currency->nameid != script_getnum(st, scriptOffset) || + currency->amount != script_getnum(st, scriptOffset + 1) || + currency->refine != script_getnum(st, scriptOffset + 2)) { + found = false; + break; + } + } + if (!found) + continue; break; + } + } else { + for (i = 0; i < nd->u.scr.shop->items; i++) { + if (nd->u.scr.shop->item[i].nameid == id) { + break; + } + } } } @@ -19934,19 +25171,30 @@ 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; + } + } + + bool foundInShop = (i != nd->u.scr.shop->items); + if (foundInShop) { 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; } @@ -19956,11 +25204,89 @@ 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; + nd->u.scr.shop->item[i].currency = NULL; + } + nd->u.scr.shop->shop_last_index = i; + + if (!foundInShop) { + for (int k = 5; k <= script_lastdata(st); k += 3) { + script->sellitemcurrency_add(nd, st, k); + } } + if (foundInShop) { + if (nd->u.scr.shop->type == NST_EXPANDED_BARTER) { /* has been manually updated, make it reflect on sql */ + npc->expanded_barter_tosql(nd, i); + } + } return true; } + +/** + * @call startsellitem <Item_ID>,{,price{,qty}}; + * + * Starts adding item into expanded barter shop (NST_EXPANDED_BARTER) + **/ +static BUILDIN(startsellitem) +{ + struct npc_data *nd; + struct item_data *it; + int i = 0, id = script_getnum(st,2); + int value2 = 0; + int qty = 0; + + if (!(nd = map->id2nd(st->oid))) { + ShowWarning("buildin_startsellitem: trying to run without a proper NPC!\n"); + return false; + } else if (!(it = itemdb->exists(id))) { + ShowWarning("buildin_startsellitem: unknown item id '%d'!\n", id); + return false; + } + + const bool have_shop = (nd->u.scr.shop != NULL); + if (!have_shop) { + npc->trader_update(nd->src_id ? nd->src_id : nd->bl.id); + } + + if (nd->u.scr.shop->type != NST_EXPANDED_BARTER) { + ShowWarning("script_startsellitem: can works only for NST_EXPANDED_BARTER shops"); + return false; + } + + int value = script_hasdata(st, 3) ? script_getnum(st, 3) : it->value_buy; + if (value == -1) + value = it->value_buy; + + if ((qty = script_getnum(st, 4)) <= 0 && qty != -1) { + ShowError("buildin_startsellitem: invalid 'qty' for expanded barter type shop!\n"); + return false; + } + + 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) { + ShowWarning("buildin_startsellitem: Can't add %s (%s/%s), shop list is full!\n", it->name, nd->exname, nd->path); + return false; + } + i = nd->u.scr.shop->items; + RECREATE(nd->u.scr.shop->item, struct npc_item_list, ++nd->u.scr.shop->items); + } + + 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; + nd->u.scr.shop->item[i].currency = NULL; + nd->u.scr.shop->shop_last_index = i; + return true; +} + /** * @call stopselling <Item_ID>; * @@ -19968,56 +25294,100 @@ BUILDIN(sellitem) { * * @return 1 on success, 0 otherwise **/ -BUILDIN(stopselling) { +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 if (nd->u.scr.shop->type == NST_EXPANDED_BARTER) { + if (!script_hasdata(st, 3)) { + ShowError("buildin_stopselling: called with wrong number of arguments\n"); + return false; + } + const int price = script_getnum(st, 3); + 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 == price) { + 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); + else if (nd->u.scr.shop->type == NST_BARTER) + npc->barter_delfromsql(nd, i); + else if (nd->u.scr.shop->type == NST_EXPANDED_BARTER) + npc->expanded_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; + if (nd->u.scr.shop->item[i].currency != NULL) { + aFree(nd->u.scr.shop->item[i].currency); + nd->u.scr.shop->item[i].currency = NULL; + } - 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; + nd->u.scr.shop->item[cursor].currency = nd->u.scr.shop->item[i].currency; } cursor++; } + nd->u.scr.shop->items--; + nd->u.scr.shop->item[nd->u.scr.shop->items].currency = NULL; script_pushint(st, 1); - } else + } else { script_pushint(st, 0); + } return true; } + /** * @call setcurrency <Val1>{,<Val2>}; * * updates currently-attached player shop currency **/ /* setcurrency(<Val1>,{<Val2>}) */ -BUILDIN(setcurrency) +static BUILDIN(setcurrency) { int val1 = script_getnum(st,2), val2 = script_hasdata(st, 3) ? script_getnum(st,3) : 0; @@ -20033,6 +25403,7 @@ BUILDIN(setcurrency) return true; } + /** * @call tradertype(<type>); * @@ -20040,7 +25411,8 @@ BUILDIN(setcurrency) * check enum npc_shop_types for list * cleans shop list on use **/ -BUILDIN(tradertype) { +static BUILDIN(tradertype) +{ int type = script_getnum(st, 2); struct npc_data *nd; @@ -20061,7 +25433,9 @@ 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); + npc->expanded_barter_delfromsql(nd, INT_MAX); } #if PACKETVER < 20131223 @@ -20070,18 +25444,32 @@ BUILDIN(tradertype) { script->reportsrc(st); } #endif +#if PACKETVER_MAIN_NUM < 20190116 && PACKETVER_RE_NUM < 20190116 && PACKETVER_ZERO_NUM < 20181226 + if (type == NST_BARTER) { + ShowWarning("buildin_tradertype: NST_BARTER is only available with PACKETVER_ZERO_NUM 20181226 or PACKETVER_MAIN_NUM 20190116 or PACKETVER_RE_NUM 20190116 or newer!\n"); + script->reportsrc(st); + } +#endif +#if PACKETVER_MAIN_NUM < 20191120 && PACKETVER_RE_NUM < 20191106 && PACKETVER_ZERO_NUM < 20191127 + if (type == NST_EXPANDED_BARTER) { + ShowWarning("buildin_tradertype: NST_EXPANDED_BARTER is only available with PACKETVER_ZERO_NUM 20191127 or PACKETVER_MAIN_NUM 20191120 or PACKETVER_RE_NUM 20191106 or newer!\n"); + script->reportsrc(st); + } +#endif if( nd->u.scr.shop ) nd->u.scr.shop->type = type; return true; } + /** * @call purchaseok(); * * signs the transaction can proceed **/ -BUILDIN(purchaseok) { +static BUILDIN(purchaseok) +{ struct npc_data *nd; if( !(nd = map->id2nd(st->oid)) || !nd->u.scr.shop ) { @@ -20093,12 +25481,14 @@ BUILDIN(purchaseok) { return true; } + /** * @call shopcount(<Item_ID>); * * @return number of available items in the script's attached shop **/ -BUILDIN(shopcount) { +static BUILDIN(shopcount) +{ struct npc_data *nd; int id = script_getnum(st, 2); unsigned short i; @@ -20109,8 +25499,8 @@ 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 && nd->u.scr.shop->type != NST_EXPANDED_BARTER) { + ShowWarning("buildin_shopcount(%d): trying to use on a non-NST_MARKET and non-NST_BARTER and non-NST_EXPANDED_BARTER shop!\n",id); return false; } @@ -20135,9 +25525,9 @@ BUILDIN(shopcount) { * Sends a message through the specified chat channel. * */ -BUILDIN(channelmes) +static BUILDIN(channelmes) { - struct map_session_data *sd = script->rid2sd(st); + struct map_session_data *sd = map->id2sd(st->rid); const char *channelname = script_getstr(st, 2); struct channel_data *chan = channel->search(channelname, sd); @@ -20152,14 +25542,66 @@ BUILDIN(channelmes) return true; } +static BUILDIN(addchannelhandler) +{ + int i; + struct map_session_data *sd = map->id2sd(st->rid); + const char *channelname = script_getstr(st, 2); + const char *eventname = script_getstr(st, 3); + struct channel_data *chan = channel->search(channelname, sd); + + if (!chan) { + script_pushint(st, 0); + return true; + } + + ARR_FIND(0, MAX_EVENTQUEUE, i, chan->handlers[i][0] == '\0'); + + if (i < MAX_EVENTQUEUE) { + safestrncpy(chan->handlers[i], eventname, EVENT_NAME_LENGTH); //Event enqueued. + script_pushint(st, 1); + return true; + } + + ShowWarning("script:addchannelhandler: too many handlers for channel %s.\n", channelname); + script_pushint(st, 0); + return true; +} + +static BUILDIN(removechannelhandler) +{ + int i; + struct map_session_data *sd = map->id2sd(st->rid); + const char *channelname = script_getstr(st, 2); + const char *eventname = script_getstr(st, 3); + struct channel_data *chan = channel->search(channelname, sd); + + if (!chan) { + script_pushint(st, 0); + return true; + } + + for (i = 0; i < MAX_EVENTQUEUE; i++) { + if (strcmp(chan->handlers[i], eventname) == 0) { + chan->handlers[i][0] = '\0'; + script_pushint(st, 1); + return true; + } + } + + script_pushint(st, 0); + return true; +} + /** By Cydh -Display script message -showscript "<message>"{,<GID>}; -*/ -BUILDIN(showscript) { + * Display script message + * showscript "<message>"{, <GID>}; + */ +static BUILDIN(showscript) +{ struct block_list *bl = NULL; const char *msg = script_getstr(st, 2); - int id = 0; + int id = 0, flag = AREA; if (script_hasdata(st, 3)) { id = script_getnum(st, 3); @@ -20171,18 +25613,18 @@ BUILDIN(showscript) { if (!bl) { ShowError("buildin_showscript: Script not attached. (id=%d, rid=%d, oid=%d)\n", id, st->rid, st->oid); - script_pushint(st, 0); return false; } - clif->ShowScript(bl, msg); - - script_pushint(st, 1); + if (script_hasdata(st, 4)) + if (script_getnum(st, 4) == SELF) + flag = SELF; + clif->ShowScript(bl, msg, flag); return true; } -BUILDIN(mergeitem) +static BUILDIN(mergeitem) { struct map_session_data *sd = script->rid2sd(st); @@ -20193,8 +25635,198 @@ BUILDIN(mergeitem) return true; } + +// getcalendartime(<day of month>, <day of week>{, <hour>{, <minute>}}); +// Returns the UNIX Timestamp of next ocurrency of given time +static BUILDIN(getcalendartime) +{ + struct tm info = { 0 }; + int day_of_month = script_hasdata(st, 4) ? script_getnum(st, 4) : -1; + int day_of_week = script_hasdata(st, 5) ? script_getnum(st, 5) : -1; + int year = date_get_year(); + int month = date_get_month(); + int day = date_get_day(); + int cur_hour = date_get_hour(); + int cur_min = date_get_min(); + int hour = script_getnum(st, 2); + int minute = script_getnum(st, 3); + + info.tm_sec = 0; + info.tm_min = minute; + info.tm_hour = hour; + info.tm_mday = day; + info.tm_mon = month - 1; + info.tm_year = year - 1900; + + if (day_of_month > -1 && day_of_week > -1) { + ShowError("script:getcalendartime: You must only specify a day_of_week or a day_of_month, not both\n"); + script_pushint(st, -1); + return false; + } + if (day_of_month > -1 && (day_of_month < 1 || day_of_month > 31)) { + ShowError("script:getcalendartime: Day of Month in invalid range. Must be between 1 and 31.\n"); + script_pushint(st, -1); + return false; + } + if (day_of_week > -1 && (day_of_week < 0 || day_of_week > 6)) { + ShowError("script:getcalendartime: Day of Week in invalid range. Must be between 0 and 6.\n"); + script_pushint(st, -1); + return false; + } + if (hour > -1 && (hour > 23)) { + ShowError("script:getcalendartime: Hour in invalid range. Must be between 0 and 23.\n"); + script_pushint(st, -1); + return false; + } + if (minute > -1 && (minute > 59)) { + ShowError("script:getcalendartime: Minute in invalid range. Must be between 0 and 59.\n"); + script_pushint(st, -1); + return false; + } + if (hour == -1 || minute == -1) { + ShowError("script:getcalendartime: Minutes and Hours are required\n"); + script_pushint(st, -1); + return false; + } + + if (day_of_month > -1) { + if (day_of_month < day) { // Next Month + info.tm_mon++; + } else if (day_of_month == day) { // Today + if (hour < cur_hour || (hour == cur_hour && minute < cur_min)) { // But past time, next month + info.tm_mon++; + } + } + + // Loops until month has finding a month that has day_of_month + do { + time_t t; + struct tm *lt; + info.tm_mday = day_of_month; + t = mktime(&info); + lt = localtime(&t); + info = *lt; + } while (info.tm_mday != day_of_month); + } else if (day_of_week > -1) { + int cur_wday = date_get_dayofweek(); + + if (day_of_week > cur_wday) { // This week + info.tm_mday += (day_of_week - cur_wday); + } else if (day_of_week == cur_wday) { // Today + if (hour < cur_hour || (hour == cur_hour && minute <= cur_min)) { + info.tm_mday += 7; // Next week + } + } else if (day_of_week < cur_wday) { // Next week + info.tm_mday += (7 - cur_wday + day_of_week); + } + } else if (day_of_week == -1 && day_of_month == -1) { // Next occurence of hour/min + if (hour < cur_hour || (hour == cur_hour && minute < cur_min)) { + info.tm_mday++; + } + } + + script_pushint(st, mktime(&info)); + + return true; +} + +enum consolemes_type { + CONSOLEMES_DEBUG = 0, + CONSOLEMES_ERROR = 1, + CONSOLEMES_WARNING = 2, + CONSOLEMES_INFO = 3, + CONSOLEMES_STATUS = 4, + CONSOLEMES_NOTICE = 5, +}; + +/*========================================== +* consolemes(<type>, "text") +*------------------------------------------*/ +static BUILDIN(consolemes) +{ + struct StringBuf buf; + StrBuf->Init(&buf); + int type = script_hasdata(st, 2) ? script_getnum(st, 2) : 0; + + if (!script->sprintf_helper(st, 3, &buf)) { + StrBuf->Destroy(&buf); + script_pushint(st, 0); + return false; + } + + switch (type) { + default: + case CONSOLEMES_DEBUG: + ShowDebug("consolemes: %s\n", StrBuf->Value(&buf)); + break; + case CONSOLEMES_ERROR: + ShowError("consolemes: (st->rid: %d) (st->oid: %d) %s\n", st->rid, st->oid, StrBuf->Value(&buf)); + break; + case CONSOLEMES_WARNING: + ShowWarning("consolemes: (st->rid: %d) (st->oid: %d) %s\n", st->rid, st->oid, StrBuf->Value(&buf)); + break; + case CONSOLEMES_INFO: + ShowInfo("consolemes: %s\n", StrBuf->Value(&buf)); + break; + case CONSOLEMES_STATUS: + ShowStatus("consolemes: %s\n", StrBuf->Value(&buf)); + break; + case CONSOLEMES_NOTICE: + ShowNotice("consolemes: %s\n", StrBuf->Value(&buf)); + break; + } + + StrBuf->Destroy(&buf); + script_pushint(st, 1); + return true; +} + +static BUILDIN(setfavoriteitemidx) +{ + struct map_session_data *sd = script_rid2sd(st); + int idx = script_getnum(st, 2); + int value = script_getnum(st, 3); + + if (sd == NULL) { + ShowError("buildin_setfavoriteitemidx: No player attached.\n"); + return false; + } + + if (idx < 0 || idx >= sd->status.inventorySize) { + ShowError("buildin_setfavoriteitemidx: Invalid inventory index %d (min: %d, max: %d).\n", idx, 0, (sd->status.inventorySize - 1)); + return false; + } else if (sd->inventory_data[idx] == NULL || sd->inventory_data[idx]->nameid <= 0) { + ShowWarning("buildin_setfavoriteitemidx: Current inventory index %d has no data.\n", idx); + return false; + } else if (sd->status.inventory[idx].equip > 0) { + ShowWarning("buildin_setfavoriteitemidx: Cant change favorite flag of an equipped item.\n"); + return false; + } else { + sd->status.inventory[idx].favorite = cap_value(value, 0, 1); + clif->favorite_item(sd, idx); + } + + return true; +} + +static BUILDIN(autofavoriteitem) +{ + int nameid = script_getnum(st, 2); + int flag = script_getnum(st, 3); + struct item_data *item_data; + + if ((item_data = itemdb->exists(nameid)) == NULL) { + ShowError("buildin_autofavoriteitem: Invalid item '%d'.\n", nameid); + return false; + } + + item_data->flag.auto_favorite = cap_value(flag, 0, 1); + return true; +} + /** place holder for the translation macro **/ -BUILDIN(_) { +static BUILDIN(_) +{ return true; } @@ -20204,7 +25836,52 @@ BUILDIN(activatepset); BUILDIN(deactivatepset); BUILDIN(deletepset); -BUILDIN(pcre_match) { +enum dressroom_mode { + DRESSROOM_CLOSE = 0, + DRESSROOM_OPEN = 1 +}; + +/** + * dressroom({<enum dressroom_mode>}); + */ +static BUILDIN(dressroom) +{ +#if PACKETVER >= 20150513 + struct map_session_data *sd = script->rid2sd(st); + enum dressroom_mode mode = DRESSROOM_OPEN; + + if (sd == NULL) { + return false; + } + + if (script_hasdata(st, 2)) { + mode = script_getnum(st, 2); + } + + switch (mode) { + case DRESSROOM_OPEN: + clif->dressroom_open(sd, 1); + break; + case DRESSROOM_CLOSE: + clif->dressroom_open(sd, 0); + break; + default: + ShowWarning("script:dressroom: unknown mode (%u).\n", mode); + script_pushint(st, 0); + return false; + } + + script_pushint(st, 1); + return true; +#else + ShowError("The dressing room works only with packet version >= 20150513"); + script_pushint(st, 0); + return false; +#endif +} + +static BUILDIN(pcre_match) +{ const char *input = script_getstr(st, 2); const char *regex = script_getstr(st, 3); @@ -20213,6 +25890,685 @@ BUILDIN(pcre_match) { } /** + * navigateto("<map>"{,<x>,<y>,<flag>,<hide_window>,<monster_id>,<char_id>}); + */ +static BUILDIN(navigateto) +{ +#if PACKETVER >= 20111010 + struct map_session_data* sd; + const char *mapname; + uint16 x = 0; + uint16 y = 0; + uint16 monster_id = 0; + uint8 flag = NAV_KAFRA_AND_AIRSHIP; + bool hideWindow = true; + + mapname = script_getstr(st, 2); + + if (script_hasdata(st, 3)) + x = script_getnum(st, 3); + if (script_hasdata(st, 4)) + y = script_getnum(st, 4); + if (script_hasdata(st, 5)) + flag = (uint8)script_getnum(st, 5); + if (script_hasdata(st, 6)) + hideWindow = script_getnum(st, 6) ? true : false; + if (script_hasdata(st, 7)) + monster_id = script_getnum(st, 7); + + if (script_hasdata(st, 8)) { + sd = map->charid2sd(script_getnum(st, 8)); + } else { + sd = script->rid2sd(st); + } + + clif->navigate_to(sd, mapname, x, y, flag, hideWindow, monster_id); + + return true; +#else + ShowError("Navigation system works only with packet version >= 20111010"); + return false; +#endif +} + +static bool rodex_sendmail_sub(struct script_state *st, struct rodex_message *msg) +{ + const char *sender_name, *title, *body; + + if (strcmp(script->getfuncname(st), "rodex_sendmail_acc") == 0 || strcmp(script->getfuncname(st), "rodex_sendmail_acc2") == 0) + msg->receiver_accountid = script_getnum(st, 2); + else + msg->receiver_id = script_getnum(st, 2); + + sender_name = script_getstr(st, 3); + if (strlen(sender_name) >= NAME_LENGTH) { + ShowError("script:rodex_sendmail: Sender name must not be bigger than %d!\n", NAME_LENGTH - 1); + return false; + } + safestrncpy(msg->sender_name, sender_name, NAME_LENGTH); + + title = script_getstr(st, 4); + if (strlen(title) >= RODEX_TITLE_LENGTH) { + ShowError("script:rodex_sendmail: Mail Title must not be bigger than %d!\n", RODEX_TITLE_LENGTH - 1); + return false; + } + safestrncpy(msg->title, title, RODEX_TITLE_LENGTH); + + body = script_getstr(st, 5); + if (strlen(body) >= MAIL_BODY_LENGTH) { + ShowError("script:rodex_sendmail: Mail Message must not be bigger than %d!\n", RODEX_BODY_LENGTH - 1); + return false; + } + safestrncpy(msg->body, body, MAIL_BODY_LENGTH); + + if (script_hasdata(st, 6)) { + msg->zeny = script_getnum(st, 6); + if (msg->zeny < 0 || msg->zeny > MAX_ZENY) { + ShowError("script:rodex_sendmail: Invalid Zeny value %"PRId64"!\n", msg->zeny); + return false; + } + } + + return true; +} + +static BUILDIN(rodex_sendmail) +{ + struct rodex_message msg = { 0 }; + int item_count = 0, i = 0, param = 7; + + // Common parameters - sender/message/zeny + if (rodex_sendmail_sub(st, &msg) == false) + return false; + + // Item list + while (i < RODEX_MAX_ITEM && script_hasdata(st, param)) { + struct item_data *idata; + + if (!script_hasdata(st, param + 1)) { + ShowError("script:rodex_sendmail: Missing Item %d amount!\n", (i + 1)); + return false; + } + + ++item_count; + if (data_isstring(script_getdata(st, param)) == false) { + int itemid = script_getnum(st, param); + + if (itemdb->exists(itemid) == false) { + ShowError("script:rodex_sendmail: Unknown item ID %d.\n", itemid); + return false; + } + + idata = itemdb->search(itemid); + } + else { + ShowError("script:rodex_sendmail: Item %d must be passed as Number.\n", (i + 1)); + return false; + } + + msg.items[i].item.nameid = idata->nameid; + msg.items[i].item.amount = script_getnum(st, (param + 1)); + msg.items[i].item.identify = 1; + + ++i; + param += 2; + } + msg.items_count = item_count; + + msg.type = MAIL_TYPE_NPC; + if (msg.zeny > 0) + msg.type |= MAIL_TYPE_ZENY; + if (msg.items_count > 0) + msg.type |= MAIL_TYPE_ITEM; + msg.send_date = (int)time(NULL); + msg.expire_date = (int)time(NULL) + RODEX_EXPIRE; + + intif->rodex_sendmail(&msg); + + return true; +} + +static BUILDIN(rodex_sendmail2) +{ + struct rodex_message msg = { 0 }; + int item_count = 0, i = 0, param = 7; + + // Common parameters - sender/message/zeny + if (rodex_sendmail_sub(st, &msg) == false) + return false; + + // Item list + while (i < RODEX_MAX_ITEM && script_hasdata(st, param)) { + struct item_data *idata; + int j; + + // Tests + if (!script_hasdata(st, param + 1)) { + ShowError("script:rodex_sendmail: Missing Item %d amount!\n", (i + 1)); + return false; + } + if (!script_hasdata(st, param + 2)) { + ShowError("script:rodex_sendmail: Missing Item %d refine!\n", (i + 1)); + return false; + } + if (!script_hasdata(st, param + 3)) { + ShowError("script:rodex_sendmail: Missing Item %d attribute!\n", (i + 1)); + return false; + } + for (j = 0; j < MAX_SLOTS; ++j) { + if (!script_hasdata(st, param + 4 + j)) { + ShowError("script:rodex_sendmail: Missing Item %d card %d!\n", (i + 1), j); + return false; + } + } + + // Set data to message + ++item_count; + if (data_isstring(script_getdata(st, param)) == false) { + int itemid = script_getnum(st, param); + + if (itemdb->exists(itemid) == false) { + ShowError("script:rodex_sendmail: Unknown item ID %d.\n", itemid); + return false; + } + + idata = itemdb->search(itemid); + } else { + ShowError("script:rodex_sendmail: Item %d must be passed as Number.\n", (i + 1)); + return false; + } + + msg.items[i].item.nameid = idata->nameid; + msg.items[i].item.amount = script_getnum(st, (param + 1)); + msg.items[i].item.refine = script_getnum(st, (param + 2)); + msg.items[i].item.attribute = script_getnum(st, (param + 3)); + msg.items[i].item.identify = 1; + + for (j = 0; j < MAX_SLOTS; ++j) { + msg.items[i].item.card[j] = script_getnum(st, param + 4 + j); + } + + ++i; + param += 4 + MAX_SLOTS; + } + msg.items_count = item_count; + + msg.type = MAIL_TYPE_NPC; + if (msg.zeny > 0) + msg.type |= MAIL_TYPE_ZENY; + if (msg.items_count > 0) + msg.type |= MAIL_TYPE_ITEM; + msg.send_date = (int)time(NULL); + msg.expire_date = (int)time(NULL) + RODEX_EXPIRE; + + intif->rodex_sendmail(&msg); + + return true; +} + +/** + * Clan System: Add a player to a clan + */ +static BUILDIN(clan_join) +{ + struct map_session_data *sd = NULL; + int clan_id = script_getnum(st, 2); + + if (script_hasdata(st, 3)) + sd = map->id2sd(script_getnum(st, 3)); + else + sd = map->id2sd(st->rid); + + if (sd == NULL) { + script_pushint(st, false); + return false; + } + + if (clan->join(sd, clan_id)) + script_pushint(st, true); + else + script_pushint(st, false); + + return true; +} + +/** + * Clan System: Remove a player from clan + */ +static BUILDIN(clan_leave) +{ + struct map_session_data *sd = NULL; + + if (script_hasdata(st, 2)) + sd = map->id2sd(script_getnum(st, 2)); + else + sd = map->id2sd(st->rid); + + if (sd == NULL) { + script_pushint(st, false); + return false; + } + + if (clan->leave(sd, false)) + script_pushint(st, true); + else + script_pushint(st, false); + + return true; +} + +/** + * Clan System: Show clan emblem next to npc name + */ +static BUILDIN(clan_master) +{ + struct npc_data *nd = map->id2nd(st->oid); + int clan_id = script_getnum(st, 2); + + if (nd == NULL) { + script_pushint(st, false); + return false; + } else if (clan_id <= 0) { + script_pushint(st, false); + ShowError("buildin_clan_master: Received Invalid Clan ID %d\n", clan_id); + return false; + } else if (clan->search(clan_id) == NULL) { + script_pushint(st, false); + ShowError("buildin_clan_master: Received Id of a nonexistent Clan. Id: %d\n", clan_id); + return false; + } + + nd->clan_id = clan_id; + clif->sc_load(&nd->bl, nd->bl.id, AREA, status->get_sc_icon(SC_CLAN_INFO), 0, clan_id, 0); + + script_pushint(st, true); + return true; +} + +static BUILDIN(airship_respond) +{ + struct map_session_data *sd = map->id2sd(st->rid); + int32 flag = script_getnum(st, 2); + + if (sd == NULL) + return false; + + if (flag < P_AIRSHIP_NONE || flag > P_AIRSHIP_ITEM_INVALID) { + ShowWarning("buildin_airship_respond: invalid flag %d has been given.", flag); + return false; + } + + clif->PrivateAirshipResponse(sd, flag); + return true; +} + +/** + * hateffect(EffectID, Enable_State) + */ +static BUILDIN(hateffect) +{ +#if PACKETVER >= 20150422 + struct map_session_data *sd = script_rid2sd(st); + int effectId, enabled = 0; + int i; + + if (sd == NULL) + return false; + + effectId = script_getnum(st, 2); + enabled = script_getnum(st, 3); + + for (i = 0; i < VECTOR_LENGTH(sd->hatEffectId); ++i) { + if (VECTOR_INDEX(sd->hatEffectId, i) == effectId) { + if (enabled == 1) { // Already Enabled + return true; + } else { // Remove + VECTOR_ERASE(sd->hatEffectId, i); + clif->hat_effect_single(&sd->bl, effectId, enabled); + return true; + } + } + } + + VECTOR_ENSURE(sd->hatEffectId, 1, 1); + VECTOR_PUSH(sd->hatEffectId, effectId); + + clif->hat_effect_single(&sd->bl, effectId, enabled); +#endif + return true; +} + +static BUILDIN(openstylist) +{ + struct map_session_data *sd = script_rid2sd(st); + + if (sd == NULL) + return false; + +#if PACKETVER >= 20150128 + clif->open_ui(sd, CZ_STYLIST_UI); +#endif + 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; +} + +// force close roulette window if it opened +static BUILDIN(closeroulette) +{ + struct map_session_data *sd = script_rid2sd(st); + if (sd == NULL) + return false; + clif->roulette_close(sd); + return true; +} + +static BUILDIN(openrefineryui) +{ + struct map_session_data *sd = script_rid2sd(st); + + if (sd == NULL) { + script_pushint(st, 0); + return true; + } + + if (battle_config.enable_refinery_ui == 0) { + script_pushint(st, 0); + return true; + } + + clif->OpenRefineryUI(sd); + script_pushint(st, 1); + return true; +} + +/** + * identify(<item id>) + * Identifies the first unidentified <item id> item on player's inventory. + * Returns -2 on error, -1 if no item to identify was found, identified idx otherwise. + */ +static BUILDIN(identify) +{ + struct map_session_data *sd = script_rid2sd(st); + + if (sd == NULL) { + script_pushint(st, -2); + return true; + } + + int itemid = script_getnum(st, 2); + if (itemdb->exists(itemid) == NULL) { + ShowError("buildin_identify: Invalid item ID (%d)\n", itemid); + script_pushint(st, -2); + return true; + } + + int idx = -1; + ARR_FIND(0, sd->status.inventorySize, idx, (sd->status.inventory[idx].nameid == itemid && sd->status.inventory[idx].identify == 0)); + + if (idx < 0 || idx >= sd->status.inventorySize) { + script_pushint(st, -1); + return true; + } + + sd->status.inventory[idx].identify = 1; + clif->item_identified(sd, idx, 0); + script_pushint(st, idx); + + return true; +} + +/** + * identifyidx(idx) + * Identifies item at idx. + * Returns true if item is identified, false otherwise. + */ +static BUILDIN(identifyidx) +{ + struct map_session_data *sd = script_rid2sd(st); + + if (sd == NULL) { + script_pushint(st, false); + return true; + } + + int idx = script_getnum(st, 2); + if (idx < 0 || idx >= sd->status.inventorySize) { + ShowError("buildin_identifyidx: Invalid inventory index (%d), expected a value between 0 and %d\n", idx, sd->status.inventorySize); + script_pushint(st, false); + return true; + } + + if (sd->status.inventory[idx].nameid <= 0 || sd->status.inventory[idx].identify != 0) { + script_pushint(st, false); + return true; + } + + sd->status.inventory[idx].identify = 1; + clif->item_identified(sd, idx, 0); + script_pushint(st, true); + + return true; +} + +static BUILDIN(openlapineddukddakboxui) +{ + struct map_session_data *sd = script_rid2sd(st); + if (sd == NULL) + return false; + const int item_id = script_getnum(st, 2); + struct item_data *it = itemdb->exists(item_id); + if (it == NULL) { + ShowError("buildin_openlapineddukddakboxui: Item %d is not valid\n", item_id); + script->reportfunc(st); + script->reportsrc(st); + script_pushint(st, false); + return true; + } + clif->lapineDdukDdak_open(sd, item_id); + script_pushint(st, true); + return true; +} + +// Reset 'Feeling' maps. +BUILDIN(resetfeel) +{ + struct map_session_data *sd; + + if (script_hasdata(st, 2)) + sd = script->id2sd(st, script_getnum(st, 2)); + else + sd = script->rid2sd(st); + + if (sd != NULL) + pc->resetfeel(sd); + + return true; +} + +// Reset hatred target marks. +BUILDIN(resethate) +{ + struct map_session_data *sd; + + if (script_hasdata(st, 2)) + sd = script->id2sd(st, script_getnum(st, 2)); + else + sd = script->rid2sd(st); + + if (sd != NULL) + pc->resethate(sd); + + return true; +} + +/** * Adds a built-in script function. * * @param buildin Script function data @@ -20220,7 +26576,8 @@ BUILDIN(pcre_match) { * (i.e. a plugin overriding a built-in function) * @return Whether the function was successfully added. */ -bool script_add_builtin(const struct script_function *buildin, bool override) { +static bool script_add_builtin(const struct script_function *buildin, bool override) +{ int n = 0, offset = 0; size_t slen; if( !buildin ) { @@ -20272,8 +26629,10 @@ bool script_add_builtin(const struct script_function *buildin, bool override) { else if( strcmp(buildin->name, "callfunc") == 0 ) script->buildin_callfunc_ref = n; else if( strcmp(buildin->name, "getelementofarray") == 0 ) script->buildin_getelementofarray_ref = n; else if( strcmp(buildin->name, "mes") == 0 ) script->buildin_mes_offset = script->buildin_count; + else if( strcmp(buildin->name, "mesf") == 0 ) script->buildin_mesf_offset = script->buildin_count; else if( strcmp(buildin->name, "select") == 0 ) script->buildin_select_offset = script->buildin_count; else if( strcmp(buildin->name, "_") == 0 ) script->buildin_lang_macro_offset = script->buildin_count; + else if( strcmp(buildin->name, "_$") == 0 ) script->buildin_lang_macro_fmtstring_offset = script->buildin_count; offset = script->buildin_count; @@ -20299,7 +26658,8 @@ bool script_add_builtin(const struct script_function *buildin, bool override) { return true; } -bool script_hp_add(char *name, char *args, bool (*func)(struct script_state *st), bool isDeprecated) { +static bool script_hp_add(char *name, char *args, bool (*func)(struct script_state *st), bool isDeprecated) +{ struct script_function buildin; buildin.name = name; buildin.arg = args; @@ -20308,7 +26668,7 @@ bool script_hp_add(char *name, char *args, bool (*func)(struct script_state *st) return script->add_builtin(&buildin, true); } -void script_run_use_script(struct map_session_data *sd, struct item_data *data, int oid) __attribute__((nonnull (1))); +static void script_run_use_script(struct map_session_data *sd, struct item_data *data, int oid) __attribute__((nonnull (1))); /** * Run use script for item. @@ -20317,14 +26677,15 @@ void script_run_use_script(struct map_session_data *sd, struct item_data *data, * @param n item index in inventory. Must be correct and checked before. * @param oid npc id. Can be also 0 or fake npc id. */ -void script_run_use_script(struct map_session_data *sd, struct item_data *data, int oid) +static void script_run_use_script(struct map_session_data *sd, struct item_data *data, int oid) { + nullpo_retv(data); script->current_item_id = data->nameid; script->run(data->script, 0, sd->bl.id, oid); script->current_item_id = 0; } -void script_run_item_equip_script(struct map_session_data *sd, struct item_data *data, int oid) __attribute__((nonnull (1, 2))); +static void script_run_item_equip_script(struct map_session_data *sd, struct item_data *data, int oid) __attribute__((nonnull (1, 2))); /** * Run item equip script for item. @@ -20333,14 +26694,14 @@ void script_run_item_equip_script(struct map_session_data *sd, struct item_data * @param data equipped item data. Must be correct and checked before. * @param oid npc id. Can be also 0 or fake npc id. */ -void script_run_item_equip_script(struct map_session_data *sd, struct item_data *data, int oid) +static void script_run_item_equip_script(struct map_session_data *sd, struct item_data *data, int oid) { script->current_item_id = data->nameid; script->run(data->equip_script, 0, sd->bl.id, oid); script->current_item_id = 0; } -void script_run_item_unequip_script(struct map_session_data *sd, struct item_data *data, int oid) __attribute__((nonnull (1, 2))); +static void script_run_item_unequip_script(struct map_session_data *sd, struct item_data *data, int oid) __attribute__((nonnull (1, 2))); /** * Run item unequip script for item. @@ -20349,31 +26710,80 @@ void script_run_item_unequip_script(struct map_session_data *sd, struct item_dat * @param data unequipped item data. Must be correct and checked before. * @param oid npc id. Can be also 0 or fake npc id. */ -void script_run_item_unequip_script(struct map_session_data *sd, struct item_data *data, int oid) +static void script_run_item_unequip_script(struct map_session_data *sd, struct item_data *data, int oid) { script->current_item_id = data->nameid; script->run(data->unequip_script, 0, sd->bl.id, oid); script->current_item_id = 0; } +static void script_run_item_rental_start_script(struct map_session_data *sd, struct item_data *data, int oid) __attribute__((nonnull(1, 2))); + +/** + * Run item rental start script + * @param sd player session data. Must be correct and checked before. + * @param data rental item data. Must be correct and checked before. + * @param oid npc id. Can be also 0 or fake npc id. + **/ +static void script_run_item_rental_start_script(struct map_session_data *sd, struct item_data *data, int oid) +{ + script->current_item_id = data->nameid; + script->run(data->rental_start_script, 0, sd->bl.id, oid); + script->current_item_id = 0; +} + +static void script_run_item_rental_end_script(struct map_session_data *sd, struct item_data *data, int oid) __attribute__((nonnull(1, 2))); + +/** +* Run item rental end script +* @param sd player session data. Must be correct and checked before. +* @param data rental item data. Must be correct and checked before. +* @param oid npc id. Can be also 0 or fake npc id. +**/ +static void script_run_item_rental_end_script(struct map_session_data *sd, struct item_data *data, int oid) +{ + script->current_item_id = data->nameid; + script->run(data->rental_end_script, 0, sd->bl.id, oid); + script->current_item_id = 0; +} + +static void script_run_item_lapineddukddak_script(struct map_session_data *sd, struct item_data *data, int oid) __attribute__((nonnull (1, 2))); + +/** + * Run item lapineddukddak script for item. + * + * @param sd player session data. Must be correct and checked before. + * @param data unequipped item data. Must be correct and checked before. + * @param oid npc id. Can be also 0 or fake npc id. + */ +static void script_run_item_lapineddukddak_script(struct map_session_data *sd, struct item_data *data, int oid) +{ + script->current_item_id = data->nameid; + script->run(data->lapineddukddak->script, 0, sd->bl.id, oid); + script->current_item_id = 0; +} + #define BUILDIN_DEF(x,args) { buildin_ ## x , #x , args, false } #define BUILDIN_DEF2(x,x2,args) { buildin_ ## x , x2 , args, false } #define BUILDIN_DEF_DEPRECATED(x,args) { buildin_ ## x , #x , args, true } #define BUILDIN_DEF2_DEPRECATED(x,x2,args) { buildin_ ## x , x2 , args, true } -void script_parse_builtin(void) { +static void script_parse_builtin(void) +{ struct script_function BUILDIN[] = { /* Commands for internal use by the script engine */ BUILDIN_DEF(__jump_zero,"il"), BUILDIN_DEF(__setr,"rv?"), // NPC interaction - BUILDIN_DEF(mes,"s*"), + BUILDIN_DEF(mes, "?"), + BUILDIN_DEF(mesf, "s*"), BUILDIN_DEF(next,""), + BUILDIN_DEF(mesclear,""), 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*"), @@ -20386,8 +26796,8 @@ void script_parse_builtin(void) { BUILDIN_DEF(warp,"sii?"), BUILDIN_DEF(areawarp,"siiiisii??"), BUILDIN_DEF(warpchar,"siii"), // [LuzZza] - BUILDIN_DEF(warpparty,"siii?"), // [Fredzilla] [Paradox924X] - BUILDIN_DEF(warpguild,"siii"), // [Fredzilla] + BUILDIN_DEF(warpparty,"siii???"), // [Fredzilla] [Paradox924X] [Jedzkie] [Dastgir] + BUILDIN_DEF(warpguild,"siii??"), // [Fredzilla] BUILDIN_DEF(setlook,"ii"), BUILDIN_DEF(changelook,"ii"), // Simulates but don't Store it BUILDIN_DEF2(__setr,"set","rv"), @@ -20395,6 +26805,7 @@ void script_parse_builtin(void) { BUILDIN_DEF(cleararray,"rvi"), BUILDIN_DEF(copyarray,"rri"), BUILDIN_DEF(getarraysize,"r"), + BUILDIN_DEF(getarrayindex,"r"), BUILDIN_DEF(deletearray,"r?"), BUILDIN_DEF(getelementofarray,"ri"), BUILDIN_DEF(getitem,"vi?"), @@ -20403,8 +26814,10 @@ void script_parse_builtin(void) { BUILDIN_DEF(getnameditem,"vv"), BUILDIN_DEF2(grouprandomitem,"groupranditem","i"), BUILDIN_DEF(makeitem,"visii"), + BUILDIN_DEF(makeitem2,"viiiiiiii????"), BUILDIN_DEF(delitem,"vi?"), BUILDIN_DEF(delitem2,"viiiiiiii?"), + BUILDIN_DEF(delitemidx, "i??"), BUILDIN_DEF2(enableitemuse,"enable_items",""), BUILDIN_DEF2(disableitemuse,"disable_items",""), BUILDIN_DEF(cutin,"si"), @@ -20415,20 +26828,24 @@ void script_parse_builtin(void) { BUILDIN_DEF(rand,"i?"), BUILDIN_DEF(countitem,"v"), BUILDIN_DEF(countitem2,"viiiiiii"), + BUILDIN_DEF(countnameditem,"v?"), BUILDIN_DEF(checkweight,"vi*"), BUILDIN_DEF(checkweight2,"rr"), 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?"), - BUILDIN_DEF(getguildname,"i"), - BUILDIN_DEF(getguildmaster,"i"), - BUILDIN_DEF(getguildmasterid,"i"), + BUILDIN_DEF_DEPRECATED(getguildname,"i"), + BUILDIN_DEF_DEPRECATED(getguildmaster,"i"), + BUILDIN_DEF_DEPRECATED(getguildmasterid,"i"), BUILDIN_DEF(getguildmember,"i?"), - BUILDIN_DEF(strcharinfo,"i"), - BUILDIN_DEF(strnpcinfo,"i"), + BUILDIN_DEF(getguildinfo,"i?"), + BUILDIN_DEF(getguildonline, "i?"), + BUILDIN_DEF(strcharinfo,"i??"), + BUILDIN_DEF(strnpcinfo,"i??"), BUILDIN_DEF(charid2rid,"i"), BUILDIN_DEF(getequipid,"i"), BUILDIN_DEF(getequipname,"i"), @@ -20439,14 +26856,15 @@ void script_parse_builtin(void) { BUILDIN_DEF(getequipisequiped,"i"), BUILDIN_DEF(getequipisenableref,"i"), BUILDIN_DEF(getequipisidentify,"i"), - BUILDIN_DEF(getequiprefinerycnt,"i"), + BUILDIN_DEF(getequiprefinerycnt,"i*"), BUILDIN_DEF(getequipweaponlv,"i"), - BUILDIN_DEF(getequippercentrefinery,"i"), + BUILDIN_DEF(getequippercentrefinery,"i?"), BUILDIN_DEF(successrefitem,"i?"), BUILDIN_DEF(failedrefitem,"i"), 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"), @@ -20463,21 +26881,21 @@ void script_parse_builtin(void) { BUILDIN_DEF(basicskillcheck,""), BUILDIN_DEF(getgmlevel,""), BUILDIN_DEF(setgroupid, "i?"), - BUILDIN_DEF(getgroupid,""), + BUILDIN_DEF(getgroupid,"?"), BUILDIN_DEF(end,""), - BUILDIN_DEF(checkoption,"i"), - BUILDIN_DEF(setoption,"i?"), + BUILDIN_DEF(checkoption,"i?"), + BUILDIN_DEF(setoption,"i??"), BUILDIN_DEF(setcart,"?"), BUILDIN_DEF(checkcart,""), BUILDIN_DEF(setfalcon,"?"), BUILDIN_DEF(checkfalcon,""), - BUILDIN_DEF(setmount,"?"), + BUILDIN_DEF(setmount,"??"), BUILDIN_DEF(checkmount,""), BUILDIN_DEF(checkwug,""), BUILDIN_DEF(savepoint,"sii"), BUILDIN_DEF(gettimetick,"i"), BUILDIN_DEF(gettime,"i"), - BUILDIN_DEF(gettimestr,"si"), + BUILDIN_DEF(gettimestr, "si?"), BUILDIN_DEF(openstorage,""), BUILDIN_DEF(guildopenstorage,""), BUILDIN_DEF(itemskill,"vi?"), @@ -20488,12 +26906,15 @@ void script_parse_builtin(void) { BUILDIN_DEF(areamonster,"siiiisii???"), BUILDIN_DEF(killmonster,"ss?"), BUILDIN_DEF(killmonsterall,"s?"), + BUILDIN_DEF(killmonstergid, "i"), BUILDIN_DEF(clone,"siisi????"), BUILDIN_DEF(doevent,"s"), BUILDIN_DEF(donpcevent,"s"), - BUILDIN_DEF(addtimer,"is"), - BUILDIN_DEF(deltimer,"s"), - BUILDIN_DEF(addtimercount,"si"), + BUILDIN_DEF(addtimer,"is?"), + BUILDIN_DEF(deltimer,"s?"), + BUILDIN_DEF(addtimercount,"si?"), + BUILDIN_DEF(gettimer,"i??"), + BUILDIN_DEF(getunits,"iri?????"), BUILDIN_DEF(initnpctimer,"??"), BUILDIN_DEF(stopnpctimer,"??"), BUILDIN_DEF(startnpctimer,"??"), @@ -20502,6 +26923,7 @@ void script_parse_builtin(void) { BUILDIN_DEF(attachnpctimer,"?"), // attached the player id to the npc timer [Celest] BUILDIN_DEF(detachnpctimer,"?"), // detached the player id from the npc timer [Celest] BUILDIN_DEF(playerattached,""), // returns id of the current attached player. [Skotlex] + BUILDIN_DEF(mobattached, ""), BUILDIN_DEF(announce,"si?????"), BUILDIN_DEF(mapannounce,"ssi?????"), BUILDIN_DEF(areaannounce,"siiiisi?????"), @@ -20514,18 +26936,23 @@ void script_parse_builtin(void) { BUILDIN_DEF(disablenpc,"s"), BUILDIN_DEF(hideoffnpc,"s"), BUILDIN_DEF(hideonnpc,"s"), + BUILDIN_DEF(cloakonnpc,"s?"), + BUILDIN_DEF(cloakoffnpc,"s?"), BUILDIN_DEF(sc_start,"iii???"), BUILDIN_DEF2(sc_start,"sc_start2","iiii???"), BUILDIN_DEF2(sc_start,"sc_start4","iiiiii???"), BUILDIN_DEF(sc_end,"i?"), BUILDIN_DEF(getstatus, "i?"), BUILDIN_DEF(getscrate,"ii?"), - BUILDIN_DEF(debugmes,"v"), + BUILDIN_DEF_DEPRECATED(debugmes,"v*"), + BUILDIN_DEF(consolemes,"iv*"), BUILDIN_DEF2(catchpet,"pet","i"), BUILDIN_DEF2(birthpet,"bpet",""), BUILDIN_DEF(resetlvl,"i"), BUILDIN_DEF(resetstatus,""), BUILDIN_DEF(resetskill,""), + BUILDIN_DEF(resetfeel, "?"), + BUILDIN_DEF(resethate, "?"), BUILDIN_DEF(skillpointcount,""), BUILDIN_DEF(changebase,"i?"), BUILDIN_DEF(changesex,""), @@ -20542,6 +26969,7 @@ void script_parse_builtin(void) { BUILDIN_DEF(isloggedin,"i?"), BUILDIN_DEF(setmapflagnosave,"ssii"), BUILDIN_DEF(getmapflag,"si"), + BUILDIN_DEF(getmapinfo,"i?"), BUILDIN_DEF(setmapflag,"si?"), BUILDIN_DEF(removemapflag,"si"), BUILDIN_DEF(pvpon,"s"), @@ -20578,8 +27006,8 @@ void script_parse_builtin(void) { BUILDIN_DEF(getcartinventorylist,""), BUILDIN_DEF(getskilllist,""), BUILDIN_DEF(clearitem,""), - BUILDIN_DEF(classchange,"ii"), - BUILDIN_DEF(misceffect,"i"), + BUILDIN_DEF(classchange,"ii?"), + BUILDIN_DEF_DEPRECATED(misceffect,"i"), BUILDIN_DEF(playbgm,"s"), BUILDIN_DEF(playbgmall,"s?????"), BUILDIN_DEF(soundeffect,"si"), @@ -20594,15 +27022,18 @@ void script_parse_builtin(void) { BUILDIN_DEF(petskillsupport,"viiii"), // [Skotlex] 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(specialeffect2,"i??"), // skill effect on players[Valaris] + BUILDIN_DEF(specialeffect,"i???"), // npc skill effect [Valaris] + BUILDIN_DEF(specialeffectnum,"iii???"), // npc skill effect with num [4144] + 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 BUILDIN_DEF(atcommand,"s"), // [MouseJstr] BUILDIN_DEF2(atcommand,"charcommand","s"), // [MouseJstr] BUILDIN_DEF(movenpc,"sii?"), // [MouseJstr] BUILDIN_DEF(message,"vs"), // [MouseJstr] - BUILDIN_DEF(npctalk,"s?"), // [Valaris] + BUILDIN_DEF(servicemessage, "si?"), + BUILDIN_DEF(npctalk,"s??"), // [Valaris][Murilo BiO] BUILDIN_DEF(mobcount,"ss"), BUILDIN_DEF(getlook,"i"), BUILDIN_DEF(getsavepoint,"i"), @@ -20614,11 +27045,11 @@ void script_parse_builtin(void) { BUILDIN_DEF(setnpcdir,"*"), // [4144] BUILDIN_DEF(getnpcclass,"?"), // [4144] BUILDIN_DEF(getmapxy,"rrri?"), //by Lorky [Lupus] - BUILDIN_DEF(checkoption1,"i"), - BUILDIN_DEF(checkoption2,"i"), + BUILDIN_DEF(checkoption1,"i?"), + BUILDIN_DEF(checkoption2,"i?"), BUILDIN_DEF(guildgetexp,"i"), BUILDIN_DEF(guildchangegm,"is"), - BUILDIN_DEF(logmes,"s"), //this command actls as MES but rints info into LOG file either SQL/TXT [Lupus] + BUILDIN_DEF(logmes,"s?"), //this command actls as MES but rints info into LOG file either SQL/TXT [Lupus] BUILDIN_DEF(summon,"si??"), // summons a slave monster [Celest] BUILDIN_DEF(isnight,""), // check whether it is night time [Celest] BUILDIN_DEF(isequipped,"i*"), // check whether another item/card has been equipped [Celest] @@ -20631,10 +27062,11 @@ void script_parse_builtin(void) { BUILDIN_DEF(activatepset,"i"), // Activate a pattern set [MouseJstr] BUILDIN_DEF(deactivatepset,"i"), // Deactive a pattern set [MouseJstr] BUILDIN_DEF(deletepset,"i"), // Delete a pattern set [MouseJstr] + BUILDIN_DEF(dressroom,"?"), BUILDIN_DEF(pcre_match,"ss"), BUILDIN_DEF(dispbottom,"s?"), //added from jA [Lupus] BUILDIN_DEF(getusersname,""), - BUILDIN_DEF(recovery,""), + BUILDIN_DEF(recovery,"?????"), BUILDIN_DEF(getpetinfo,"i"), BUILDIN_DEF(gethominfo,"i"), BUILDIN_DEF(getmercinfo,"i?"), @@ -20644,6 +27076,12 @@ void script_parse_builtin(void) { BUILDIN_DEF(getstrlen,"s"), //strlen [Valaris] BUILDIN_DEF(charisalpha,"si"), //isalpha [Valaris] 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"), BUILDIN_DEF(insertchar,"ssi"), BUILDIN_DEF(delchar,"si"), @@ -20662,24 +27100,29 @@ void script_parse_builtin(void) { BUILDIN_DEF(setnpcdisplay,"sv??"), BUILDIN_DEF(compare,"ss"), // Lordalfa - To bring strstr to scripting Engine. BUILDIN_DEF(strcmp,"ss"), - BUILDIN_DEF(getiteminfo,"ii"), //[Lupus] returns Items Buy / sell Price, etc info + BUILDIN_DEF(getiteminfo,"vi"), //[Lupus] returns Items Buy / sell Price, etc info BUILDIN_DEF(setiteminfo,"iii"), //[Lupus] set Items Buy / sell Price, etc info BUILDIN_DEF(getequipcardid,"ii"), //[Lupus] returns CARD ID or other info from CARD slot N of equipped item + BUILDIN_DEF(getequippedoptioninfo, "i"), + BUILDIN_DEF(getequipoption, "iii"), + BUILDIN_DEF(setequipoption, "iiii"), + BUILDIN_DEF(getequipisenableopt, "i"), // List of mathematics commands ---> BUILDIN_DEF(log10,"i"), BUILDIN_DEF(sqrt,"i"), //[zBuffer] - BUILDIN_DEF(pow,"ii"), //[zBuffer] + BUILDIN_DEF_DEPRECATED(pow,"ii"), //[zBuffer] BUILDIN_DEF(distance,"iiii"), //[zBuffer] // <--- List of mathematics commands BUILDIN_DEF(min, "i*"), BUILDIN_DEF(max, "i*"), + BUILDIN_DEF(cap_value, "iii"), BUILDIN_DEF(md5,"s"), BUILDIN_DEF(swap,"rr"), // [zBuffer] List of dynamic var commands ---> BUILDIN_DEF(getd,"s"), BUILDIN_DEF(setd,"sv"), // <--- [zBuffer] List of dynamic var commands - BUILDIN_DEF(petstat,"i"), + BUILDIN_DEF_DEPRECATED(petstat, "i"), // Deprecated 2019-03-11 BUILDIN_DEF(callshop,"s?"), // [Skotlex] BUILDIN_DEF(npcshopitem,"sii*"), // [Lance] BUILDIN_DEF(npcshopadditem,"sii*"), @@ -20706,16 +27149,26 @@ void script_parse_builtin(void) { BUILDIN_DEF(rid2name,"i"), BUILDIN_DEF(pcfollow,"ii"), BUILDIN_DEF(pcstopfollow,"i"), - BUILDIN_DEF(pcblockmove,"ii"), + BUILDIN_DEF_DEPRECATED(pcblockmove,"ii"), // Deprecated 2018-05-04 + BUILDIN_DEF(setpcblock, "ii?"), + BUILDIN_DEF(checkpcblock, "?"), // <--- [zBuffer] List of player cont commands // [zBuffer] List of mob control commands ---> BUILDIN_DEF(getunittype,"i"), + /* Unit Data */ + BUILDIN_DEF(setunitdata,"iiv??"), + BUILDIN_DEF(getunitdata,"ii?"), + BUILDIN_DEF(getunitname,"i"), + BUILDIN_DEF(setunitname,"is"), + BUILDIN_DEF(getunittitle,"i"), + BUILDIN_DEF(setunittitle,"is"), BUILDIN_DEF(unitwalk,"ii?"), + BUILDIN_DEF(unitiswalking, "?"), BUILDIN_DEF(unitkill,"i"), BUILDIN_DEF(unitwarp,"isii"), BUILDIN_DEF(unitattack,"iv?"), BUILDIN_DEF(unitstop,"i"), - BUILDIN_DEF(unittalk,"is"), + BUILDIN_DEF(unittalk,"is???"), BUILDIN_DEF(unitemote,"ii"), BUILDIN_DEF(unitskilluseid,"ivi?"), // originally by Qamera [Celest] BUILDIN_DEF(unitskillusepos,"iviii"), // [Celest] @@ -20724,6 +27177,7 @@ void script_parse_builtin(void) { BUILDIN_DEF(sleep2,"i"), BUILDIN_DEF(awake,"s"), BUILDIN_DEF(getvariableofnpc,"rs"), + BUILDIN_DEF(getvariableofpc,"ri?"), BUILDIN_DEF(warpportal,"iisii"), BUILDIN_DEF2(homunculus_evolution,"homevolution",""), //[orn] BUILDIN_DEF2(homunculus_mutate,"hommutate","?"), @@ -20751,16 +27205,23 @@ void script_parse_builtin(void) { BUILDIN_DEF(mercenary_set_faith,"ii"), BUILDIN_DEF(readbook,"ii"), BUILDIN_DEF(setfont,"i"), + BUILDIN_DEF(getfont, ""), BUILDIN_DEF(areamobuseskill,"siiiiviiiii"), BUILDIN_DEF(progressbar,"si"), + BUILDIN_DEF(progressbar_unit,"si?"), BUILDIN_DEF(pushpc,"ii"), 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?"), + BUILDIN_DEF(achievement_iscompleted, "i?"), // BattleGround BUILDIN_DEF(waitingroom2bg,"siiss?"), BUILDIN_DEF(waitingroom2bg_single,"isiis"), @@ -20819,7 +27280,10 @@ void script_parse_builtin(void) { **/ BUILDIN_DEF(bindatcmd, "ss???"), BUILDIN_DEF(unbindatcmd, "s"), - BUILDIN_DEF(useatcmd, "s"), + BUILDIN_DEF_DEPRECATED(useatcmd, "s"), + BUILDIN_DEF(has_permission, "v?"), + BUILDIN_DEF(can_use_command, "s?"), + BUILDIN_DEF(add_group_command, "siii"), /** * Item bound [Xantara] [Akinari] [Mhalicot/Hercules] @@ -20830,8 +27294,9 @@ void script_parse_builtin(void) { BUILDIN_DEF(checkbound, "i???????"), //Quest Log System [Inkfish] - BUILDIN_DEF(questinfo, "ii??"), - BUILDIN_DEF(setquest, "i"), + BUILDIN_DEF(questinfo, "i?"), + BUILDIN_DEF(setquestinfo, "i???"), + BUILDIN_DEF(setquest, "i?"), BUILDIN_DEF(erasequest, "i?"), BUILDIN_DEF(completequest, "i?"), BUILDIN_DEF(questprogress, "i?"), @@ -20868,17 +27333,63 @@ 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(sellitemcurrency, "ii?"), + BUILDIN_DEF(startsellitem, "iii"), + BUILDIN_DEF(endsellitem, ""), + BUILDIN_DEF(stopselling,"i??"), BUILDIN_DEF(setcurrency,"i?"), BUILDIN_DEF(tradertype,"i"), BUILDIN_DEF(purchaseok,""), BUILDIN_DEF(shopcount, "i"), + /* Navigation */ + BUILDIN_DEF(navigateto, "s??????"), + + /* Clan System */ + BUILDIN_DEF(clan_join,"i?"), + BUILDIN_DEF(clan_leave,"?"), + BUILDIN_DEF(clan_master,"i"), + BUILDIN_DEF(channelmes, "ss"), - BUILDIN_DEF(showscript, "s?"), + BUILDIN_DEF(addchannelhandler, "ss"), + BUILDIN_DEF(removechannelhandler, "ss"), + BUILDIN_DEF(showscript, "s??"), BUILDIN_DEF(mergeitem,""), + BUILDIN_DEF(getcalendartime, "ii??"), + + // -- RoDEX + BUILDIN_DEF(rodex_sendmail, "isss???????????"), + BUILDIN_DEF2(rodex_sendmail, "rodex_sendmail_acc", "isss???????????"), + BUILDIN_DEF(rodex_sendmail2, "isss?????????????????????????????????????????"), + BUILDIN_DEF2(rodex_sendmail2, "rodex_sendmail_acc2", "isss?????????????????????????????????????????"), + BUILDIN_DEF(airship_respond, "i"), + BUILDIN_DEF(openstylist,""), BUILDIN_DEF(_,"s"), + BUILDIN_DEF2(_, "_$", "s"), + + // -- 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, ""), + + BUILDIN_DEF(closeroulette, ""), + BUILDIN_DEF(openrefineryui, ""), + BUILDIN_DEF(setfavoriteitemidx, "ii"), + BUILDIN_DEF(autofavoriteitem, "ii"), + + BUILDIN_DEF(identify, "i"), + BUILDIN_DEF(identifyidx, "i"), + BUILDIN_DEF(openlapineddukddakboxui, "i"), }; int i, len = ARRAYLENGTH(BUILDIN); RECREATE(script->buildin, char *, script->buildin_count + len); // Pre-alloc to speed up @@ -20890,7 +27401,8 @@ void script_parse_builtin(void) { #undef BUILDIN_DEF #undef BUILDIN_DEF2 -void script_label_add(int key, int pos) { +static void script_label_add(int key, int pos) +{ int idx = script->label_count; if( script->labels_size == script->label_count ) { @@ -20906,7 +27418,7 @@ void script_label_add(int key, int pos) { /** * Sets source-end constants for scripts to play with **/ -void script_hardcoded_constants(void) +static void script_hardcoded_constants(void) { script->constdb_comment("Boolean"); script->set_constant("true", 1, false, false); @@ -20917,12 +27429,20 @@ 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_ITEM_ID",MAX_ITEM_ID,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->set_constant("MOB_CLONE_START", MOB_CLONE_START, false, false); + script->set_constant("MOB_CLONE_END", MOB_CLONE_END, false, false); + script->set_constant("MAX_NPC_PER_MAP", MAX_NPC_PER_MAP, false, false); script->constdb_comment("status options"); script->set_constant("Option_Nothing",OPTION_NOTHING,false, false); @@ -20949,6 +27469,7 @@ void script_hardcoded_constants(void) script->set_constant("Option_Dragon5",OPTION_DRAGON5,false, false); script->set_constant("Option_Hanbok",OPTION_HANBOK,false, false); script->set_constant("Option_Oktoberfest",OPTION_OKTOBERFEST,false, false); + script->set_constant("Option_Summer2", OPTION_SUMMER2, false, false); script->constdb_comment("status option compounds"); script->set_constant("Option_Dragon",OPTION_DRAGON,false, false); @@ -21026,6 +27547,483 @@ void script_hardcoded_constants(void) script->set_constant("EQP_SHADOW_SHOES", EQP_SHADOW_SHOES, false, false); script->set_constant("EQP_SHADOW_ACC_R", EQP_SHADOW_ACC_R, false, false); script->set_constant("EQP_SHADOW_ACC_L", EQP_SHADOW_ACC_L, false, false); + // Synonyms and combined values + script->set_constant("EQP_WEAPON", EQP_WEAPON, false, false); + script->set_constant("EQP_SHIELD", EQP_SHIELD, false, false); + script->set_constant("EQP_ARMS", EQP_ARMS, false, false); + script->set_constant("EQP_HELM", EQP_HELM, false, false); + script->set_constant("EQP_ACC", EQP_ACC, false, false); + script->set_constant("EQP_COSTUME", EQP_COSTUME, false, false); + script->set_constant("EQP_SHADOW_ACC", EQP_SHADOW_ACC, false, false); + script->set_constant("EQP_SHADOW_ARMS", EQP_SHADOW_ARMS, false, false); + + script->constdb_comment("Item Option Types"); + script->set_constant("IT_OPT_INDEX", IT_OPT_INDEX, false, false); + script->set_constant("IT_OPT_VALUE", IT_OPT_VALUE, false, false); + script->set_constant("IT_OPT_PARAM", IT_OPT_PARAM, false, false); + + script->constdb_comment("Maximum Item Options"); + script->set_constant("MAX_ITEM_OPTIONS", MAX_ITEM_OPTIONS, false, false); + + script->constdb_comment("Navigation constants, use with *navigateto*"); + script->set_constant("NAV_NONE", NAV_NONE, false, false); + script->set_constant("NAV_AIRSHIP_ONLY", NAV_AIRSHIP_ONLY, false, false); + script->set_constant("NAV_SCROLL_ONLY", NAV_SCROLL_ONLY, false, false); + script->set_constant("NAV_AIRSHIP_AND_SCROLL", NAV_AIRSHIP_AND_SCROLL, false, false); + script->set_constant("NAV_KAFRA_ONLY", NAV_KAFRA_ONLY, false, false); + script->set_constant("NAV_KAFRA_AND_AIRSHIP", NAV_KAFRA_AND_AIRSHIP, false, false); + script->set_constant("NAV_KAFRA_AND_SCROLL", NAV_KAFRA_AND_SCROLL, false, false); + script->set_constant("NAV_ALL", NAV_ALL, false, false); + + script->constdb_comment("BL types"); + script->set_constant("BL_PC",BL_PC,false, false); + script->set_constant("BL_MOB",BL_MOB,false, false); + script->set_constant("BL_PET",BL_PET,false, false); + script->set_constant("BL_HOM",BL_HOM,false, false); + script->set_constant("BL_MER",BL_MER,false, false); + script->set_constant("BL_ITEM",BL_ITEM,false, false); + script->set_constant("BL_SKILL",BL_SKILL,false, false); + script->set_constant("BL_NPC",BL_NPC,false, false); + script->set_constant("BL_CHAT",BL_CHAT,false, false); + script->set_constant("BL_ELEM",BL_ELEM,false, false); + script->set_constant("BL_CHAR",BL_CHAR,false, false); + script->set_constant("BL_ALL",BL_ALL,false, false); + + script->constdb_comment("Refine Chance Types"); + script->set_constant("REFINE_CHANCE_TYPE_NORMAL", REFINE_CHANCE_TYPE_NORMAL, false, false); + script->set_constant("REFINE_CHANCE_TYPE_ENRICHED", REFINE_CHANCE_TYPE_ENRICHED, false, false); + script->set_constant("REFINE_CHANCE_TYPE_E_NORMAL", REFINE_CHANCE_TYPE_E_NORMAL, false, false); + script->set_constant("REFINE_CHANCE_TYPE_E_ENRICHED", REFINE_CHANCE_TYPE_E_ENRICHED, false, false); + + script->constdb_comment("Player permissions"); + script->set_constant("PERM_TRADE", PC_PERM_TRADE, false, false); + script->set_constant("PERM_PARTY", PC_PERM_PARTY, false, false); + script->set_constant("PERM_ALL_SKILL", PC_PERM_ALL_SKILL, false, false); + script->set_constant("PERM_USE_ALL_EQUIPMENT", PC_PERM_USE_ALL_EQUIPMENT, false, false); + script->set_constant("PERM_SKILL_UNCONDITIONAL", PC_PERM_SKILL_UNCONDITIONAL, false, false); + script->set_constant("PERM_JOIN_ALL_CHAT", PC_PERM_JOIN_ALL_CHAT, false, false); + script->set_constant("PERM_NO_CHAT_KICK", PC_PERM_NO_CHAT_KICK, false, false); + script->set_constant("PERM_HIDE_SESSION", PC_PERM_HIDE_SESSION, false, false); + script->set_constant("PERM_RECEIVE_HACK_INFO", PC_PERM_RECEIVE_HACK_INFO, false, false); + script->set_constant("PERM_WARP_ANYWHERE", PC_PERM_WARP_ANYWHERE, false, false); + script->set_constant("PERM_VIEW_HPMETER", PC_PERM_VIEW_HPMETER, false, false); + script->set_constant("PERM_VIEW_EQUIPMENT", PC_PERM_VIEW_EQUIPMENT, false, false); + script->set_constant("PERM_USE_CHECK", PC_PERM_USE_CHECK, false, false); + script->set_constant("PERM_USE_CHANGEMAPTYPE", PC_PERM_USE_CHANGEMAPTYPE, false, false); + script->set_constant("PERM_USE_ALL_COMMANDS", PC_PERM_USE_ALL_COMMANDS, false, false); + script->set_constant("PERM_RECEIVE_REQUESTS", PC_PERM_RECEIVE_REQUESTS, false, false); + script->set_constant("PERM_SHOW_BOSS", PC_PERM_SHOW_BOSS, false, false); + script->set_constant("PERM_DISABLE_PVM", PC_PERM_DISABLE_PVM, false, false); + script->set_constant("PERM_DISABLE_PVP", PC_PERM_DISABLE_PVP, false, false); + script->set_constant("PERM_DISABLE_CMD_DEAD", PC_PERM_DISABLE_CMD_DEAD, false, false); + script->set_constant("PERM_HCHSYS_ADMIN", PC_PERM_HCHSYS_ADMIN, false, false); + script->set_constant("PERM_TRADE_BOUND", PC_PERM_TRADE_BOUND, false, false); + script->set_constant("PERM_DISABLE_PICK_UP", PC_PERM_DISABLE_PICK_UP, false, false); + script->set_constant("PERM_DISABLE_STORE", PC_PERM_DISABLE_STORE, false, false); + script->set_constant("PERM_DISABLE_EXP", PC_PERM_DISABLE_EXP, false, false); + script->set_constant("PERM_DISABLE_SKILL_USAGE", PC_PERM_DISABLE_SKILL_USAGE, false, false); + script->set_constant("PERM_BYPASS_NOSTORAGE", PC_PERM_BYPASS_NOSTORAGE, false, false); + + script->constdb_comment("Data types"); + script->set_constant("DATATYPE_NIL", DATATYPE_NIL, false, false); + script->set_constant("DATATYPE_STR", DATATYPE_STR, false, false); + script->set_constant("DATATYPE_INT", DATATYPE_INT, false, false); + script->set_constant("DATATYPE_CONST", DATATYPE_CONST, false, false); + script->set_constant("DATATYPE_PARAM", DATATYPE_PARAM, false, false); + script->set_constant("DATATYPE_VAR", DATATYPE_VAR, false, false); + script->set_constant("DATATYPE_LABEL", DATATYPE_LABEL, false, false); + + script->constdb_comment("Logmes types"); + script->set_constant("LOGMES_NPC", LOGMES_NPC, false, false); + script->set_constant("LOGMES_ATCOMMAND", LOGMES_ATCOMMAND, false, false); + + script->constdb_comment("Item Subtypes (Weapon types)"); + script->set_constant("W_FIST", W_FIST, false, false); + script->set_constant("W_DAGGER", W_DAGGER, false, false); + script->set_constant("W_1HSWORD", W_1HSWORD, false, false); + script->set_constant("W_2HSWORD", W_2HSWORD, false, false); + script->set_constant("W_1HSPEAR", W_1HSPEAR, false, false); + script->set_constant("W_2HSPEAR", W_2HSPEAR, false, false); + script->set_constant("W_1HAXE", W_1HAXE, false, false); + script->set_constant("W_2HAXE", W_2HAXE, false, false); + script->set_constant("W_MACE", W_MACE, false, false); + script->set_constant("W_2HMACE", W_2HMACE, false, false); + script->set_constant("W_STAFF", W_STAFF, false, false); + script->set_constant("W_BOW", W_BOW, false, false); + script->set_constant("W_KNUCKLE", W_KNUCKLE, false, false); + script->set_constant("W_MUSICAL", W_MUSICAL, false, false); + script->set_constant("W_WHIP", W_WHIP, false, false); + script->set_constant("W_BOOK", W_BOOK, false, false); + script->set_constant("W_KATAR", W_KATAR, false, false); + script->set_constant("W_REVOLVER", W_REVOLVER, false, false); + script->set_constant("W_RIFLE", W_RIFLE, false, false); + script->set_constant("W_GATLING", W_GATLING, false, false); + script->set_constant("W_SHOTGUN", W_SHOTGUN, false, false); + script->set_constant("W_GRENADE", W_GRENADE, false, false); + script->set_constant("W_HUUMA", W_HUUMA, false, false); + script->set_constant("W_2HSTAFF", W_2HSTAFF, false, false); + + script->constdb_comment("Item Subtypes (Ammunition types)"); + script->set_constant("A_ARROW", A_ARROW, false, false); + script->set_constant("A_DAGGER", A_DAGGER, false, false); + script->set_constant("A_BULLET", A_BULLET, false, false); + script->set_constant("A_SHELL", A_SHELL, false, false); + script->set_constant("A_GRENADE", A_GRENADE, false, false); + script->set_constant("A_SHURIKEN", A_SHURIKEN, false, false); + script->set_constant("A_KUNAI", A_KUNAI, false, false); + script->set_constant("A_CANNONBALL", A_CANNONBALL, false, false); + script->set_constant("A_THROWWEAPON", A_THROWWEAPON, false, false); + + script->constdb_comment("Item Upper Masks"); + script->set_constant("ITEMUPPER_NONE", ITEMUPPER_NONE, false, false); + script->set_constant("ITEMUPPER_NORMAL", ITEMUPPER_NORMAL, false, false); + script->set_constant("ITEMUPPER_UPPER", ITEMUPPER_UPPER, false, false); + script->set_constant("ITEMUPPER_BABY", ITEMUPPER_BABY, false, false); + script->set_constant("ITEMUPPER_THIRD", ITEMUPPER_THIRD, false, false); + script->set_constant("ITEMUPPER_THIRDUPPER", ITEMUPPER_THIRDUPPER, false, false); + script->set_constant("ITEMUPPER_THIRDBABY", ITEMUPPER_THIRDBABY, false, false); + script->set_constant("ITEMUPPER_ALL", ITEMUPPER_ALL, false, false); + + script->constdb_comment("dressroom modes"); + script->set_constant("DRESSROOM_OPEN", DRESSROOM_OPEN, false, false); + script->set_constant("DRESSROOM_CLOSE", DRESSROOM_CLOSE, false, false); + + script->constdb_comment("getmapinfo options"); + script->set_constant("MAPINFO_NAME", MAPINFO_NAME, false, false); + script->set_constant("MAPINFO_ID", MAPINFO_ID, false, false); + script->set_constant("MAPINFO_SIZE_X", MAPINFO_SIZE_X, false, false); + script->set_constant("MAPINFO_SIZE_Y", MAPINFO_SIZE_Y, false, false); + script->set_constant("MAPINFO_ZONE", MAPINFO_ZONE, false, false); + script->set_constant("MAPINFO_NPC_COUNT", MAPINFO_NPC_COUNT, false, false); + + script->constdb_comment("consolemes options"); + script->set_constant("CONSOLEMES_DEBUG", CONSOLEMES_DEBUG, false, false); + script->set_constant("CONSOLEMES_ERROR", CONSOLEMES_ERROR, false, false); + script->set_constant("CONSOLEMES_WARNING", CONSOLEMES_WARNING, false, false); + script->set_constant("CONSOLEMES_INFO", CONSOLEMES_INFO, false, false); + script->set_constant("CONSOLEMES_STATUS", CONSOLEMES_STATUS, false, false); + script->set_constant("CONSOLEMES_NOTICE", CONSOLEMES_NOTICE, false, false); + + script->constdb_comment("set/getiteminfo options"); + script->set_constant("ITEMINFO_BUYPRICE", ITEMINFO_BUYPRICE, false, false); + script->set_constant("ITEMINFO_SELLPRICE", ITEMINFO_SELLPRICE, false, false); + script->set_constant("ITEMINFO_TYPE", ITEMINFO_TYPE, false, false); + script->set_constant("ITEMINFO_MAXCHANCE", ITEMINFO_MAXCHANCE, false, false); + script->set_constant("ITEMINFO_SEX", ITEMINFO_SEX, false, false); + script->set_constant("ITEMINFO_LOC", ITEMINFO_LOC, false, false); + script->set_constant("ITEMINFO_WEIGHT", ITEMINFO_WEIGHT, false, false); + script->set_constant("ITEMINFO_ATK", ITEMINFO_ATK, false, false); + script->set_constant("ITEMINFO_DEF", ITEMINFO_DEF, false, false); + script->set_constant("ITEMINFO_RANGE", ITEMINFO_RANGE, false, false); + script->set_constant("ITEMINFO_SLOTS", ITEMINFO_SLOTS, false, false); + script->set_constant("ITEMINFO_SUBTYPE", ITEMINFO_SUBTYPE, false, false); + script->set_constant("ITEMINFO_ELV", ITEMINFO_ELV, false, false); + script->set_constant("ITEMINFO_WLV", ITEMINFO_WLV, false, false); + 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->set_constant("ITEMINFO_ELV_MAX", ITEMINFO_ELV_MAX, false, false); + script->set_constant("ITEMINFO_DROPEFFECT_MODE", ITEMINFO_DROPEFFECT_MODE, false, false); + script->set_constant("ITEMINFO_DELAY", ITEMINFO_DELAY, false, false); + script->set_constant("ITEMINFO_CLASS_BASE_1", ITEMINFO_CLASS_BASE_1, false, false); + script->set_constant("ITEMINFO_CLASS_BASE_2", ITEMINFO_CLASS_BASE_2, false, false); + script->set_constant("ITEMINFO_CLASS_BASE_3", ITEMINFO_CLASS_BASE_3, false, false); + script->set_constant("ITEMINFO_CLASS_UPPER", ITEMINFO_CLASS_UPPER, false, false); + script->set_constant("ITEMINFO_FLAG_NO_REFINE", ITEMINFO_FLAG_NO_REFINE, false, false); + script->set_constant("ITEMINFO_FLAG_DELAY_CONSUME", ITEMINFO_FLAG_DELAY_CONSUME, false, false); + script->set_constant("ITEMINFO_FLAG_AUTOEQUIP", ITEMINFO_FLAG_AUTOEQUIP, false, false); + script->set_constant("ITEMINFO_FLAG_AUTO_FAVORITE", ITEMINFO_FLAG_AUTO_FAVORITE, false, false); + script->set_constant("ITEMINFO_FLAG_BUYINGSTORE", ITEMINFO_FLAG_BUYINGSTORE, false, false); + script->set_constant("ITEMINFO_FLAG_BINDONEQUIP", ITEMINFO_FLAG_BINDONEQUIP, false, false); + script->set_constant("ITEMINFO_FLAG_KEEPAFTERUSE", ITEMINFO_FLAG_KEEPAFTERUSE, false, false); + script->set_constant("ITEMINFO_FLAG_FORCE_SERIAL", ITEMINFO_FLAG_FORCE_SERIAL, false, false); + script->set_constant("ITEMINFO_FLAG_NO_OPTIONS", ITEMINFO_FLAG_NO_OPTIONS, false, false); + script->set_constant("ITEMINFO_FLAG_DROP_ANNOUNCE", ITEMINFO_FLAG_DROP_ANNOUNCE, false, false); + script->set_constant("ITEMINFO_FLAG_SHOWDROPEFFECT", ITEMINFO_FLAG_SHOWDROPEFFECT, false, false); + script->set_constant("ITEMINFO_STACK_AMOUNT", ITEMINFO_STACK_AMOUNT, false, false); + script->set_constant("ITEMINFO_STACK_FLAG", ITEMINFO_STACK_FLAG, false, false); + script->set_constant("ITEMINFO_ITEM_USAGE_FLAG", ITEMINFO_ITEM_USAGE_FLAG, false, false); + script->set_constant("ITEMINFO_ITEM_USAGE_OVERRIDE", ITEMINFO_ITEM_USAGE_OVERRIDE, false, false); + script->set_constant("ITEMINFO_GM_LV_TRADE_OVERRIDE", ITEMINFO_GM_LV_TRADE_OVERRIDE, false, false); + script->set_constant("ITEMINFO_ID", ITEMINFO_ID, false, false); + script->set_constant("ITEMINFO_AEGISNAME", ITEMINFO_AEGISNAME, false, false); + script->set_constant("ITEMINFO_NAME", ITEMINFO_NAME, false, false); + + script->constdb_comment("getmercinfo options"); + script->set_constant("MERCINFO_ID,", MERCINFO_ID, false, false); + script->set_constant("MERCINFO_CLASS", MERCINFO_CLASS, false, false); + script->set_constant("MERCINFO_NAME", MERCINFO_NAME, false, false); + script->set_constant("MERCINFO_FAITH", MERCINFO_FAITH, false, false); + script->set_constant("MERCINFO_CALLS", MERCINFO_CALLS, false, false); + script->set_constant("MERCINFO_KILLCOUNT", MERCINFO_KILLCOUNT, false, false); + script->set_constant("MERCINFO_LIFETIME", MERCINFO_LIFETIME, false, false); + script->set_constant("MERCINFO_LEVEL", MERCINFO_LEVEL, false, false); + script->set_constant("MERCINFO_GID", MERCINFO_GID, false, false); + + script->constdb_comment("getpetinfo options"); + script->set_constant("PETINFO_ID", PETINFO_ID, false, false); + script->set_constant("PETINFO_CLASS", PETINFO_CLASS, false, false); + script->set_constant("PETINFO_NAME", PETINFO_NAME, false, false); + script->set_constant("PETINFO_INTIMACY", PETINFO_INTIMACY, false, false); + script->set_constant("PETINFO_HUNGRY", PETINFO_HUNGRY, false, false); + script->set_constant("PETINFO_RENAME", PETINFO_RENAME, false, false); + script->set_constant("PETINFO_GID", PETINFO_GID, false, false); + script->set_constant("PETINFO_EGGITEM", PETINFO_EGGITEM, false, false); + script->set_constant("PETINFO_FOODITEM", PETINFO_FOODITEM, false, false); + script->set_constant("PETINFO_ACCESSORYITEM", PETINFO_ACCESSORYITEM, false, false); + script->set_constant("PETINFO_ACCESSORYFLAG", PETINFO_ACCESSORYFLAG, false, false); + script->set_constant("PETINFO_EVO_EGGID", PETINFO_EVO_EGGID, false, false); + script->set_constant("PETINFO_AUTOFEED", PETINFO_AUTOFEED, false, false); + + script->constdb_comment("Pet hunger levels"); + script->set_constant("PET_HUNGER_STARVING", PET_HUNGER_STARVING, false, false); + script->set_constant("PET_HUNGER_VERY_HUNGRY", PET_HUNGER_VERY_HUNGRY, false, false); + script->set_constant("PET_HUNGER_HUNGRY", PET_HUNGER_HUNGRY, false, false); + script->set_constant("PET_HUNGER_NEUTRAL", PET_HUNGER_NEUTRAL, false, false); + script->set_constant("PET_HUNGER_SATISFIED", PET_HUNGER_SATISFIED, false, false); + script->set_constant("PET_HUNGER_STUFFED", PET_HUNGER_STUFFED, false, false); + + script->constdb_comment("Pet intimacy levels"); + script->set_constant("PET_INTIMACY_NONE", PET_INTIMACY_NONE, false, false); + script->set_constant("PET_INTIMACY_AWKWARD", PET_INTIMACY_AWKWARD, false, false); + script->set_constant("PET_INTIMACY_SHY", PET_INTIMACY_SHY, false, false); + script->set_constant("PET_INTIMACY_NEUTRAL", PET_INTIMACY_NEUTRAL, false, false); + script->set_constant("PET_INTIMACY_CORDIAL", PET_INTIMACY_CORDIAL, false, false); + script->set_constant("PET_INTIMACY_LOYAL", PET_INTIMACY_LOYAL, false, false); + script->set_constant("PET_INTIMACY_MAX", PET_INTIMACY_MAX, false, false); + + script->constdb_comment("monster skill states"); + script->set_constant("MSS_ANY", MSS_ANY, false, false); + script->set_constant("MSS_IDLE", MSS_IDLE, false, false); + script->set_constant("MSS_WALK", MSS_WALK, false, false); + script->set_constant("MSS_LOOT", MSS_LOOT, false, false); + script->set_constant("MSS_DEAD", MSS_DEAD, false, false); + script->set_constant("MSS_BERSERK", MSS_BERSERK, false, false); + script->set_constant("MSS_ANGRY", MSS_ANGRY, false, false); + script->set_constant("MSS_RUSH", MSS_RUSH, false, false); + script->set_constant("MSS_FOLLOW", MSS_FOLLOW, false, false); + script->set_constant("MSS_ANYTARGET", MSS_ANYTARGET, false, false); + + script->constdb_comment("monster skill conditions"); + script->set_constant("MSC_ANY", -1, false, false); + script->set_constant("MSC_ALWAYS", MSC_ALWAYS, false, false); + script->set_constant("MSC_MYHPLTMAXRATE", MSC_MYHPLTMAXRATE, false, false); + script->set_constant("MSC_MYHPINRATE", MSC_MYHPINRATE, false, false); + script->set_constant("MSC_FRIENDHPLTMAXRATE", MSC_FRIENDHPLTMAXRATE, false, false); + script->set_constant("MSC_FRIENDHPINRATE", MSC_FRIENDHPINRATE, false, false); + script->set_constant("MSC_MYSTATUSON", MSC_MYSTATUSON, false, false); + script->set_constant("MSC_MYSTATUSOFF", MSC_MYSTATUSOFF, false, false); + script->set_constant("MSC_FRIENDSTATUSON", MSC_FRIENDSTATUSON, false, false); + script->set_constant("MSC_FRIENDSTATUSOFF", MSC_FRIENDSTATUSOFF, false, false); + script->set_constant("MSC_ATTACKPCGT", MSC_ATTACKPCGT, false, false); + script->set_constant("MSC_ATTACKPCGE", MSC_ATTACKPCGE, false, false); + script->set_constant("MSC_SLAVELT", MSC_SLAVELT, false, false); + script->set_constant("MSC_SLAVELE", MSC_SLAVELE, false, false); + script->set_constant("MSC_CLOSEDATTACKED", MSC_CLOSEDATTACKED, false, false); + script->set_constant("MSC_LONGRANGEATTACKED", MSC_LONGRANGEATTACKED, false, false); + script->set_constant("MSC_SKILLUSED", MSC_SKILLUSED, false, false); + script->set_constant("MSC_AFTERSKILL", MSC_AFTERSKILL, false, false); + script->set_constant("MSC_CASTTARGETED", MSC_CASTTARGETED, false, false); + script->set_constant("MSC_RUDEATTACKED", MSC_RUDEATTACKED, false, false); + script->set_constant("MSC_MASTERHPLTMAXRATE", MSC_MASTERHPLTMAXRATE, false, false); + script->set_constant("MSC_MASTERATTACKED", MSC_MASTERATTACKED, false, false); + script->set_constant("MSC_ALCHEMIST", MSC_ALCHEMIST, false, false); + script->set_constant("MSC_SPAWN", MSC_SPAWN, false, false); + + script->constdb_comment("monster skill targets"); + script->set_constant("MST_TARGET", MST_TARGET, false, false); + script->set_constant("MST_RANDOM", MST_RANDOM , false, false); + script->set_constant("MST_SELF", MST_SELF, false, false); + script->set_constant("MST_FRIEND", MST_FRIEND , false, false); + script->set_constant("MST_MASTER", MST_MASTER , false, false); + script->set_constant("MST_AROUND5", MST_AROUND5, false, false); + script->set_constant("MST_AROUND6", MST_AROUND6, false, false); + script->set_constant("MST_AROUND7", MST_AROUND7, false, false); + script->set_constant("MST_AROUND8", MST_AROUND8, false, false); + script->set_constant("MST_AROUND1", MST_AROUND1, false, false); + script->set_constant("MST_AROUND2", MST_AROUND2, false, false); + script->set_constant("MST_AROUND3", MST_AROUND3, false, false); + script->set_constant("MST_AROUND4", MST_AROUND4, false, false); + script->set_constant("MST_AROUND", MST_AROUND , false, false); + + script->constdb_comment("Monster group constants"); + script->set_constant("ALL_MOBS_NONBOSS", ALL_MOBS_NONBOSS, false, false); + script->set_constant("ALL_MOBS_BOSS", ALL_MOBS_BOSS, false, false); + script->set_constant("ALL_MOBS", ALL_MOBS, false, false); + + script->constdb_comment("pc block constants, use with *setpcblock* and *checkpcblock*"); + script->set_constant("PCBLOCK_NONE", PCBLOCK_NONE, false, false); + script->set_constant("PCBLOCK_MOVE", PCBLOCK_MOVE, false, false); + script->set_constant("PCBLOCK_ATTACK", PCBLOCK_ATTACK, false, false); + script->set_constant("PCBLOCK_SKILL", PCBLOCK_SKILL, false, false); + script->set_constant("PCBLOCK_USEITEM", PCBLOCK_USEITEM, false, false); + script->set_constant("PCBLOCK_CHAT", PCBLOCK_CHAT, false, false); + script->set_constant("PCBLOCK_IMMUNE", PCBLOCK_IMMUNE, false, false); + script->set_constant("PCBLOCK_SITSTAND", PCBLOCK_SITSTAND, false, false); + script->set_constant("PCBLOCK_COMMANDS", PCBLOCK_COMMANDS, false, false); + script->set_constant("PCBLOCK_NPC", PCBLOCK_NPC, false, false); + + script->constdb_comment("private airship responds"); + script->set_constant("P_AIRSHIP_NONE", P_AIRSHIP_NONE, false, false); + script->set_constant("P_AIRSHIP_RETRY", P_AIRSHIP_RETRY, false, false); + script->set_constant("P_AIRSHIP_INVALID_START_MAP", P_AIRSHIP_INVALID_START_MAP, false, false); + script->set_constant("P_AIRSHIP_INVALID_END_MAP", P_AIRSHIP_INVALID_END_MAP, false, false); + script->set_constant("P_AIRSHIP_ITEM_NOT_ENOUGH", P_AIRSHIP_ITEM_NOT_ENOUGH, false, false); + script->set_constant("P_AIRSHIP_ITEM_INVALID", P_AIRSHIP_ITEM_INVALID, false, false); + + script->constdb_comment("questinfo types"); + script->set_constant("QINFO_JOB", QINFO_JOB, false, false); + script->set_constant("QINFO_SEX", QINFO_SEX, false, false); + script->set_constant("QINFO_BASE_LEVEL", QINFO_BASE_LEVEL, false, false); + script->set_constant("QINFO_JOB_LEVEL", QINFO_JOB_LEVEL, false, false); + script->set_constant("QINFO_ITEM", QINFO_ITEM, false, false); + 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->set_constant("NST_EXPANDED_BARTER", NST_EXPANDED_BARTER, false, false); + + script->constdb_comment("script unit data types"); + script->set_constant("UDT_TYPE", UDT_TYPE, false, false); + script->set_constant("UDT_SIZE", UDT_SIZE, false, false); + script->set_constant("UDT_LEVEL", UDT_LEVEL, false, false); + script->set_constant("UDT_HP", UDT_HP, false, false); + script->set_constant("UDT_MAXHP", UDT_MAXHP, false, false); + script->set_constant("UDT_SP", UDT_SP, false, false); + script->set_constant("UDT_MAXSP", UDT_MAXSP, false, false); + script->set_constant("UDT_MASTERAID", UDT_MASTERAID, false, false); + script->set_constant("UDT_MASTERCID", UDT_MASTERCID, false, false); + script->set_constant("UDT_MAPIDXY", UDT_MAPIDXY, false, true); // for setunitdata use *unitwarp, for getunitdata use *getmapxy + script->set_constant("UDT_WALKTOXY", UDT_WALKTOXY, false, true); // use *unitwalk + script->set_constant("UDT_SPEED", UDT_SPEED, false, false); + script->set_constant("UDT_MODE", UDT_MODE, false, false); + script->set_constant("UDT_AI", UDT_AI, false, false); + script->set_constant("UDT_SCOPTION", UDT_SCOPTION, false, false); + script->set_constant("UDT_SEX", UDT_SEX, false, false); + script->set_constant("UDT_CLASS", UDT_CLASS, false, false); + script->set_constant("UDT_HAIRSTYLE", UDT_HAIRSTYLE, false, false); + script->set_constant("UDT_HAIRCOLOR", UDT_HAIRCOLOR, false, false); + script->set_constant("UDT_HEADBOTTOM", UDT_HEADBOTTOM, false, false); + script->set_constant("UDT_HEADMIDDLE", UDT_HEADMIDDLE, false, false); + script->set_constant("UDT_HEADTOP", UDT_HEADTOP, false, false); + script->set_constant("UDT_CLOTHCOLOR", UDT_CLOTHCOLOR, false, false); + script->set_constant("UDT_SHIELD", UDT_SHIELD, false, false); + script->set_constant("UDT_WEAPON", UDT_WEAPON, false, false); + script->set_constant("UDT_LOOKDIR", UDT_LOOKDIR, false, false); + script->set_constant("UDT_CANMOVETICK", UDT_CANMOVETICK, false, false); + script->set_constant("UDT_STR", UDT_STR, false, false); + script->set_constant("UDT_AGI", UDT_AGI, false, false); + script->set_constant("UDT_VIT", UDT_VIT, false, false); + script->set_constant("UDT_INT", UDT_INT, false, false); + script->set_constant("UDT_DEX", UDT_DEX, false, false); + script->set_constant("UDT_LUK", UDT_LUK, false, false); + script->set_constant("UDT_ATKRANGE", UDT_ATKRANGE, false, false); + script->set_constant("UDT_ATKMIN", UDT_ATKMIN, false, false); + script->set_constant("UDT_ATKMAX", UDT_ATKMAX, false, false); + script->set_constant("UDT_MATKMIN", UDT_MATKMIN, false, false); + script->set_constant("UDT_MATKMAX", UDT_MATKMAX, false, false); + script->set_constant("UDT_DEF", UDT_DEF, false, false); + script->set_constant("UDT_MDEF", UDT_MDEF, false, false); + script->set_constant("UDT_HIT", UDT_HIT, false, false); + script->set_constant("UDT_FLEE", UDT_FLEE, false, false); + script->set_constant("UDT_PDODGE", UDT_PDODGE, false, false); + script->set_constant("UDT_CRIT", UDT_CRIT, false, false); + script->set_constant("UDT_RACE", UDT_RACE, false, false); + script->set_constant("UDT_ELETYPE", UDT_ELETYPE, false, false); + script->set_constant("UDT_ELELEVEL", UDT_ELELEVEL, false, false); + script->set_constant("UDT_AMOTION", UDT_AMOTION, false, false); + script->set_constant("UDT_ADELAY", UDT_ADELAY, false, false); + script->set_constant("UDT_DMOTION", UDT_DMOTION, false, false); + script->set_constant("UDT_HUNGER", UDT_HUNGER, false, false); + script->set_constant("UDT_INTIMACY", UDT_INTIMACY, false, false); + script->set_constant("UDT_LIFETIME", UDT_LIFETIME, false, false); + script->set_constant("UDT_MERC_KILLCOUNT", UDT_MERC_KILLCOUNT, false, false); + script->set_constant("UDT_STATPOINT", UDT_STATPOINT, false, false); + script->set_constant("UDT_ROBE", UDT_ROBE, false, false); + script->set_constant("UDT_BODY2", UDT_BODY2, false, false); + script->set_constant("UDT_GROUP", UDT_GROUP, false, false); + script->set_constant("UDT_DAMAGE_TAKEN_RATE", UDT_DAMAGE_TAKEN_RATE, false, false); + + script->constdb_comment("getguildonline types"); + script->set_constant("GUILD_ONLINE_ALL", GUILD_ONLINE_ALL, false, false); + script->set_constant("GUILD_ONLINE_VENDOR", GUILD_ONLINE_VENDOR, false, false); + script->set_constant("GUILD_ONLINE_NO_VENDOR", GUILD_ONLINE_NO_VENDOR, false, false); + + script->constdb_comment("Siege Types"); + script->set_constant("SIEGE_TYPE_FE", SIEGE_TYPE_FE, false, false); + script->set_constant("SIEGE_TYPE_SE", SIEGE_TYPE_SE, false, false); + script->set_constant("SIEGE_TYPE_TE", SIEGE_TYPE_TE, false, false); + + script->constdb_comment("guildinfo types"); + script->set_constant("GUILDINFO_NAME", GUILDINFO_NAME, false, false); + script->set_constant("GUILDINFO_ID", GUILDINFO_ID, false, false); + script->set_constant("GUILDINFO_LEVEL", GUILDINFO_LEVEL, false, false); + script->set_constant("GUILDINFO_ONLINE", GUILDINFO_ONLINE, false, false); + script->set_constant("GUILDINFO_AV_LEVEL", GUILDINFO_AV_LEVEL, false, false); + script->set_constant("GUILDINFO_MAX_MEMBERS", GUILDINFO_MAX_MEMBERS, false, false); + script->set_constant("GUILDINFO_EXP", GUILDINFO_EXP, false, false); + script->set_constant("GUILDINFO_NEXT_EXP", GUILDINFO_NEXT_EXP, false, false); + script->set_constant("GUILDINFO_SKILL_POINTS", GUILDINFO_SKILL_POINTS, false, false); + script->set_constant("GUILDINFO_MASTER_NAME", GUILDINFO_MASTER_NAME, false, false); + script->set_constant("GUILDINFO_MASTER_CID", GUILDINFO_MASTER_CID, false, false); + + script->constdb_comment("madogear types"); + script->set_constant("MADO_ROBOT", MADO_ROBOT, false, false); + script->set_constant("MADO_SUITE", MADO_SUITE, false, false); + + script->constdb_comment("itemskill option flags"); + script->set_constant("ISF_NONE", ISF_NONE, false, false); + script->set_constant("ISF_CHECKCONDITIONS", ISF_CHECKCONDITIONS, false, false); + script->set_constant("ISF_INSTANTCAST", ISF_INSTANTCAST, false, false); + script->set_constant("ISF_CASTONSELF", ISF_CASTONSELF, false, false); + + script->constdb_comment("Item Bound Types"); + script->set_constant("IBT_ANY", IBT_NONE, false, false); // for *checkbound() + script->set_constant("IBT_ACCOUNT", IBT_ACCOUNT, false, false); + script->set_constant("IBT_GUILD", IBT_GUILD, false, false); + script->set_constant("IBT_PARTY", IBT_PARTY, false, false); + script->set_constant("IBT_CHARACTER", IBT_CHARACTER, false, false); script->constdb_comment("Renewal"); #ifdef RENEWAL @@ -21069,7 +28067,8 @@ void script_hardcoded_constants(void) /** * a mapindex_name2id wrapper meant to help with invalid name handling **/ -unsigned short script_mapindexname2id (struct script_state *st, const char* name) { +static unsigned short script_mapindexname2id(struct script_state *st, const char *name) +{ unsigned short index; if( !(index=mapindex->name2id(name)) ) { @@ -21079,7 +28078,8 @@ unsigned short script_mapindexname2id (struct script_state *st, const char* name return index; } -void script_defaults(void) { +void script_defaults(void) +{ // aegis->athena slot position conversion table unsigned int equip[SCRIPT_EQUIP_TABLE_SIZE] = {EQP_HEAD_TOP,EQP_ARMOR,EQP_HAND_L,EQP_HAND_R,EQP_GARMENT,EQP_SHOES,EQP_ACC_L,EQP_ACC_R,EQP_HEAD_MID,EQP_HEAD_LOW,EQP_COSTUME_HEAD_LOW,EQP_COSTUME_HEAD_MID,EQP_COSTUME_HEAD_TOP,EQP_COSTUME_GARMENT,EQP_SHADOW_ARMOR, EQP_SHADOW_WEAPON, EQP_SHADOW_SHIELD, EQP_SHADOW_SHOES, EQP_SHADOW_ACC_R, EQP_SHADOW_ACC_L}; @@ -21112,8 +28112,8 @@ void script_defaults(void) { script->label_count = 0; script->labels_size = 0; - script->buf = NULL; - script->pos = 0, script->size = 0; + VECTOR_INIT(script->buf); + VECTOR_INIT(script->translation_buf); script->parse_options = 0; script->buildin_set_ref = 0; @@ -21171,10 +28171,12 @@ void script_defaults(void) { script->get_val = get_val; script->get_val2 = get_val2; script->get_val_ref_str = get_val_npcscope_str; + script->get_val_pc_ref_str = get_val_pc_ref_str; script->get_val_scope_str = get_val_npcscope_str; script->get_val_npc_str = get_val_npcscope_str; script->get_val_instance_str = get_val_instance_str; script->get_val_ref_num = get_val_npcscope_num; + script->get_val_pc_ref_num = get_val_pc_ref_num; script->get_val_scope_num = get_val_npcscope_num; script->get_val_npc_num = get_val_npcscope_num; script->get_val_instance_num = get_val_instance_num; @@ -21203,10 +28205,12 @@ 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; script->attach_state = script_attach_state; + script->sprintf_helper = script_sprintf_helper; script->queue = script_hqueue_get; script->queue_add = script_hqueue_add; @@ -21239,6 +28243,11 @@ void script_defaults(void) { script->parse_nextline = parse_nextline; script->parse_variable = parse_variable; script->parse_simpleexpr = parse_simpleexpr; + script->parse_simpleexpr_paren = parse_simpleexpr_paren; + script->parse_simpleexpr_number = parse_simpleexpr_number; + script->parse_simpleexpr_string = parse_simpleexpr_string; + script->parse_simpleexpr_name = parse_simpleexpr_name; + script->add_translatable_string = script_add_translatable_string; script->parse_expr = parse_expr; script->parse_line = parse_line; script->read_constdb = read_constdb; @@ -21248,10 +28257,12 @@ void script_defaults(void) { script->errorwarning_sub = script_errorwarning_sub; script->set_reg = set_reg; script->set_reg_ref_str = set_reg_npcscope_str; + script->set_reg_pc_ref_str = set_reg_pc_ref_str; script->set_reg_scope_str = set_reg_npcscope_str; script->set_reg_npc_str = set_reg_npcscope_str; script->set_reg_instance_str = set_reg_instance_str; script->set_reg_ref_num = set_reg_npcscope_num; + script->set_reg_pc_ref_num = set_reg_pc_ref_num; script->set_reg_scope_num = set_reg_npcscope_num; script->set_reg_npc_num = set_reg_npcscope_num; script->set_reg_instance_num = set_reg_instance_num; @@ -21268,6 +28279,9 @@ void script_defaults(void) { script->db_free_code_sub = db_script_free_code_sub; script->add_autobonus = script_add_autobonus; script->menu_countoptions = menu_countoptions; + script->buildin_recovery_sub = buildin_recovery_sub; + script->buildin_recovery_pc_sub = buildin_recovery_pc_sub; + script->buildin_recovery_bl_sub = buildin_recovery_bl_sub; script->buildin_areawarp_sub = buildin_areawarp_sub; script->buildin_areapercentheal_sub = buildin_areapercentheal_sub; script->buildin_delitem_delete = buildin_delitem_delete; @@ -21295,8 +28309,8 @@ void script_defaults(void) { script->getfuncname = script_getfuncname; /* script_config base */ - script->config.warn_func_mismatch_argtypes = 1; - script->config.warn_func_mismatch_paramnum = 1; + script->config.warn_func_mismatch_argtypes = true; + script->config.warn_func_mismatch_paramnum = true; script->config.check_cmdcount = 65535; script->config.check_gotocount = 2048; script->config.input_min_value = 0; @@ -21358,15 +28372,22 @@ void script_defaults(void) { script->mapindexname2id = script_mapindexname2id; script->string_dup = script_string_dup; script->load_translations = script_load_translations; + script->load_translation_addstring = script_load_translation_addstring; + script->load_translation_file = script_load_translation_file; script->load_translation = script_load_translation; script->translation_db_destroyer = script_translation_db_destroyer; script->clear_translations = script_clear_translations; script->parse_cleanup_timer = script_parse_cleanup_timer; script->add_language = script_add_language; - script->get_translation_file_name = script_get_translation_file_name; + script->get_translation_dir_name = script_get_translation_dir_name; script->parser_clean_leftovers = script_parser_clean_leftovers; script->run_use_script = script_run_use_script; script->run_item_equip_script = script_run_item_equip_script; script->run_item_unequip_script = script_run_item_unequip_script; + script->run_item_rental_start_script = script_run_item_rental_start_script; + script->run_item_rental_end_script = script_run_item_rental_end_script; + script->run_item_lapineddukddak_script = script_run_item_lapineddukddak_script; + + script->sellitemcurrency_add = script_sellitemcurrency_add; } |