// Copyright (c) Athena Dev Teams - Licensed under GNU GPL // For more information, see LICENCE in the main folder //#define DEBUG_FUNCIN //#define DEBUG_DISP //#define DEBUG_DISASM //#define DEBUG_RUN #include "../common/cbasetypes.h" #include "../common/socket.h" #include "../common/timer.h" #include "../common/malloc.h" #include "../common/lock.h" #include "../common/nullpo.h" #include "../common/showmsg.h" #include "../common/strlib.h" #include "../common/utils.h" #include "map.h" #include "clif.h" #include "chrif.h" #include "itemdb.h" #include "pc.h" #include "status.h" #include "storage.h" #include "mob.h" #include "npc.h" #include "pet.h" #include "mercenary.h" //[orn] #include "intif.h" #include "skill.h" #include "chat.h" #include "battle.h" #include "party.h" #include "guild.h" #include "atcommand.h" #include "charcommand.h" #include "log.h" #include "unit.h" #include "irc.h" #include "pet.h" #include "script.h" #include #include #include #include #ifndef WIN32 #include #endif #include #include // // struct script_state* st; // /// Returns the stack_data at the target index #define script_getdata(st,i) &((st)->stack->stack_data[(st)->start+(i)]) /// Returns if the stack contains data at the target index #define script_hasdata(st,i) ( (st)->end > (st)->start + (i) ) /// Returns the index of the last data in the stack #define script_lastdata(st) ( (st)->end - (st)->start - 1 ) /// Pushes an int into the stack #define script_pushint(st,val) push_val((st)->stack, C_INT, (val)) #define script_pushstr(st,val) push_str((st)->stack, C_STR, (val)) #define script_pushconststr(st,val) push_str((st)->stack, C_CONSTSTR, (val)) #define script_getnum(st,val) conv_num(st, script_getdata(st,val)) #define script_getstr(st,val) conv_str(st, script_getdata(st,val)) #define script_getref(st,val) ((st)->stack->stack_data[(st)->start+(val)].ref) // // struct script_data* data; // /// Returns if the script data is a string #define data_isstring(data) ( (data)->type == C_STR || (data)->type == C_CONSTSTR ) /// Returns if the script data is an int #define data_isint(data) ( (data)->type == C_INT ) /// Returns if the script data is a reference #define data_isreference(data) ( (data)->type == C_NAME ) /// Returns if the script data is a label #define data_islabel(data) ( (data)->type == C_POS ) /// Returns if the script data is an internal script function label #define data_isfunclabel(data) ( (data)->type == C_USERFUNC_POS ) #define FETCH(n, t) \ if( script_hasdata(st,n) ) \ (t)=script_getnum(st,n); #define SCRIPT_BLOCK_SIZE 512 enum { LABEL_NEXTLINE=1,LABEL_START }; static unsigned char * script_buf = NULL; static int script_pos,script_size; #define GETVALUE(buf,i) ((int)MakeDWord(MakeWord((buf)[i],(buf)[i+1]),MakeWord((buf)[i+2],0))) #define SETVALUE(buf,i,n) ((buf)[i]=GetByte(n,0),(buf)[i+1]=GetByte(n,1),(buf)[i+2]=GetByte(n,2)) #define GETSTRING(off) (str_buf+(off)) static char *str_buf; static int str_pos,str_size; static struct str_data_struct { int type; int str; int backpatch; int label; int (*func)(struct script_state *st); int val; int next; } *str_data = NULL; int str_num=LABEL_START,str_data_size; // Using a prime number for SCRIPT_HASH_SIZE should give better distributions #define SCRIPT_HASH_SIZE 1021 int str_hash[SCRIPT_HASH_SIZE]; //#define SCRIPT_HASH_DJB2 //#define SCRIPT_HASH_SDBM //#define SCRIPT_HASH_ELF //#define SCRIPT_HASH_PJW static struct dbt *mapreg_db=NULL; static struct dbt *mapregstr_db=NULL; static int mapreg_dirty=-1; char mapreg_txt[256]="save/mapreg.txt"; #define MAPREG_AUTOSAVE_INTERVAL (300*1000) static struct dbt *scriptlabel_db=NULL; static struct dbt *userfunc_db=NULL; static int parse_options=0; struct dbt* script_get_label_db(){ return scriptlabel_db; } struct dbt* script_get_userfunc_db(){ return userfunc_db; } static char pos[11][100] = {"Head","Body","Left hand","Right hand","Robe","Shoes","Accessory 1","Accessory 2","Head 2","Head 3","Not Equipped"}; struct Script_Config script_config; static jmp_buf error_jump; static char* error_msg; static const char* error_pos; static int error_report; // if the error should produce output // for advanced scripting support ( nested if, switch, while, for, do-while, function, etc ) // [Eoe / jA 1080, 1081, 1094, 1164] enum curly_type { TYPE_NULL = 0, TYPE_IF, TYPE_SWITCH, TYPE_WHILE, TYPE_FOR, TYPE_DO, TYPE_USERFUNC, TYPE_ARGLIST // function argument list }; #define ARGLIST_UNDEFINED 0 #define ARGLIST_NO_PAREN 1 #define ARGLIST_PAREN 2 static struct { struct { int type; int index; int count; int flag; struct linkdb_node *case_label; } curly[256]; // 右カッコの情報 int curly_count; // 右カッコの数 int index; // スクリプト内で使用した構文の数 } syntax; const char* parse_curly_close(const char* p); const char* parse_syntax_close(const char* p); const char* parse_syntax_close_sub(const char* p,int* flag); const char* parse_syntax(const char* p); static int parse_syntax_for_flag = 0; extern int current_equip_item_index; //for New CARS Scripts. It contains Inventory Index of the EQUIP_SCRIPT caller item. [Lupus] int potion_flag=0; //For use on Alchemist improved potions/Potion Pitcher. [Skotlex] int potion_hp=0, potion_per_hp=0, potion_sp=0, potion_per_sp=0; int potion_target=0; #if !defined(TXT_ONLY) && defined(MAPREGSQL) // [zBuffer] SQL Mapreg Saving/Loading Database Declaration char mapregsql_db[32] = "mapreg"; char mapregsql_db_varname[32] = "varname"; char mapregsql_db_index[32] = "index"; char mapregsql_db_value[32] = "value"; char tmp_sql[65535]; // -------------------------------------------------------- #endif int get_com(unsigned char *script,int *pos); int get_num(unsigned char *script,int *pos); extern struct script_function { int (*func)(struct script_state *st); const char *name; const char *arg; } buildin_func[]; static struct linkdb_node *sleep_db; #define not_server_variable(prefix) (prefix != '$' && prefix != '.') /*========================================== * ローカルプロトタイプ宣言 (必要な物のみ) *------------------------------------------ */ const char* parse_subexpr(const char* p,int limit); void push_val(struct script_stack *stack,int type,int val); int run_func(struct script_state *st); int mapreg_setreg(int num,int val); int mapreg_setregstr(int num,const char *str); enum c_op { C_NOP, C_POS, C_INT, // number C_PARAM, // parameter variable (see pc_readparam/pc_setparam) C_FUNC, // buildin function call C_STR, // string (free'd automatically) C_CONSTSTR, // string (not free'd) C_ARG, // start of argument list C_NAME, C_EOL, // end of line (extra stack values are cleared) C_RETINFO, C_USERFUNC, // internal script function C_USERFUNC_POS, // internal script function label // operators C_OP3, // a ? b : c C_LOR, // a || b C_LAND, // a && b C_LE, // a <= b C_LT, // a < b C_GE, // a >= b C_GT, // a > b C_EQ, // a == b C_NE, // a != b C_XOR, // a ^ b C_OR, // a | b C_AND, // a & b C_ADD, // a + b C_SUB, // a - b C_MUL, // a * b C_DIV, // a / b C_MOD, // a % b C_NEG, // - a C_LNOT, // ! a C_NOT, // ~ a C_R_SHIFT, // a >> b C_L_SHIFT // a << b }; enum { MF_NOMEMO, //0 MF_NOTELEPORT, MF_NOSAVE, MF_NOBRANCH, MF_NOPENALTY, MF_NOZENYPENALTY, MF_PVP, MF_PVP_NOPARTY, MF_PVP_NOGUILD, MF_GVG, MF_GVG_NOPARTY, //10 MF_NOTRADE, MF_NOSKILL, MF_NOWARP, MF_PARTYLOCK, MF_NOICEWALL, MF_SNOW, MF_FOG, MF_SAKURA, MF_LEAVES, MF_RAIN, //20 MF_INDOORS, MF_NOGO, MF_CLOUDS, MF_CLOUDS2, MF_FIREWORKS, MF_GVG_CASTLE, MF_GVG_DUNGEON, MF_NIGHTENABLED, MF_NOBASEEXP, MF_NOJOBEXP, //30 MF_NOMOBLOOT, MF_NOMVPLOOT, MF_NORETURN, MF_NOWARPTO, MF_NIGHTMAREDROP, MF_RESTRICTED, MF_NOCOMMAND, MF_NODROP, MF_JEXP, MF_BEXP, //40 MF_NOVENDING, MF_LOADEVENT, MF_NOCHAT, MF_NOEXPPENALTY, MF_GUILDLOCK }; //Reports on the console the src of a script error. static void report_src(struct script_state *st) { struct block_list *bl; if (!st->oid) return; //Can't report source. bl = map_id2bl(st->oid); if (!bl) return; switch (bl->type) { case BL_NPC: if (bl->m >=0) ShowDebug("Source (NPC): %s at %s (%d,%d)\n", ((struct npc_data *)bl)->name, map[bl->m].name, bl->x, bl->y); else ShowDebug("Source (NPC): %s (invisible/not on a map)\n", ((struct npc_data *)bl)->name); break; default: if (bl->m >=0) ShowDebug("Source (Non-NPC type %d): name %s at %s (%d,%d)\n", bl->type, status_get_name(bl), map[bl->m].name, bl->x, bl->y); else ShowDebug("Source (Non-NPC type %d): name %s (invisible/not on a map)\n", bl->type, status_get_name(bl)); break; } } /*========================================== * エラーメッセージ出力 *------------------------------------------ */ static void disp_error_message2(const char *mes,const char *pos,int report) { error_msg = aStrdup(mes); error_pos = pos; error_report = report; longjmp( error_jump, 1 ); } #define disp_error_message(mes,pos) disp_error_message2(mes,pos,1) /// Checks event parameter validity static void check_event(struct script_state *st, const char *evt) { if( evt != NULL && *evt != '\0' && !stristr(evt,"::On") ){ ShowError("NPC event parameter deprecated! Please use 'NPCNAME::OnEVENT' instead of '%s'.\n",evt); report_src(st); } } /*========================================== * 文字列のハッシュを計算 *------------------------------------------ */ #define calc_hash(x) (calc_hash2(x)%SCRIPT_HASH_SIZE) static unsigned int calc_hash2(const unsigned char *p) { #if defined(SCRIPT_HASH_DJB2) unsigned int h = 5381; while( *p ) // hash*33 + c h = ( h << 5 ) + h + ((unsigned char)TOLOWER(*p++)); return h; #elif defined(SCRIPT_HASH_SDBM) unsigned int h = 0; while( *p ) h = ( h << 6 ) + ( h << 16 ) - h + ((unsigned char)TOLOWER(*p++)); return h; #elif defined(SCRIPT_HASH_ELF) unsigned int h = 0; unsigned int g; while( *p ){ // UNIX ELF hash h = ( h << 4 ) + ((unsigned char)TOLOWER(*p++)); if ( g = h & 0xF0000000 ) h ^= g >> 24; h &= ~g; } return h; #elif defined(SCRIPT_HASH_PJW) unsigned int h = 0; unsigned int g; while( *p ){ h = ( h << 4 ) + ((unsigned char)TOLOWER(*p++)); if ( (g=h&0xF0000000) ) { h ^= g>>24; h ^= g; } } return h; #else unsigned int h = 0; while( *p ){ h = ( h << 1 ) + ( h >> 3 ) + ( h >> 5 ) + ( h >> 8 ); h+=(unsigned char)TOLOWER(*p++); } return h; #endif } /*========================================== * str_dataの中に名前があるか検索する *------------------------------------------ */ // 既存のであれば番号、無ければ-1 static int search_str(const char *p) { int i; i=str_hash[calc_hash(p)]; while(i){ if(strcasecmp(str_buf+str_data[i].str,p)==0){ return i; } i=str_data[i].next; } return -1; } /*========================================== * str_dataに名前を登録 *------------------------------------------ */ // 既存のであれば番号、無ければ登録して新規番号 int add_str(const char* p) { int i; int len; i=calc_hash(p); if(str_hash[i]==0){ str_hash[i]=str_num; } else { i=str_hash[i]; for(;;){ if(strcasecmp(str_buf+str_data[i].str,p)==0){ return i; } if(str_data[i].next==0) break; i=str_data[i].next; } str_data[i].next=str_num; } if(str_num>=str_data_size){ str_data_size+=128; RECREATE(str_data,struct str_data_struct,str_data_size); memset(str_data + (str_data_size - 128), '\0', 128); } len=(int)strlen(p); while(str_pos+len+1>=str_size){ str_size+=256; RECREATE(str_buf,char,str_size); memset(str_buf + (str_size - 256), '\0', 256); } memcpy(str_buf+str_pos,p,len+1); str_data[str_num].type=C_NOP; str_data[str_num].str=str_pos; str_data[str_num].next=0; str_data[str_num].func=NULL; str_data[str_num].backpatch=-1; str_data[str_num].label=-1; str_pos+=len+1; return str_num++; } /*========================================== * スクリプトバッファサイズの確認と拡張 *------------------------------------------ */ static void expand_script_buf(void) { script_size+=SCRIPT_BLOCK_SIZE; RECREATE(script_buf,unsigned char,script_size); } /*========================================== * スクリプトバッファに1バイト書き込む *------------------------------------------ */ #define add_scriptb(a) if( script_pos+1>=script_size ) expand_script_buf(); script_buf[script_pos++]=(uint8)(a) #if 0 static void add_scriptb(int a) { expand_script_buf(); script_buf[script_pos++]=a; } #endif /*========================================== * スクリプトバッファにデータタイプを書き込む *------------------------------------------ */ static void add_scriptc(int a) { while(a>=0x40){ add_scriptb((a&0x3f)|0x40); a=(a-0x40)>>6; } add_scriptb(a&0x3f); } /*========================================== * スクリプトバッファに整数を書き込む *------------------------------------------ */ static void add_scripti(int a) { while(a>=0x40){ add_scriptb(a|0xc0); a=(a-0x40)>>6; } add_scriptb(a|0x80); } /*========================================== * スクリプトバッファにラベル/変数/関数を書き込む *------------------------------------------ */ // 最大16Mまで static void add_scriptl(int l) { int backpatch = str_data[l].backpatch; switch(str_data[l].type){ case C_POS: case C_USERFUNC_POS: add_scriptc(C_POS); add_scriptb(str_data[l].label); add_scriptb(str_data[l].label>>8); add_scriptb(str_data[l].label>>16); break; case C_NOP: case C_USERFUNC: // ラベルの可能性があるのでbackpatch用データ埋め込み add_scriptc(C_NAME); str_data[l].backpatch=script_pos; add_scriptb(backpatch); add_scriptb(backpatch>>8); add_scriptb(backpatch>>16); break; case C_INT: add_scripti(abs(str_data[l].val)); if(str_data[l].val < 0) //Notice that this is negative, from jA (Rayce) add_scriptc(C_NEG); break; default: // もう他の用途と確定してるので数字をそのまま add_scriptc(C_NAME); add_scriptb(l); add_scriptb(l>>8); add_scriptb(l>>16); break; } } /*========================================== * ラベルを解決する *------------------------------------------ */ void set_label(int l,int pos, const char* script_pos) { int i,next; if(str_data[l].type==C_INT || str_data[l].type==C_PARAM) { //Prevent overwriting constants values and parameters [Skotlex] disp_error_message("set_label: invalid label name",script_pos); return; } if(str_data[l].label!=-1){ disp_error_message("set_label: dup label ",script_pos); return; } str_data[l].type=(str_data[l].type == C_USERFUNC ? C_USERFUNC_POS : C_POS); str_data[l].label=pos; for(i=str_data[l].backpatch;i>=0 && i!=0x00ffffff;){ next=GETVALUE(script_buf,i); script_buf[i-1]=(str_data[l].type == C_USERFUNC ? C_USERFUNC_POS : C_POS); SETVALUE(script_buf,i,pos); i=next; } } /// Skips spaces and/or comments. static const char* skip_space(const char* p) { for(;;) { while( ISSPACE(*p) ) ++p; if( *p == '/' && p[1] == '/' ) {// line comment while(*p && *p!='\n') ++p; } else if( *p == '/' && p[1] == '*' ) {// block comment p += 2; for(;;) { if( *p == '\0' ) disp_error_message("script:skip_space: end of file while parsing block comment. expected "CL_BOLD"*/"CL_NORM, p); if( *p == '*' || p[1] == '/' ) {// end of block comment p += 2; break; } ++p; } } else break; } return p; } /// Skips a word. /// A word consists of undercores and/or alfanumeric characters, /// and valid variable prefixes/postfixes. static const char* skip_word(const char* p) { // prefix switch( *p ) { case '@':// temporary char variable ++p; break; case '#':// account variable p += ( p[1] == '#' ? 2 : 1 ); break; case '.':// npc variable p += ( p[1] == '@' ? 2 : 1 ); break; case '$':// global variable p += ( p[1] == '@' ? 2 : 1 ); break; } while( ISALNUM(*p) || *p == '_' ) ++p; // postfix if( *p == '$' )// string p++; return p; } /// Adds a word to str_data. /// @see skip_word /// @see add_str static int add_word(const char* p) { char* word; int len; int i; // Check for a word len = skip_word(p) - p; if( len == 0 ) disp_error_message("script:add_word: invalid word. A word consists of undercores and/or alfanumeric characters, and valid variable prefixes/postfixes.", p); // Duplicate the word CREATE(word, char, len+1); memcpy(word, p, len); word[len] = 0; // add the word i = add_str(word); aFree(word); return i; } /// 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) { const char* p2; const char* arg=NULL; int func; func = add_word(p); if( str_data[func].type == C_FUNC ){ // buildin function add_scriptl(func); add_scriptc(C_ARG); arg = buildin_func[str_data[func].val].arg; } else if( str_data[func].type == C_USERFUNC || str_data[func].type == C_USERFUNC_POS ){ // script defined function int callsub = search_str("callsub"); add_scriptl(callsub); add_scriptc(C_ARG); add_scriptl(func); arg = buildin_func[str_data[callsub].val].arg; if( *arg == 0 ) disp_error_message("parse_callfunc: callsub has no arguments, please review it's definition",p); if( *arg != '*' ) ++arg; // count func as argument } else disp_error_message("parse_line: expect command, missing function name or calling undeclared function",p); p = skip_word(p); p = skip_space(p); syntax.curly[syntax.curly_count].type = TYPE_ARGLIST; syntax.curly[syntax.curly_count].count = 0; if( *p == ';' ) {// ';' syntax.curly[syntax.curly_count].flag = ARGLIST_NO_PAREN; } else if( *p == '(' && *(p2=skip_space(p+1)) == ')' ) {// '(' ')' syntax.curly[syntax.curly_count].flag = ARGLIST_PAREN; p = p2; /* } else if( 0 && require_paren && *p != '(' ) {// syntax.curly[syntax.curly_count].flag = ARGLIST_NO_PAREN; */ } else {// if( require_paren ){ if( *p != '(' ) disp_error_message("need '('",p); ++p; // skip '(' syntax.curly[syntax.curly_count].flag = ARGLIST_PAREN; } else if( *p == '(' ){ syntax.curly[syntax.curly_count].flag = ARGLIST_UNDEFINED; } else { syntax.curly[syntax.curly_count].flag = ARGLIST_NO_PAREN; } ++syntax.curly_count; while( *arg ) { p2=parse_subexpr(p,-1); if( p == p2 ) break; // not an argument if( *arg != '*' ) ++arg; // next argument p=skip_space(p2); if( *arg == 0 || *p != ',' ) break; // no more arguments ++p; // skip comma } --syntax.curly_count; } if( *arg && *arg != '?' && *arg != '*' ) disp_error_message2("parse_callfunc: not enough arguments, expected ','", p, script_config.warn_func_mismatch_paramnum); if( syntax.curly[syntax.curly_count].type != TYPE_ARGLIST ) disp_error_message("parse_callfunc: DEBUG last curly is not an argument list",p); if( syntax.curly[syntax.curly_count].flag == ARGLIST_PAREN ){ if( *p != ')' ) disp_error_message("parse_callfunc: expected ')' to close argument list",p); ++p; } add_scriptc(C_FUNC); return p; } /*========================================== * 項の解析 *------------------------------------------ */ const char* parse_simpleexpr(const char *p) { int i; p=skip_space(p); #ifdef DEBUG_FUNCIN if(battle_config.etc_log) ShowDebug("parse_simpleexpr %s\n",p); #endif if(*p==';' || *p==',') disp_error_message("parse_simpleexpr: unexpected expr end",p); if(*p=='('){ if( (i=syntax.curly_count-1) >= 0 && syntax.curly[i].type == TYPE_ARGLIST ) ++syntax.curly[i].count; p=parse_subexpr(p+1,-1); p=skip_space(p); if( (i=syntax.curly_count-1) >= 0 && syntax.curly[i].type == TYPE_ARGLIST && syntax.curly[i].flag == ARGLIST_UNDEFINED && --syntax.curly[i].count == 0 ){ if( *p == ',' ){ syntax.curly[i].flag = ARGLIST_PAREN; return p; } else syntax.curly[i].flag = ARGLIST_NO_PAREN; } if( *p != ')' ) disp_error_message("parse_simpleexpr: unmatch ')'",p); ++p; } else if(ISDIGIT(*p) || ((*p=='-' || *p=='+') && ISDIGIT(p[1]))){ char *np; i=strtoul(p,&np,0); add_scripti(i); p=np; } else if(*p=='"'){ add_scriptc(C_STR); p++; while( *p && *p != '"' ){ if( (unsigned char)p[-1] <= 0x7e && *p == '\\' ) p++; else if( *p == '\n' ) disp_error_message("parse_simpleexpr: unexpected newline @ string",p); add_scriptb(*p++); } if(!*p) disp_error_message("parse_simpleexpr: unexpected eof @ string",p); add_scriptb(0); p++; //'"' } else { int l; // label , register , function etc if(skip_word(p)==p) disp_error_message("parse_simpleexpr: unexpected character",p); l=add_word(p); if( str_data[l].type == C_FUNC || str_data[l].type == C_USERFUNC || str_data[l].type == C_USERFUNC_POS) return parse_callfunc(p,1); p=skip_word(p); if( *p == '[' ){ // array(name[i] => getelementofarray(name,i) ) add_scriptl(search_str("getelementofarray")); add_scriptc(C_ARG); add_scriptl(l); p=parse_subexpr(p+1,-1); p=skip_space(p); if( *p != ']' ) disp_error_message("parse_simpleexpr: unmatch ']'",p); ++p; add_scriptc(C_FUNC); }else add_scriptl(l); } #ifdef DEBUG_FUNCIN if(battle_config.etc_log) ShowDebug("parse_simpleexpr end %s\n",p); #endif return p; } /*========================================== * 式の解析 *------------------------------------------ */ const char* parse_subexpr(const char* p,int limit) { int op,opl,len; const char* tmpp; #ifdef DEBUG_FUNCIN if(battle_config.etc_log) ShowDebug("parse_subexpr %s\n",p); #endif p=skip_space(p); if(*p=='-'){ tmpp=skip_space(p+1); if(*tmpp==';' || *tmpp==','){ add_scriptl(LABEL_NEXTLINE); p++; return p; } } tmpp=p; if((op=C_NEG,*p=='-') || (op=C_LNOT,*p=='!') || (op=C_NOT,*p=='~')){ p=parse_subexpr(p+1,10); add_scriptc(op); } else p=parse_simpleexpr(p); p=skip_space(p); while(( (op=C_OP3,opl=0,len=1,*p=='?') || (op=C_ADD,opl=8,len=1,*p=='+') || (op=C_SUB,opl=8,len=1,*p=='-') || (op=C_MUL,opl=9,len=1,*p=='*') || (op=C_DIV,opl=9,len=1,*p=='/') || (op=C_MOD,opl=9,len=1,*p=='%') || (op=C_LAND,opl=2,len=2,*p=='&' && p[1]=='&') || (op=C_AND,opl=6,len=1,*p=='&') || (op=C_LOR,opl=1,len=2,*p=='|' && p[1]=='|') || (op=C_OR,opl=5,len=1,*p=='|') || (op=C_XOR,opl=4,len=1,*p=='^') || (op=C_EQ,opl=3,len=2,*p=='=' && p[1]=='=') || (op=C_NE,opl=3,len=2,*p=='!' && p[1]=='=') || (op=C_R_SHIFT,opl=7,len=2,*p=='>' && p[1]=='>') || (op=C_GE,opl=3,len=2,*p=='>' && p[1]=='=') || (op=C_GT,opl=3,len=1,*p=='>') || (op=C_L_SHIFT,opl=7,len=2,*p=='<' && p[1]=='<') || (op=C_LE,opl=3,len=2,*p=='<' && p[1]=='=') || (op=C_LT,opl=3,len=1,*p=='<')) && opl>limit){ p+=len; if(op == C_OP3) { p=parse_subexpr(p,-1); p=skip_space(p); if( *(p++) != ':') disp_error_message("parse_subexpr: need ':'", p-1); p=parse_subexpr(p,-1); } else { p=parse_subexpr(p,opl); } add_scriptc(op); p=skip_space(p); } #ifdef DEBUG_FUNCIN if(battle_config.etc_log) ShowDebug("parse_subexpr end %s\n",p); #endif return p; /* return first untreated operator */ } /*========================================== * 式の評価 *------------------------------------------ */ const char* parse_expr(const char *p) { #ifdef DEBUG_FUNCIN if(battle_config.etc_log) ShowDebug("parse_expr %s\n",p); #endif switch(*p){ case ')': case ';': case ':': case '[': case ']': case '}': disp_error_message("parse_expr: unexpected char",p); } p=parse_subexpr(p,-1); #ifdef DEBUG_FUNCIN if(battle_config.etc_log) ShowDebug("parse_expr end %s\n",p); #endif return p; } /*========================================== * 行の解析 *------------------------------------------ */ const char* parse_line(const char* p) { const char* p2; p=skip_space(p); if(*p==';') { // if(); for(); while(); のために閉じ判定 p = parse_syntax_close(p); return p+1; } if(*p==')' && parse_syntax_for_flag) return p+1; p = skip_space(p); if(p[0] == '{') { syntax.curly[syntax.curly_count].type = TYPE_NULL; syntax.curly[syntax.curly_count].count = -1; syntax.curly[syntax.curly_count].index = -1; syntax.curly_count++; return p + 1; } else if(p[0] == '}') { return parse_curly_close(p); } // 構文関連の処理 p2 = parse_syntax(p); if(p2 != NULL) return p2; p = parse_callfunc(p,0); p = skip_space(p); if(parse_syntax_for_flag) { if( *p != ')' ) disp_error_message("parse_line: need ')'",p); } else { if( *p != ';' ) disp_error_message("parse_line: need ';'",p); } // if, for , while の閉じ判定 p = parse_syntax_close(p+1); return p; } // { ... } の閉じ処理 const char* parse_curly_close(const char* p) { if(syntax.curly_count <= 0) { disp_error_message("parse_curly_close: unexpected string",p); return p + 1; } else if(syntax.curly[syntax.curly_count-1].type == TYPE_NULL) { syntax.curly_count--; // if, for , while の閉じ判定 p = parse_syntax_close(p + 1); return p; } else if(syntax.curly[syntax.curly_count-1].type == TYPE_SWITCH) { // switch() 閉じ判定 int pos = syntax.curly_count-1; char label[256]; int l; // 一時変数を消す sprintf(label,"set $@__SW%x_VAL,0;",syntax.curly[pos].index); syntax.curly[syntax.curly_count++].type = TYPE_NULL; parse_line(label); syntax.curly_count--; // 無条件で終了ポインタに移動 sprintf(label,"goto __SW%x_FIN;",syntax.curly[pos].index); syntax.curly[syntax.curly_count++].type = TYPE_NULL; parse_line(label); syntax.curly_count--; // 現在地のラベルを付ける sprintf(label,"__SW%x_%x",syntax.curly[pos].index,syntax.curly[pos].count); l=add_str(label); set_label(l,script_pos, p); if(syntax.curly[pos].flag) { // default が存在する sprintf(label,"goto __SW%x_DEF;",syntax.curly[pos].index); syntax.curly[syntax.curly_count++].type = TYPE_NULL; parse_line(label); syntax.curly_count--; } // 終了ラベルを付ける sprintf(label,"__SW%x_FIN",syntax.curly[pos].index); l=add_str(label); set_label(l,script_pos, p); linkdb_final(&syntax.curly[pos].case_label); // free the list of case label syntax.curly_count--; return p+1; } else { disp_error_message("parse_curly_close: unexpected string",p); return p + 1; } } // 構文関連の処理 // break, case, continue, default, do, for, function, // if, switch, while をこの内部で処理します。 const char* parse_syntax(const char* p) { const char *p2 = skip_word(p); switch(*p) { case 'B': case 'b': if(p2 - p == 5 && !strncasecmp(p,"break",5)) { // break の処理 char label[256]; int pos = syntax.curly_count - 1; while(pos >= 0) { if(syntax.curly[pos].type == TYPE_DO) { sprintf(label,"goto __DO%x_FIN;",syntax.curly[pos].index); break; } else if(syntax.curly[pos].type == TYPE_FOR) { sprintf(label,"goto __FR%x_FIN;",syntax.curly[pos].index); break; } else if(syntax.curly[pos].type == TYPE_WHILE) { sprintf(label,"goto __WL%x_FIN;",syntax.curly[pos].index); break; } else if(syntax.curly[pos].type == TYPE_SWITCH) { sprintf(label,"goto __SW%x_FIN;",syntax.curly[pos].index); break; } pos--; } if(pos < 0) { disp_error_message("parse_syntax: unexpected 'break'",p); } else { syntax.curly[syntax.curly_count++].type = TYPE_NULL; parse_line(label); syntax.curly_count--; } p = skip_space(p2); if(*p != ';') { disp_error_message("parse_syntax: need ';'",p); } p++; // if, for , while の閉じ判定 p = parse_syntax_close(p + 1); return p; } break; case 'c': case 'C': if(p2 - p == 4 && !strncasecmp(p,"case",4)) { // case の処理 int pos = syntax.curly_count-1; if(pos < 0 || syntax.curly[pos].type != TYPE_SWITCH) { disp_error_message("parse_syntax: unexpected 'case' ",p); return p+1; } else { char label[256]; int l,v; char *np; if(syntax.curly[pos].count != 1) { // FALLTHRU 用のジャンプ sprintf(label,"goto __SW%x_%xJ;",syntax.curly[pos].index,syntax.curly[pos].count); syntax.curly[syntax.curly_count++].type = TYPE_NULL; parse_line(label); syntax.curly_count--; // 現在地のラベルを付ける sprintf(label,"__SW%x_%x",syntax.curly[pos].index,syntax.curly[pos].count); l=add_str(label); set_label(l,script_pos, p); } // switch 判定文 p = skip_space(p2); if(p == p2) { disp_error_message("parse_syntax: expect space ' '",p); } // check whether case label is integer or not v = strtol(p,&np,0); if(np == p) { //Check for constants p2 = skip_word(p); v = p2-p; // length of word at p2 memcpy(label,p,v); label[v]='\0'; v = search_str(label); if (v < 0 || str_data[v].type != C_INT) disp_error_message("parse_syntax: 'case' label not integer",p); v = str_data[v].val; p = skip_word(p); } else { //Numeric value if((*p == '-' || *p == '+') && ISDIGIT(p[1])) // pre-skip because '-' can not skip_word p++; p = skip_word(p); if(np != p) disp_error_message("parse_syntax: 'case' label not integer",np); } p = skip_space(p); if(*p != ':') disp_error_message("parse_syntax: expect ':'",p); sprintf(label,"if(%d != $@__SW%x_VAL) goto __SW%x_%x;", v,syntax.curly[pos].index,syntax.curly[pos].index,syntax.curly[pos].count+1); syntax.curly[syntax.curly_count++].type = TYPE_NULL; // 2回parse しないとダメ p2 = parse_line(label); parse_line(p2); syntax.curly_count--; if(syntax.curly[pos].count != 1) { // FALLTHRU 終了後のラベル sprintf(label,"__SW%x_%xJ",syntax.curly[pos].index,syntax.curly[pos].count); l=add_str(label); set_label(l,script_pos,p); } // check duplication of case label [Rayce] if(linkdb_search(&syntax.curly[pos].case_label, (void*)v) != NULL) disp_error_message("parse_syntax: dup 'case'",p); linkdb_insert(&syntax.curly[pos].case_label, (void*)v, (void*)1); sprintf(label,"set $@__SW%x_VAL,0;",syntax.curly[pos].index); syntax.curly[syntax.curly_count++].type = TYPE_NULL; parse_line(label); syntax.curly_count--; syntax.curly[pos].count++; } return p + 1; } else if(p2 - p == 8 && !strncasecmp(p,"continue",8)) { // continue の処理 char label[256]; int pos = syntax.curly_count - 1; while(pos >= 0) { if(syntax.curly[pos].type == TYPE_DO) { sprintf(label,"goto __DO%x_NXT;",syntax.curly[pos].index); syntax.curly[pos].flag = 1; // continue 用のリンク張るフラグ break; } else if(syntax.curly[pos].type == TYPE_FOR) { sprintf(label,"goto __FR%x_NXT;",syntax.curly[pos].index); break; } else if(syntax.curly[pos].type == TYPE_WHILE) { sprintf(label,"goto __WL%x_NXT;",syntax.curly[pos].index); break; } pos--; } if(pos < 0) { disp_error_message("parse_syntax: unexpected 'continue'",p); } else { syntax.curly[syntax.curly_count++].type = TYPE_NULL; parse_line(label); syntax.curly_count--; } p = skip_space(p2); if(*p != ';') disp_error_message("parse_syntax: need ';'",p); p++; // if, for , while の閉じ判定 p = parse_syntax_close(p + 1); return p; } break; case 'd': case 'D': if(p2 - p == 7 && !strncasecmp(p,"default",7)) { // switch - default の処理 int pos = syntax.curly_count-1; if(pos < 0 || syntax.curly[pos].type != TYPE_SWITCH) { disp_error_message("parse_syntax: unexpected 'default'",p); } else if(syntax.curly[pos].flag) { disp_error_message("parse_syntax: dup 'default'",p); } else { char label[256]; int l; // 現在地のラベルを付ける p = skip_space(p2); if(*p != ':') { disp_error_message("parse_syntax: need ':'",p); } sprintf(label,"__SW%x_%x",syntax.curly[pos].index,syntax.curly[pos].count); l=add_str(label); set_label(l,script_pos,p); // 無条件で次のリンクに飛ばす sprintf(label,"goto __SW%x_%x;",syntax.curly[pos].index,syntax.curly[pos].count+1); syntax.curly[syntax.curly_count++].type = TYPE_NULL; parse_line(label); syntax.curly_count--; // default のラベルを付ける sprintf(label,"__SW%x_DEF",syntax.curly[pos].index); l=add_str(label); set_label(l,script_pos,p); syntax.curly[syntax.curly_count - 1].flag = 1; syntax.curly[pos].count++; } return p + 1; } else if(p2 - p == 2 && !strncasecmp(p,"do",2)) { int l; char label[256]; p=skip_space(p2); syntax.curly[syntax.curly_count].type = TYPE_DO; syntax.curly[syntax.curly_count].count = 1; syntax.curly[syntax.curly_count].index = syntax.index++; syntax.curly[syntax.curly_count].flag = 0; // 現在地のラベル形成する sprintf(label,"__DO%x_BGN",syntax.curly[syntax.curly_count].index); l=add_str(label); set_label(l,script_pos,p); syntax.curly_count++; return p; } break; case 'f': case 'F': if(p2 - p == 3 && !strncasecmp(p,"for",3)) { int l; char label[256]; int pos = syntax.curly_count; syntax.curly[syntax.curly_count].type = TYPE_FOR; syntax.curly[syntax.curly_count].count = 1; syntax.curly[syntax.curly_count].index = syntax.index++; syntax.curly[syntax.curly_count].flag = 0; syntax.curly_count++; p=skip_space(p2); if(*p != '(') disp_error_message("parse_syntax: need '('",p); p++; // 初期化文を実行する syntax.curly[syntax.curly_count++].type = TYPE_NULL; p=parse_line(p); syntax.curly_count--; // 条件判断開始のラベル形成する sprintf(label,"__FR%x_J",syntax.curly[pos].index); l=add_str(label); set_label(l,script_pos,p); p=skip_space(p); if(*p == ';') { // for(;;) のパターンなので必ず真 ; } else { // 条件が偽なら終了地点に飛ばす sprintf(label,"__FR%x_FIN",syntax.curly[pos].index); add_scriptl(add_str("jump_zero")); add_scriptc(C_ARG); p=parse_expr(p); p=skip_space(p); add_scriptl(add_str(label)); add_scriptc(C_FUNC); } if(*p != ';') disp_error_message("parse_syntax: need ';'",p); p++; // ループ開始に飛ばす sprintf(label,"goto __FR%x_BGN;",syntax.curly[pos].index); syntax.curly[syntax.curly_count++].type = TYPE_NULL; parse_line(label); syntax.curly_count--; // 次のループへのラベル形成する sprintf(label,"__FR%x_NXT",syntax.curly[pos].index); l=add_str(label); set_label(l,script_pos,p); // 次のループに入る時の処理 // for 最後の ')' を ';' として扱うフラグ parse_syntax_for_flag = 1; syntax.curly[syntax.curly_count++].type = TYPE_NULL; p=parse_line(p); syntax.curly_count--; parse_syntax_for_flag = 0; // 条件判定処理に飛ばす sprintf(label,"goto __FR%x_J;",syntax.curly[pos].index); syntax.curly[syntax.curly_count++].type = TYPE_NULL; parse_line(label); syntax.curly_count--; // ループ開始のラベル付け sprintf(label,"__FR%x_BGN",syntax.curly[pos].index); l=add_str(label); set_label(l,script_pos,p); return p; } else if( p2 - p == 8 && strncasecmp(p,"function",8) == 0 ) {// internal script function const char *func_name; func_name = skip_space(p2); p = skip_word(func_name); if( p == func_name ) disp_error_message("parse_syntax:function: function name is missing or invalid", p); if( *skip_space(p) == ';' ) {// function ; // function declaration - just register the name int l; l = add_word(func_name); if( str_data[l].type == C_NOP )//## ??? [FlavioJS] str_data[l].type = C_USERFUNC; return skip_space(p) + 1; } else {// function char label[256]; int l; syntax.curly[syntax.curly_count].type = TYPE_USERFUNC; syntax.curly[syntax.curly_count].count = 1; syntax.curly[syntax.curly_count].index = syntax.index++; syntax.curly[syntax.curly_count].flag = 0; ++syntax.curly_count; // Jump over the function code sprintf(label, "goto __FN%x_FIN;", syntax.curly[syntax.curly_count-1].index); syntax.curly[syntax.curly_count].type = TYPE_NULL; ++syntax.curly_count; parse_line(label); --syntax.curly_count; // Set the position of the function (label) l=add_word(func_name); if( str_data[l].type == C_NOP )//## ??? [FlavioJS] str_data[l].type = C_USERFUNC; set_label(l, script_pos, p); if( parse_options&SCRIPT_USE_LABEL_DB ) strdb_put(scriptlabel_db, GETSTRING(str_data[l].str), (void*)script_pos); return skip_space(p); } } break; case 'i': case 'I': if(p2 - p == 2 && !strncasecmp(p,"if",2)) { // if() の処理 char label[256]; p=skip_space(p2); if(*p != '(') { //Prevent if this {} non-c syntax. from Rayce (jA) disp_error_message("need '('",p); } syntax.curly[syntax.curly_count].type = TYPE_IF; syntax.curly[syntax.curly_count].count = 1; syntax.curly[syntax.curly_count].index = syntax.index++; syntax.curly[syntax.curly_count].flag = 0; sprintf(label,"__IF%x_%x",syntax.curly[syntax.curly_count].index,syntax.curly[syntax.curly_count].count); syntax.curly_count++; add_scriptl(add_str("jump_zero")); add_scriptc(C_ARG); p=parse_expr(p); p=skip_space(p); add_scriptl(add_str(label)); add_scriptc(C_FUNC); return p; } break; case 's': case 'S': if(p2 - p == 6 && !strncasecmp(p,"switch",6)) { // switch() の処理 char label[256]; p=skip_space(p2); if(*p != '(') { disp_error_message("need '('",p); } syntax.curly[syntax.curly_count].type = TYPE_SWITCH; syntax.curly[syntax.curly_count].count = 1; syntax.curly[syntax.curly_count].index = syntax.index++; syntax.curly[syntax.curly_count].flag = 0; sprintf(label,"$@__SW%x_VAL",syntax.curly[syntax.curly_count].index); syntax.curly_count++; add_scriptl(add_str("set")); add_scriptc(C_ARG); add_scriptl(add_str(label)); p=parse_expr(p); p=skip_space(p); if(*p != '{') { disp_error_message("parse_syntax: need '{'",p); } add_scriptc(C_FUNC); return p + 1; } break; case 'w': case 'W': if(p2 - p == 5 && !strncasecmp(p,"while",5)) { int l; char label[256]; p=skip_space(p2); if(*p != '(') { disp_error_message("need '('",p); } syntax.curly[syntax.curly_count].type = TYPE_WHILE; syntax.curly[syntax.curly_count].count = 1; syntax.curly[syntax.curly_count].index = syntax.index++; syntax.curly[syntax.curly_count].flag = 0; // 条件判断開始のラベル形成する sprintf(label,"__WL%x_NXT",syntax.curly[syntax.curly_count].index); l=add_str(label); set_label(l,script_pos,p); // 条件が偽なら終了地点に飛ばす sprintf(label,"__WL%x_FIN",syntax.curly[syntax.curly_count].index); syntax.curly_count++; add_scriptl(add_str("jump_zero")); add_scriptc(C_ARG); p=parse_expr(p); p=skip_space(p); add_scriptl(add_str(label)); add_scriptc(C_FUNC); return p; } break; } return NULL; } const char* parse_syntax_close(const char *p) { // if(...) for(...) hoge(); のように、1度閉じられたら再度閉じられるか確認する int flag; do { p = parse_syntax_close_sub(p,&flag); } while(flag); return p; } // if, for , while , do の閉じ判定 // flag == 1 : 閉じられた // flag == 0 : 閉じられない const char* parse_syntax_close_sub(const char* p,int* flag) { char label[256]; int pos = syntax.curly_count - 1; int l; *flag = 1; if(syntax.curly_count <= 0) { *flag = 0; return p; } else if(syntax.curly[pos].type == TYPE_IF) { const char *bp = p; const char *p2; // if 最終場所へ飛ばす sprintf(label,"goto __IF%x_FIN;",syntax.curly[pos].index); syntax.curly[syntax.curly_count++].type = TYPE_NULL; parse_line(label); syntax.curly_count--; // 現在地のラベルを付ける sprintf(label,"__IF%x_%x",syntax.curly[pos].index,syntax.curly[pos].count); l=add_str(label); set_label(l,script_pos,p); syntax.curly[pos].count++; p = skip_space(p); p2 = skip_word(p); if(!syntax.curly[pos].flag && p2 - p == 4 && !strncasecmp(p,"else",4)) { // else or else - if p = skip_space(p2); p2 = skip_word(p); if(p2 - p == 2 && !strncasecmp(p,"if",2)) { // else - if p=skip_space(p2); if(*p != '(') { disp_error_message("need '('",p); } sprintf(label,"__IF%x_%x",syntax.curly[pos].index,syntax.curly[pos].count); add_scriptl(add_str("jump_zero")); add_scriptc(C_ARG); p=parse_expr(p); p=skip_space(p); add_scriptl(add_str(label)); add_scriptc(C_FUNC); *flag = 0; return p; } else { // else if(!syntax.curly[pos].flag) { syntax.curly[pos].flag = 1; *flag = 0; return p; } } } // if 閉じ syntax.curly_count--; // 最終地のラベルを付ける sprintf(label,"__IF%x_FIN",syntax.curly[pos].index); l=add_str(label); set_label(l,script_pos,p); if(syntax.curly[pos].flag == 1) { // このifに対するelseじゃないのでポインタの位置は同じ return bp; } return p; } else if(syntax.curly[pos].type == TYPE_DO) { int l; char label[256]; const char *p2; if(syntax.curly[pos].flag) { // 現在地のラベル形成する(continue でここに来る) sprintf(label,"__DO%x_NXT",syntax.curly[pos].index); l=add_str(label); set_label(l,script_pos,p); } // 条件が偽なら終了地点に飛ばす p = skip_space(p); p2 = skip_word(p); if(p2 - p != 5 || strncasecmp(p,"while",5)) disp_error_message("parse_syntax: need 'while'",p); p = skip_space(p2); if(*p != '(') { disp_error_message("need '('",p); } sprintf(label,"__DO%x_FIN",syntax.curly[pos].index); add_scriptl(add_str("jump_zero")); add_scriptc(C_ARG); p=parse_expr(p); p=skip_space(p); add_scriptl(add_str(label)); add_scriptc(C_FUNC); // 開始地点に飛ばす sprintf(label,"goto __DO%x_BGN;",syntax.curly[pos].index); syntax.curly[syntax.curly_count++].type = TYPE_NULL; parse_line(label); syntax.curly_count--; // 条件終了地点のラベル形成する sprintf(label,"__DO%x_FIN",syntax.curly[pos].index); l=add_str(label); set_label(l,script_pos,p); p = skip_space(p); if(*p != ';') { disp_error_message("parse_syntax: need ';'",p); return p+1; } p++; syntax.curly_count--; return p; } else if(syntax.curly[pos].type == TYPE_FOR) { // 次のループに飛ばす sprintf(label,"goto __FR%x_NXT;",syntax.curly[pos].index); syntax.curly[syntax.curly_count++].type = TYPE_NULL; parse_line(label); syntax.curly_count--; // for 終了のラベル付け sprintf(label,"__FR%x_FIN",syntax.curly[pos].index); l=add_str(label); set_label(l,script_pos,p); syntax.curly_count--; return p; } else if(syntax.curly[pos].type == TYPE_WHILE) { // while 条件判断へ飛ばす sprintf(label,"goto __WL%x_NXT;",syntax.curly[pos].index); syntax.curly[syntax.curly_count++].type = TYPE_NULL; parse_line(label); syntax.curly_count--; // while 終了のラベル付け sprintf(label,"__WL%x_FIN",syntax.curly[pos].index); l=add_str(label); set_label(l,script_pos,p); syntax.curly_count--; return p; } else if(syntax.curly[syntax.curly_count-1].type == TYPE_USERFUNC) { int pos = syntax.curly_count-1; char label[256]; int l; // 戻す sprintf(label,"return;"); syntax.curly[syntax.curly_count++].type = TYPE_NULL; parse_line(label); syntax.curly_count--; // 現在地のラベルを付ける sprintf(label,"__FN%x_FIN",syntax.curly[pos].index); l=add_str(label); set_label(l,script_pos,p); syntax.curly_count--; return p + 1; } else { *flag = 0; return p; } } /*========================================== * 組み込み関数の追加 *------------------------------------------ */ static void add_buildin_func(void) { int i,n; const char* p; for( i=0; buildin_func[i].func; i++ ){ // arg must follow the pattern: (v|s|i|r|l)*\?*\*? // 'v' - value (either string or int or reference) // 's' - string // 'i' - int // 'r' - reference (of a variable) // 'l' - label // '?' - one optional parameter // '*' - unknown number of optional parameters p=buildin_func[i].arg; while( *p == 'v' || *p == 's' || *p == 'i' || *p == 'r' || *p == 'l' ) ++p; while( *p == '?' ) ++p; if( *p == '*' ) ++p; if( *p != 0){ ShowWarning("add_buildin_func: ignoring function \"%s\" with invalid arg \"%s\".\n", buildin_func[i].name, buildin_func[i].arg); } else if( *skip_word(buildin_func[i].name) != 0 ){ ShowWarning("add_buildin_func: ignoring function with invalid name \"%s\" (must be a word).\n", buildin_func[i].name); } else { n=add_str(buildin_func[i].name); str_data[n].type=C_FUNC; str_data[n].val=i; str_data[n].func=buildin_func[i].func; } } } /*========================================== * 定数データベースの読み込み *------------------------------------------ */ static void read_constdb(void) { FILE *fp; char line[1024],name[1024],val[1024]; int n,type; sprintf(line, "%s/const.txt", db_path); fp=fopen(line, "r"); if(fp==NULL){ ShowError("can't read %s\n", line); return ; } while(fgets(line,1020,fp)){ if(line[0]=='/' && line[1]=='/') continue; type=0; if(sscanf(line,"%[A-Za-z0-9_],%[-0-9xXA-Fa-f],%d",name,val,&type)>=2 || sscanf(line,"%[A-Za-z0-9_] %[-0-9xXA-Fa-f] %d",name,val,&type)>=2){ n=add_str(name); if(type==0) str_data[n].type=C_INT; else str_data[n].type=C_PARAM; str_data[n].val= (int)strtol(val,NULL,0); } } fclose(fp); } /*========================================== * エラー表示 *------------------------------------------ */ const char* script_print_line( const char *p, const char *mark, int line ) { int i; if( p == NULL || !p[0] ) return NULL; if( line < 0 ) printf("*% 5d : ", -line); else printf(" % 5d : ", line); for(i=0;p[i] && p[i] != '\n';i++){ if(p + i != mark) printf("%c",p[i]); else printf("\'%c\'",p[i]); } printf("\n"); return p+i+(p[i] == '\n' ? 1 : 0); } void script_error(const char *src,const char *file,int start_line, const char *error_msg, const char *error_pos) { // エラーが発生した行を求める int j; int line = start_line; const char *p; const char *linestart[5] = { NULL, NULL, NULL, NULL, NULL }; for(p=src;p && *p;line++){ char *lineend=strchr(p,'\n'); if(lineend==NULL || error_posclear(scriptlabel_db, NULL); parse_options = options; if( setjmp( error_jump ) != 0 ) { //Restore program state when script has problems. [from jA] int i; const int size = sizeof(syntax.curly)/sizeof(syntax.curly[0]); if( error_report ) script_error(src,file,line,error_msg,error_pos); aFree( error_msg ); aFree( script_buf ); script_pos = 0; script_size = 0; script_buf = NULL; for(i=LABEL_START;i=0 && j!=0x00ffffff;){ next=GETVALUE(script_buf,j); SETVALUE(script_buf,j,i); j=next; } } } #ifdef DEBUG_DISP for(i=0;iscript_buf = script_buf; code->script_size = script_size; code->script_vars = NULL; return code; } // // 実行系 // enum {RUN = 0,STOP,END,RERUNLINE,GOTO,RETFUNC}; /// 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) { struct map_session_data *sd=map_id2sd(st->rid); if(!sd){ ShowError("script_rid2sd: fatal error ! player not attached!\n"); st->state = END; } return sd; } /*========================================== * Retrieves the value of a script variable *------------------------------------------*/ int get_val(struct script_state* st, struct script_data* data) { struct map_session_data* sd = NULL; char *name, prefix, postfix; if(!data_isreference(data)) return 0; name = str_buf + str_data[data->u.num&0x00ffffff].str; prefix = name[0]; postfix = name[strlen(name)-1]; if(not_server_variable(prefix)) { sd = script_rid2sd(st); if (!sd) { // needs player attached // throw error, load some meaningful default values and return ShowError("get_val error, cannot access player variable '%s'\n", name); if (postfix == '$') { data->type = C_CONSTSTR; data->u.str = ""; } else { data->type = C_INT; data->u.num = 0; } return 0; } } if(postfix == '$') { // string variable data->type = C_CONSTSTR; switch (prefix) { case '@': data->u.str = pc_readregstr(sd, data->u.num); break; case '$': data->u.str = (char *)idb_get(mapregstr_db,data->u.num); break; case '#': data->u.str = (name[1] == '#') ? pc_readaccountreg2str(sd, name) : pc_readaccountregstr(sd, name); break; case '.': { struct linkdb_node** n; n = (data->ref) ? data->ref : (name[1] == '@') ? st->stack->var_function : &st->script->script_vars; data->u.str = linkdb_search(n, (void*)data->u.num); } break; default: data->u.str = pc_readglobalreg_str(sd, name); break; } if( data->u.str == NULL ) data->u.str = ""; } else { // integer variable data->type = C_INT; if(str_data[data->u.num&0x00ffffff].type == C_INT) { data->u.num = str_data[data->u.num&0x00ffffff].val; } else if(str_data[data->u.num&0x00ffffff].type == C_PARAM) { data->u.num = pc_readparam(sd, str_data[data->u.num&0x00ffffff].val); } else switch (prefix) { case '@': data->u.num = pc_readreg(sd, data->u.num); break; case '$': data->u.num = (int)idb_get(mapreg_db, data->u.num); break; case '#': data->u.num = (name[1] == '#') ? pc_readaccountreg2(sd, name) : pc_readaccountreg(sd, name); break; case '.': { struct linkdb_node** n; n = (data->ref) ? data->ref : (name[1] == '@') ? st->stack->var_function : &st->script->script_vars; data->u.num = (int)linkdb_search(n, (void*)data->u.num); } break; default: data->u.num = pc_readglobalreg(sd, name); break; } } return 0; } /*========================================== * Retrieves the value of a script variable *------------------------------------------*/ void* get_val2(struct script_state* st, int num, struct linkdb_node** ref) { struct script_data dat; dat.type = C_NAME; dat.u.num = num; dat.ref = ref; get_val(st, &dat); return (dat.type == C_INT) ? (void*)dat.u.num : (void*)dat.u.str; } /*========================================== * Stores the value of a script variable * Return value is 0 on fail, 1 on success. *------------------------------------------*/ static int set_reg(struct script_state* st, struct map_session_data* sd, int num, char* name, void* value, struct linkdb_node** ref) { char prefix = name[0]; char postfix = name[strlen(name)-1]; if (postfix == '$') { // string variable char* str = (char*)value; switch (prefix) { case '@': return pc_setregstr(sd, num, str); case '$': return mapreg_setregstr(num, str); case '#': return (name[1] == '#') ? pc_setaccountreg2str(sd, name, str) : pc_setaccountregstr(sd, name, str); case '.': { char* p; struct linkdb_node** n; n = (ref) ? ref : (name[1] == '@') ? st->stack->var_function : &st->script->script_vars; p = linkdb_search(n, (void*)num); if (p) { linkdb_erase(n, (void*)num); aFree(p); } if (str[0]) linkdb_insert(n, (void*)num, aStrdup(str)); } return 1; default: return pc_setglobalreg_str(sd, name, str); } } else { // integer variable int val = (int)value; if(str_data[num&0x00ffffff].type == C_PARAM) return pc_setparam(sd, str_data[num&0x00ffffff].val, val); switch (prefix) { case '@': return pc_setreg(sd, num, val); case '$': return mapreg_setreg(num, val); case '#': return (name[1] == '#') ? pc_setaccountreg2(sd, name, val) : pc_setaccountreg(sd, name, val); case '.': { struct linkdb_node** n; n = (ref) ? ref : (name[1] == '@') ? st->stack->var_function : &st->script->script_vars; if (val == 0) linkdb_erase(n, (void*)num); else linkdb_replace(n, (void*)num, (void*)val); } return 1; default: return pc_setglobalreg(sd, name, val); } } } int set_var(struct map_session_data* sd, char* name, void* val) { return set_reg(NULL, sd, add_str(name), name, val, NULL); } /*========================================== * 文字列への変換 *------------------------------------------ */ const char* conv_str(struct script_state *st,struct script_data *data) { get_val(st,data); if(data_isint(data)){ char *buf; CREATE(buf,char,ITEM_NAME_LENGTH); snprintf(buf,ITEM_NAME_LENGTH, "%d",data->u.num); buf[ITEM_NAME_LENGTH-1]=0; data->type=C_STR; data->u.str=buf; } else if(data_islabel(data)) { // Protect from crashes by passing labels to string-expected args [jA2200] data->type = C_CONSTSTR; data->u.str = "** SCRIPT ERROR **"; } else if(data_isreference(data)){ data->type=C_CONSTSTR; data->u.str=str_buf+str_data[data->u.num].str; } return data->u.str; } /*========================================== * 数値へ変換 *------------------------------------------ */ int conv_num(struct script_state *st,struct script_data *data) { char *p; get_val(st,data); if(data_isstring(data)){ p=data->u.str; data->u.num = atoi(p); if(data->type==C_STR) aFree(p); data->type=C_INT; } return data->u.num; } /*========================================== * スタックへ数値をプッシュ *------------------------------------------ */ void push_val(struct script_stack *stack,int type,int val) { if(stack->sp >= stack->sp_max){ stack->sp_max += 64; stack->stack_data = (struct script_data *)aRealloc(stack->stack_data, sizeof(stack->stack_data[0]) * stack->sp_max); memset(stack->stack_data + (stack->sp_max - 64), 0, 64 * sizeof(*(stack->stack_data))); } // if(battle_config.etc_log) // printf("push (%d,%d)-> %d\n",type,val,stack->sp); stack->stack_data[stack->sp].type=type; stack->stack_data[stack->sp].u.num=val; stack->stack_data[stack->sp].ref = NULL; stack->sp++; } /*========================================== * スタックへ数値+リファレンスをプッシュ *------------------------------------------ */ void push_val2(struct script_stack *stack,int type,int val,struct linkdb_node** ref) { push_val(stack,type,val); stack->stack_data[stack->sp-1].ref = ref; } /*========================================== * スタックへ文字列をプッシュ *------------------------------------------ */ void push_str(struct script_stack *stack,int type,char *str) { if(stack->sp>=stack->sp_max){ stack->sp_max += 64; stack->stack_data = (struct script_data *)aRealloc(stack->stack_data, sizeof(stack->stack_data[0]) * stack->sp_max); memset(stack->stack_data + (stack->sp_max - 64), '\0', 64 * sizeof(*(stack->stack_data))); } // if(battle_config.etc_log) // printf("push (%d,%x)-> %d\n",type,str,stack->sp); stack->stack_data[stack->sp].type =type; stack->stack_data[stack->sp].u.str=str; stack->stack_data[stack->sp].ref =NULL; stack->sp++; } /*========================================== * スタックへ複製をプッシュ *------------------------------------------ */ void push_copy(struct script_stack *stack,int pos) { switch(stack->stack_data[pos].type){ case C_CONSTSTR: push_str(stack,C_CONSTSTR,stack->stack_data[pos].u.str); break; case C_STR: push_str(stack,C_STR,aStrdup(stack->stack_data[pos].u.str)); break; default: push_val2( stack,stack->stack_data[pos].type,stack->stack_data[pos].u.num, stack->stack_data[pos].ref ); break; } } /*========================================== * スタックからポップ *------------------------------------------ */ void pop_stack(struct script_stack* stack,int start,int end) { int i; for(i=start;istack_data[i].type==C_STR){ aFree(stack->stack_data[i].u.str); stack->stack_data[i].type=C_INT; //Might not be correct, but it's done in case to prevent pointer errors later on. [Skotlex] } } if(stack->sp>end){ memmove(&stack->stack_data[start],&stack->stack_data[end],sizeof(stack->stack_data[0])*(stack->sp-end)); } stack->sp-=end-start; } /*========================================== * スクリプト依存変数、関数依存変数の解放 *------------------------------------------ */ void script_free_vars(struct linkdb_node **node) { struct linkdb_node *n = *node; while(n) { char *name = str_buf + str_data[(int)(n->key)&0x00ffffff].str; char postfix = name[strlen(name)-1]; if( postfix == '$' ) { // 文字型変数なので、データ削除 aFree(n->data); } n = n->next; } linkdb_final( node ); } /*========================================== * Free's the whole stack. Invoked when clearing a character. [Skotlex] *------------------------------------------ */ void script_free_stack(struct script_stack *stack) { int i; for(i = 0; i < stack->sp; i++) { if( stack->stack_data[i].type == C_STR ) { aFree(stack->stack_data[i].u.str); stack->stack_data[i].type = C_INT; } else if( i > 0 && stack->stack_data[i].type == C_RETINFO ) { struct linkdb_node** n = (struct linkdb_node**)stack->stack_data[i-1].u.num; script_free_vars( n ); aFree( n ); } } script_free_vars( stack->var_function ); aFree(stack->var_function); aFree(stack->stack_data); aFree(stack); } void script_free_code(struct script_code* code) { script_free_vars( &code->script_vars ); aFree( code->script_buf ); aFree( code ); } // // 実行部main // /*========================================== * コマンドの読み取り *------------------------------------------ */ static int unget_com_data=-1; int get_com(unsigned char *script,int *pos) { int i,j; if(unget_com_data>=0){ i=unget_com_data; unget_com_data=-1; return i; } if(script[*pos]>=0x80){ return C_INT; } i=0; j=0; while(script[*pos]>=0x40){ i=script[(*pos)++]<=0xc0){ i+=(script[(*pos)++]&0x7f)<stack->sp<=0) return 0; st->stack->sp--; get_val(st,&(st->stack->stack_data[st->stack->sp])); if(st->stack->stack_data[st->stack->sp].type==C_INT) return st->stack->stack_data[st->stack->sp].u.num; return 0; } int isstr(struct script_data *c) { if( data_isstring(c) ) return 1; else if( data_isreference(c) ) { char *p = str_buf + str_data[c->u.num & 0xffffff].str; char postfix = p[strlen(p)-1]; return (postfix == '$'); } return 0; } /*========================================== * Three-section operator * test ? if_true : if_false *------------------------------------------ */ void op_3(struct script_state *st) { int flag = 0; if( isstr(&st->stack->stack_data[st->stack->sp-3])) { const char *str = conv_str(st,& (st->stack->stack_data[st->stack->sp-3])); flag = str[0]; } else { flag = conv_num(st,& (st->stack->stack_data[st->stack->sp-3])); } if( flag ) { push_copy(st->stack, st->stack->sp-2 ); } else { push_copy(st->stack, st->stack->sp-1 ); } pop_stack(st->stack,st->stack->sp-4,st->stack->sp-1); } /*========================================== * 加算演算子 *------------------------------------------ */ void op_add(struct script_state* st) { st->stack->sp--; get_val(st,&(st->stack->stack_data[st->stack->sp])); get_val(st,&(st->stack->stack_data[st->stack->sp-1])); if(isstr(&st->stack->stack_data[st->stack->sp]) || isstr(&st->stack->stack_data[st->stack->sp-1])){ conv_str(st,&(st->stack->stack_data[st->stack->sp])); conv_str(st,&(st->stack->stack_data[st->stack->sp-1])); } if(st->stack->stack_data[st->stack->sp].type==C_INT){ // ii int *i1 = &st->stack->stack_data[st->stack->sp-1].u.num; int *i2 = &st->stack->stack_data[st->stack->sp].u.num; int ret = *i1 + *i2; double ret_double = (double)*i1 + (double)*i2; if(ret_double > INT_MAX|| ret_double < INT_MIN) { ShowWarning("script::op_add overflow detected op:%d\n",C_ADD); report_src(st); ret = cap_value(ret, INT_MIN, INT_MAX); } *i1 = ret; } else { // ssの予定 char *buf; buf=(char *)aMallocA((strlen(st->stack->stack_data[st->stack->sp-1].u.str)+ strlen(st->stack->stack_data[st->stack->sp].u.str)+1)*sizeof(char)); strcpy(buf,st->stack->stack_data[st->stack->sp-1].u.str); strcat(buf,st->stack->stack_data[st->stack->sp].u.str); if(st->stack->stack_data[st->stack->sp-1].type==C_STR) { aFree(st->stack->stack_data[st->stack->sp-1].u.str); st->stack->stack_data[st->stack->sp-1].type=C_INT; } if(st->stack->stack_data[st->stack->sp].type==C_STR) { aFree(st->stack->stack_data[st->stack->sp].u.str); st->stack->stack_data[st->stack->sp].type=C_INT; } st->stack->stack_data[st->stack->sp-1].type=C_STR; st->stack->stack_data[st->stack->sp-1].u.str=buf; } st->stack->stack_data[st->stack->sp-1].ref = NULL; } /*========================================== * 二項演算子(文字列) *------------------------------------------ */ void op_2str(struct script_state *st,int op,int sp1,int sp2) { char *s1=st->stack->stack_data[sp1].u.str, *s2=st->stack->stack_data[sp2].u.str; int a=0; switch(op){ case C_EQ: a= (strcmp(s1,s2)==0); break; case C_NE: a= (strcmp(s1,s2)!=0); break; case C_GT: a= (strcmp(s1,s2)> 0); break; case C_GE: a= (strcmp(s1,s2)>=0); break; case C_LT: a= (strcmp(s1,s2)< 0); break; case C_LE: a= (strcmp(s1,s2)<=0); break; default: ShowWarning("script: illegal string operator\n"); break; } // Because push_val() overwrite stack_data[sp1], C_STR on stack_data[sp1] won't be freed. // So, call push_val() after freeing strings. [jA1783] // script_pushint(st,a); if(st->stack->stack_data[sp1].type==C_STR) { aFree(s1); st->stack->stack_data[sp1].type=C_INT; } if(st->stack->stack_data[sp2].type==C_STR) { aFree(s2); st->stack->stack_data[sp2].type=C_INT; } script_pushint(st,a); } /*========================================== * 二項演算子(数値) *------------------------------------------ */ void op_2num(struct script_state *st,int op,int i1,int i2) { int ret = 0; double ret_double = 0; switch(op){ case C_MOD: ret = i1 % i2; break; case C_AND: ret = i1 & i2; break; case C_OR: ret = i1 | i2; break; case C_XOR: ret = i1 ^ i2; break; case C_LAND: ret = (i1 && i2); break; case C_LOR: ret = (i1 || i2); break; case C_EQ: ret = (i1 == i2); break; case C_NE: ret = (i1 != i2); break; case C_GT: ret = (i1 > i2); break; case C_GE: ret = (i1 >= i2); break; case C_LT: ret = (i1 < i2); break; case C_LE: ret = (i1 <= i2); break; case C_R_SHIFT: ret = i1>>i2; break; case C_L_SHIFT: ret = i1< INT_MAX || ret_double < INT_MIN) { printf("script::op_2num overflow detected op:%d\n",op); report_src(st); ret = (int)cap_value(ret_double,INT_MAX,INT_MIN); } } script_pushint(st,ret); } /*========================================== * 二項演算子 *------------------------------------------ */ void op_2(struct script_state *st,int op) { int i1,i2; char *s1=NULL,*s2=NULL; i2=pop_val(st); if( isstr(&st->stack->stack_data[st->stack->sp]) ) s2=st->stack->stack_data[st->stack->sp].u.str; i1=pop_val(st); if( isstr(&st->stack->stack_data[st->stack->sp]) ) s1=st->stack->stack_data[st->stack->sp].u.str; if( s1!=NULL && s2!=NULL ){ // ss => op_2str op_2str(st,op,st->stack->sp,st->stack->sp+1); }else if( s1==NULL && s2==NULL ){ // ii => op_2num op_2num(st,op,i1,i2); }else{ // si,is => error ShowWarning("script: op_2: int&str, str&int not allow.\n"); report_src(st); if(s1 && st->stack->stack_data[st->stack->sp].type == C_STR) { aFree(s1); st->stack->stack_data[st->stack->sp].type = C_INT; } if(s2 && st->stack->stack_data[st->stack->sp+1].type == C_STR) { aFree(s2); st->stack->stack_data[st->stack->sp+1].type = C_INT; } script_pushint(st,0); } } /*========================================== * 単項演算子 *------------------------------------------ */ void op_1num(struct script_state *st,int op) { int i1; i1=pop_val(st); switch(op){ case C_NEG: i1=-i1; break; case C_NOT: i1=~i1; break; case C_LNOT: i1=!i1; break; } script_pushint(st,i1); } /*========================================== * 関数の実行 *------------------------------------------ */ int run_func(struct script_state *st) { int i,start_sp,end_sp,func; end_sp=st->stack->sp; for(i=end_sp-1;i>=0 && st->stack->stack_data[i].type!=C_ARG;i--); if(i<=0){ //Crash fix when missing "push_val" causes current pointer to become -1. from Rayce (jA) if(battle_config.error_log) ShowError("function not found\n"); // st->stack->sp=0; st->state=END; report_src(st); return 1; } start_sp=i-1; st->start=i-1; st->end=end_sp; func=st->stack->stack_data[st->start].u.num; #ifdef DEBUG_RUN if(battle_config.etc_log) { ShowDebug("run_func : %s? (%d(%d)) sp=%d (%d...%d)\n",str_buf+str_data[func].str, func, str_data[func].type, st->stack->sp, st->start, st->end); ShowDebug("stack dump :"); for(i=0;istack->stack_data[i].type){ case C_INT: printf(" int(%d)",st->stack->stack_data[i].u.num); break; case C_NAME: printf(" name(%s)",str_buf+str_data[st->stack->stack_data[i].u.num & 0xffffff].str); break; case C_ARG: printf(" arg"); break; case C_POS: printf(" pos(%d)",st->stack->stack_data[i].u.num); break; case C_STR: printf(" str(%s)",st->stack->stack_data[i].u.str); break; case C_CONSTSTR: printf(" cstr(%s)",st->stack->stack_data[i].u.str); break; default: printf(" etc(%d,%d)",st->stack->stack_data[i].type,st->stack->stack_data[i].u.num); } } printf("\n"); } #endif if(str_data[func].type!=C_FUNC ){ ShowMessage ("run_func: '"CL_WHITE"%s"CL_RESET"' (type %d) is not function and command!\n", str_buf+str_data[func].str, str_data[func].type); // st->stack->sp=0; st->state=END; report_src(st); return 1; } #ifdef DEBUG_RUN ShowDebug("run_func : %s (func_no : %d , func_type : %d pos : 0x%x)\n", str_buf+str_data[func].str,func,str_data[func].type,st->pos ); #endif if(str_data[func].func){ if (str_data[func].func(st)) //Report error report_src(st); } else { if(battle_config.error_log) ShowError("run_func : %s? (%d(%d))\n",str_buf+str_data[func].str,func,str_data[func].type); script_pushint(st,0); report_src(st); } // Stack's datum are used when re-run functions [Eoe] if(st->state != RERUNLINE) { pop_stack(st->stack,start_sp,end_sp); } if(st->state==RETFUNC){ // ユーザー定義関数からの復帰 int olddefsp=st->stack->defsp; int i; pop_stack(st->stack,st->stack->defsp,start_sp); // 復帰に邪魔なスタック削除 if(st->stack->defsp<5 || st->stack->stack_data[st->stack->defsp-1].type!=C_RETINFO){ ShowWarning("script:run_func(return) return without callfunc or callsub!\n"); st->state=END; report_src(st); return 1; } script_free_vars( st->stack->var_function ); aFree(st->stack->var_function); i = conv_num(st,& (st->stack->stack_data[st->stack->defsp-5])); // 引数の数所得 st->pos=conv_num(st,& (st->stack->stack_data[st->stack->defsp-1])); // スクリプト位置の復元 st->script=(struct script_code*)conv_num(st,& (st->stack->stack_data[st->stack->defsp-3])); // スクリプトを復元 st->stack->var_function = (struct linkdb_node**)st->stack->stack_data[st->stack->defsp-2].u.num; // 関数依存変数 st->stack->defsp=conv_num(st,& (st->stack->stack_data[st->stack->defsp-4])); // 基準スタックポインタを復元 pop_stack(st->stack,olddefsp-5-i,olddefsp); // 要らなくなったスタック(引数と復帰用データ)削除 st->state=GOTO; } return 0; } /*========================================== * スクリプトの実行 *------------------------------------------ */ void run_script_main(struct script_state *st); void run_script(struct script_code *rootscript,int pos,int rid,int oid) { struct script_state *st; struct map_session_data *sd=NULL; if(rootscript==NULL || pos<0) return; if (rid) sd = map_id2sd(rid); if (sd && sd->st && sd->st->scriptroot == rootscript && sd->st->pos == pos){ //Resume script. st = sd->st; } else { st = aCalloc(sizeof(struct script_state), 1); // the script is different, make new script_state and stack st->stack = aMalloc (sizeof(struct script_stack)); st->stack->sp=0; st->stack->sp_max=64; st->stack->stack_data = (struct script_data *)aCalloc(st->stack->sp_max,sizeof(st->stack->stack_data[0])); st->stack->defsp = st->stack->sp; st->stack->var_function = aCalloc(1, sizeof(struct linkdb_node*)); st->state = RUN; st->script = rootscript; } st->pos = pos; st->rid = rid; st->oid = oid; st->sleep.timer = -1; run_script_main(st); } void script_stop_sleeptimers(int id) { struct script_state* st; for(;;) { st = (struct script_state*)linkdb_erase(&sleep_db,(void*)id); if( st == NULL ) break; // no more sleep timers if( st->sleep.timer != INVALID_TIMER ) delete_timer(st->sleep.timer, run_script_timer); script_free_stack(st->stack); aFree(st); } } /*========================================== * 指定ノードをsleep_dbから削除 *------------------------------------------ */ struct linkdb_node* script_erase_sleepdb(struct linkdb_node *n) { struct linkdb_node *retnode; if( n == NULL) return NULL; if( n->prev == NULL ) sleep_db = n->next; else n->prev->next = n->next; if( n->next ) n->next->prev = n->prev; retnode = n->next; aFree( n ); return retnode; // 次のノードを返す } /*========================================== * sleep用タイマー関数 *------------------------------------------ */ int run_script_timer(int tid, unsigned int tick, int id, int data) { struct script_state *st = (struct script_state *)data; struct linkdb_node *node = (struct linkdb_node *)sleep_db; struct map_session_data *sd = map_id2sd(st->rid); if((sd && sd->status.char_id != id) || (st->rid && !sd)) { //Character mismatch. Cancel execution. st->rid = 0; st->state = END; } while( node && st->sleep.timer != -1 ) { if( (int)node->key == st->oid && ((struct script_state *)node->data)->sleep.timer == st->sleep.timer ) { script_erase_sleepdb(node); st->sleep.timer = -1; break; } node = node->next; } if(st->state != RERUNLINE) st->sleep.tick = 0; run_script_main(st); return 0; } /*========================================== * スクリプトの実行メイン部分 *------------------------------------------ */ void run_script_main(struct script_state *st) { int c; int cmdcount=script_config.check_cmdcount; int gotocount=script_config.check_gotocount; struct map_session_data *sd; //For backing up purposes struct script_state *bk_st = NULL; int bk_npcid = 0; struct script_stack *stack=st->stack; sd = st->rid?map_id2sd(st->rid):NULL; if(sd){ if(sd->st != st){ bk_st = sd->st; bk_npcid = sd->npc_id; } sd->st = st; sd->npc_id = st->oid; } if(st->state == RERUNLINE) { st->state = RUN; run_func(st); if(st->state == GOTO) st->state = RUN; } else if(st->state != END) st->state = RUN; while(st->state == RUN){ c= get_com(st->script->script_buf,&st->pos); switch(c){ case C_EOL: if( stack->sp != stack->defsp ) { if( stack->sp > stack->defsp ) { //sp > defsp is valid in cases when you invoke functions and don't use the returned value. [Skotlex] //Since sp is supposed to be defsp in these cases, we could assume the extra stack elements are unneeded. pop_stack(stack, stack->defsp, stack->sp); //Clear out the unused stack-section. } else if( battle_config.error_log ) ShowError("script:run_script_main: unexpected stack position stack.sp(%d) != default(%d)\n", stack->sp, stack->defsp); stack->sp = stack->defsp; } break; case C_INT: push_val(stack,C_INT,get_num(st->script->script_buf,&st->pos)); break; case C_POS: case C_NAME: push_val(stack,c,GETVALUE(st->script->script_buf,st->pos)); st->pos+=3; break; case C_ARG: push_val(stack,c,0); break; case C_STR: push_str(stack,C_CONSTSTR,(st->script->script_buf+st->pos)); while(st->script->script_buf[st->pos++]); break; case C_FUNC: run_func(st); if(st->state==GOTO){ st->state = RUN; if( gotocount>0 && (--gotocount)<=0 ){ ShowError("run_script: infinity loop !\n"); report_src(st); st->state=END; } } break; case C_ADD: op_add(st); break; case C_SUB: case C_MUL: case C_DIV: case C_MOD: case C_EQ: case C_NE: case C_GT: case C_GE: case C_LT: case C_LE: case C_AND: case C_OR: case C_XOR: case C_LAND: case C_LOR: case C_R_SHIFT: case C_L_SHIFT: op_2(st,c); break; case C_NEG: case C_NOT: case C_LNOT: op_1num(st,c); break; case C_OP3: op_3(st); break; case C_NOP: st->state=END; break; default: if(battle_config.error_log) ShowError("unknown command : %d @ %d\n",c,pos); st->state=END; break; } if( cmdcount>0 && (--cmdcount)<=0 ){ ShowError("run_script: infinity loop !\n"); report_src(st); st->state=END; } } if(st->sleep.tick > 0) { //Delay execution st->sleep.charid = sd?sd->status.char_id:0; st->sleep.timer = add_timer(gettick()+st->sleep.tick, run_script_timer, st->sleep.charid, (int)st); linkdb_insert(&sleep_db, (void*)st->oid, st); //Restore previous script if (sd) { sd->st = bk_st; sd->npc_id = bk_npcid; bk_st = NULL; //Remove tag for removal. } } else if(st->state != END && sd){ //Resume later (st is already attached to player). if(bk_st) { ShowWarning("Unable to restore stack! Double continuation!\n"); //Report BOTH scripts to see if that can help somehow. ShowDebug("Previous script (lost):\n"); report_src(bk_st); ShowDebug("Current script:\n"); report_src(st); } } else { //Dispose of script. if (sd) { //Restore previous stack and save char. if(sd->state.using_fake_npc){ clif_clearchar_id(sd->npc_id, 0, sd->fd); sd->state.using_fake_npc = 0; } //Restore previous script if any. sd->st = bk_st; sd->npc_id = bk_npcid; if (!bk_st) npc_event_dequeue(sd); else bk_st = NULL; //Remove tag for removal. if (sd->state.reg_dirty&2) intif_saveregistry(sd,2); if (sd->state.reg_dirty&1) intif_saveregistry(sd,1); } st->pos = -1; script_free_stack (st->stack); aFree(st); } if (bk_st) { //Remove previous script bk_st->pos = -1; script_free_stack(bk_st->stack); aFree(bk_st); bk_st = NULL; } } /*========================================== * マップ変数の変更 *------------------------------------------ */ int mapreg_setreg(int num,int val) { #if !defined(TXT_ONLY) && defined(MAPREGSQL) int i=num>>24; char *name=str_buf+str_data[num&0x00ffffff].str; char tmp_str[64]; #endif if(val!=0) { if(idb_put(mapreg_db,num,(void*)val)) ; #if !defined(TXT_ONLY) && defined(MAPREGSQL) else if(name[1] != '@') { sprintf(tmp_sql,"INSERT INTO `%s`(`%s`,`%s`,`%s`) VALUES ('%s','%d','%d')",mapregsql_db,mapregsql_db_varname,mapregsql_db_index,mapregsql_db_value,jstrescapecpy(tmp_str,name),i,val); if(mysql_query(&mmysql_handle,tmp_sql)){ ShowSQL("DB error - %s\n",mysql_error(&mmysql_handle)); ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); } } #endif } else { // [zBuffer] #if !defined(TXT_ONLY) && defined(MAPREGSQL) if(name[1] != '@') { // Remove from database because it is unused. sprintf(tmp_sql,"DELETE FROM `%s` WHERE `%s`='%s' AND `%s`='%d'",mapregsql_db,mapregsql_db_varname,name,mapregsql_db_index,i); if(mysql_query(&mmysql_handle,tmp_sql)){ ShowSQL("DB error - %s\n",mysql_error(&mmysql_handle)); ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); } } #endif idb_remove(mapreg_db,num); } mapreg_dirty=1; return 1; } /*========================================== * 文字列型マップ変数の変更 *------------------------------------------ */ int mapreg_setregstr(int num,const char *str) { char *p; #if !defined(TXT_ONLY) && defined(MAPREGSQL) char tmp_str[64]; char tmp_str2[512]; int i=num>>24; // [zBuffer] char *name=str_buf+str_data[num&0x00ffffff].str; #endif if( str==NULL || *str==0 ){ #if !defined(TXT_ONLY) && defined(MAPREGSQL) if(name[1] != '@') { sprintf(tmp_sql,"DELETE FROM `%s` WHERE `%s`='%s' AND `%s`='%d'",mapregsql_db,mapregsql_db_varname,name,mapregsql_db_index,i); if(mysql_query(&mmysql_handle,tmp_sql)){ ShowSQL("DB error - %s\n",mysql_error(&mmysql_handle)); ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); } } #endif idb_remove(mapregstr_db,num); mapreg_dirty=1; return 1; } p=(char *)aMallocA((strlen(str)+1)*sizeof(char)); strcpy(p,str); if (idb_put(mapregstr_db,num,p)) ; #if !defined(TXT_ONLY) && defined(MAPREGSQL) else if(name[1] != '@'){ //put returned null, so we must insert. // Someone is causing a database size infinite increase here without name[1] != '@' [Lance] sprintf(tmp_sql,"INSERT INTO `%s`(`%s`,`%s`,`%s`) VALUES ('%s','%d','%s')",mapregsql_db,mapregsql_db_varname,mapregsql_db_index,mapregsql_db_value,jstrescapecpy(tmp_str,name),i,jstrescapecpy(tmp_str2,p)); if(mysql_query(&mmysql_handle,tmp_sql)){ ShowSQL("DB error - %s\n",mysql_error(&mmysql_handle)); ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); } } #endif mapreg_dirty=1; return 1; } /*========================================== * 永続的マップ変数の読み込み *------------------------------------------ */ static int script_load_mapreg(void) { #if defined(TXT_ONLY) || !defined(MAPREGSQL) FILE *fp; char line[1024]; if( (fp=fopen(mapreg_txt,"rt"))==NULL ) return -1; while(fgets(line,sizeof(line),fp)){ char buf1[256],buf2[1024],*p; int n,v,s,i; if( sscanf(line,"%255[^,],%d\t%n",buf1,&i,&n)!=2 && (i=0,sscanf(line,"%[^\t]\t%n",buf1,&n)!=1) ) continue; if( buf1[strlen(buf1)-1]=='$' ){ if( sscanf(line+n,"%[^\n\r]",buf2)!=1 ){ ShowError("%s: %s broken data !\n",mapreg_txt,buf1); continue; } p=(char *)aMallocA((strlen(buf2) + 1)*sizeof(char)); strcpy(p,buf2); s= add_str(buf1); idb_put(mapregstr_db,(i<<24)|s,p); }else{ if( sscanf(line+n,"%d",&v)!=1 ){ ShowError("%s: %s broken data !\n",mapreg_txt,buf1); continue; } s= add_str(buf1); idb_put(mapreg_db,(i<<24)|s,(void*)v); } } fclose(fp); mapreg_dirty=0; return 0; #else // SQL mapreg code start [zBuffer] /* 0 1 2 +-------------------------+ | varname | index | value | +-------------------------+ */ unsigned int perfomance = (unsigned int)time(NULL); sprintf(tmp_sql,"SELECT * FROM `%s`",mapregsql_db); ShowInfo("Querying script_load_mapreg ...\n"); if(mysql_query(&mmysql_handle, tmp_sql) ) { ShowSQL("DB error - %s\n",mysql_error(&mmysql_handle)); ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); return -1; } ShowInfo("Success! Returning results ...\n"); sql_res = mysql_store_result(&mmysql_handle); if (sql_res) { while ((sql_row = mysql_fetch_row(sql_res))) { char buf1[33], *p = NULL; int i,v,s; strcpy(buf1,sql_row[0]); if( buf1[strlen(buf1)-1]=='$' ){ i = atoi(sql_row[1]); p=(char *)aMallocA((strlen(sql_row[2]) + 1)*sizeof(char)); strcpy(p,sql_row[2]); s= add_str(buf1); idb_put(mapregstr_db,(i<<24)|s,p); }else{ s= add_str(buf1); v= atoi(sql_row[2]); i = atoi(sql_row[1]); idb_put(mapreg_db,(i<<24)|s,(void *)v); } } } ShowInfo("Freeing results...\n"); mysql_free_result(sql_res); mapreg_dirty=0; perfomance = (((unsigned int)time(NULL)) - perfomance); ShowInfo("SQL Mapreg Loading Completed Under %d Seconds.\n",perfomance); return 0; #endif /* TXT_ONLY */ } /*========================================== * 永続的マップ変数の書き込み *------------------------------------------ */ static int script_save_mapreg_intsub(DBKey key,void *data,va_list ap) { #if defined(TXT_ONLY) || !defined(MAPREGSQL) FILE *fp=va_arg(ap,FILE*); int num=key.i&0x00ffffff, i=key.i>>24; char *name=str_buf+str_data[num].str; if( name[1]!='@' ){ if(i==0) fprintf(fp,"%s\t%d\n", name, (int)data); else fprintf(fp,"%s,%d\t%d\n", name, i, (int)data); } return 0; #else int num=key.i&0x00ffffff, i=key.i>>24; // [zBuffer] char *name=str_buf+str_data[num].str; if ( name[1] != '@') { sprintf(tmp_sql,"UPDATE `%s` SET `%s`='%d' WHERE `%s`='%s' AND `%s`='%d'",mapregsql_db,mapregsql_db_value,(int)data,mapregsql_db_varname,name,mapregsql_db_index,i); if(mysql_query(&mmysql_handle, tmp_sql) ) { ShowSQL("DB error - %s\n",mysql_error(&mmysql_handle)); ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); } } return 0; #endif } static int script_save_mapreg_strsub(DBKey key,void *data,va_list ap) { #if defined(TXT_ONLY) || !defined(MAPREGSQL) FILE *fp=va_arg(ap,FILE*); int num=key.i&0x00ffffff, i=key.i>>24; char *name=str_buf+str_data[num].str; if( name[1]!='@' ){ if(i==0) fprintf(fp,"%s\t%s\n", name, (char *)data); else fprintf(fp,"%s,%d\t%s\n", name, i, (char *)data); } return 0; #else char tmp_str2[512]; int num=key.i&0x00ffffff, i=key.i>>24; char *name=str_buf+str_data[num].str; if ( name[1] != '@') { sprintf(tmp_sql,"UPDATE `%s` SET `%s`='%s' WHERE `%s`='%s' AND `%s`='%d'",mapregsql_db,mapregsql_db_value,jstrescapecpy(tmp_str2,(char *)data),mapregsql_db_varname,name,mapregsql_db_index,i); if(mysql_query(&mmysql_handle, tmp_sql) ) { ShowSQL("DB error - %s\n",mysql_error(&mmysql_handle)); ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); } } return 0; #endif } static int script_save_mapreg(void) { #if defined(TXT_ONLY) || !defined(MAPREGSQL) FILE *fp; int lock; if( (fp=lock_fopen(mapreg_txt,&lock))==NULL ) { ShowError("script_save_mapreg: Unable to lock-open file [%s]\n",mapreg_txt); return -1; } mapreg_db->foreach(mapreg_db,script_save_mapreg_intsub,fp); mapregstr_db->foreach(mapregstr_db,script_save_mapreg_strsub,fp); lock_fclose(fp,mapreg_txt,&lock); #else unsigned int perfomance = (unsigned int)time(NULL); mapreg_db->foreach(mapreg_db,script_save_mapreg_intsub); // [zBuffer] mapregstr_db->foreach(mapregstr_db,script_save_mapreg_strsub); perfomance = ((unsigned int)time(NULL) - perfomance); if(perfomance > 2) ShowWarning("Slow Query: MapregSQL Saving @ %d second(s).\n", perfomance); #endif mapreg_dirty=0; return 0; } static int script_autosave_mapreg(int tid,unsigned int tick,int id,int data) { if(mapreg_dirty) if (script_save_mapreg() == -1) ShowError("Failed to save the mapreg data!\n"); return 0; } /*========================================== * *------------------------------------------ */ static int set_posword(char *p) { char* np,* str[15]; int i=0; for(i=0;i<11;i++) { if((np=strchr(p,','))!=NULL) { str[i]=p; *np=0; p=np+1; } else { str[i]=p; p+=strlen(p); } if(str[i]) strcpy(pos[i],str[i]); } return 0; } int script_config_read_sub(char *cfgName) { int i; char line[1024],w1[1024],w2[1024]; FILE *fp; fp=fopen(cfgName,"r"); if(fp==NULL){ ShowError("file not found: [%s]\n", cfgName); return 1; } while(fgets(line,sizeof(line)-1,fp)){ if(line[0] == '/' && line[1] == '/') continue; i=sscanf(line,"%[^:]: %[^\r\n]",w1,w2); if(i!=2) continue; if(strcmpi(w1,"refine_posword")==0) { set_posword(w2); } else if(strcmpi(w1,"verbose_mode")==0) { script_config.verbose_mode = config_switch(w2); } else 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,"event_script_type")==0) { script_config.event_script_type = config_switch(w2); } else if(strcmpi(w1,"event_requires_trigger")==0) { script_config.event_requires_trigger = config_switch(w2); } else if(strcmpi(w1,"die_event_name")==0) { strncpy(script_config.die_event_name, w2, NAME_LENGTH-1); if (strlen(script_config.die_event_name) != strlen(w2)) ShowWarning("script_config_read: Event label truncated (max length is 23 chars): %d\n", script_config.die_event_name); } else if(strcmpi(w1,"kill_pc_event_name")==0) { strncpy(script_config.kill_pc_event_name, w2, NAME_LENGTH-1); if (strlen(script_config.kill_pc_event_name) != strlen(w2)) ShowWarning("script_config_read: Event label truncated (max length is 23 chars): %d\n", script_config.kill_pc_event_name); } else if(strcmpi(w1,"kill_mob_event_name")==0) { strncpy(script_config.kill_mob_event_name, w2, NAME_LENGTH-1); if (strlen(script_config.kill_mob_event_name) != strlen(w2)) ShowWarning("script_config_read: Event label truncated (max length is 23 chars): %d\n", script_config.kill_mob_event_name); } else if(strcmpi(w1,"login_event_name")==0) { strncpy(script_config.login_event_name, w2, NAME_LENGTH-1); if (strlen(script_config.login_event_name) != strlen(w2)) ShowWarning("script_config_read: Event label truncated (max length is 23 chars): %d\n", script_config.login_event_name); } else if(strcmpi(w1,"logout_event_name")==0) { strncpy(script_config.logout_event_name, w2, NAME_LENGTH-1); if (strlen(script_config.logout_event_name) != strlen(w2)) ShowWarning("script_config_read: Event label truncated (max length is 23 chars): %d\n", script_config.logout_event_name); } else if(strcmpi(w1,"loadmap_event_name")==0) { strncpy(script_config.loadmap_event_name, w2, NAME_LENGTH-1); if (strlen(script_config.loadmap_event_name) != strlen(w2)) ShowWarning("script_config_read: Event label truncated (max length is 23 chars): %d\n", script_config.loadmap_event_name); } else if(strcmpi(w1,"baselvup_event_name")==0) { strncpy(script_config.baselvup_event_name, w2, NAME_LENGTH-1); if (strlen(script_config.baselvup_event_name) != strlen(w2)) ShowWarning("script_config_read: Event label truncated (max length is 23 chars): %d\n", script_config.baselvup_event_name); } else if(strcmpi(w1,"joblvup_event_name")==0) { strncpy(script_config.joblvup_event_name, w2, NAME_LENGTH-1); if (strlen(script_config.joblvup_event_name) != strlen(w2)) ShowWarning("script_config_read: Event label truncated (max length is 23 chars): %d\n", script_config.joblvup_event_name); } else if(strcmpi(w1,"import")==0){ script_config_read_sub(w2); } } fclose(fp); return 0; } int script_config_read(char *cfgName) { //Script related variables should be initialized once! [Skotlex] memset (&script_config, 0, sizeof(script_config)); script_config.verbose_mode = 0; script_config.warn_func_mismatch_paramnum = 1; script_config.check_cmdcount = 65535; script_config.check_gotocount = 2048; script_config.event_script_type = 0; script_config.event_requires_trigger = 1; return script_config_read_sub(cfgName); } static int do_final_userfunc_sub (DBKey key,void *data,va_list ap) { struct script_code *code = (struct script_code *)data; if(code){ script_free_vars( &code->script_vars ); aFree( code->script_buf ); } return 0; } /*========================================== * 終了 *------------------------------------------ */ int do_final_script() { #ifdef DEBUG_RUN if (battle_config.etc_log) { FILE *fp = fopen("hash_dump.txt","wt"); if(fp) { int i,count[SCRIPT_HASH_SIZE]; int count2[SCRIPT_HASH_SIZE]; // number of buckets with a certain number of items int n=0; int min=INT_MAX,max=0,zero=0; double mean=0.0f; double median=0.0f; ShowNotice("Dumping script str hash information to hash_dump.txt\n"); memset(count, 0, sizeof(count)); fprintf(fp,"num : calced_val -> hash : data_name\n"); fprintf(fp,"---------------------------------------------------------------\n"); for(i=LABEL_START; i %3u : %s\n",i,h2,h,str_buf+str_data[i].str); ++count[h]; } fprintf(fp,"--------------------\n\n"); memset(count2, 0, sizeof(count2)); for(i=0; i count[i]) min = count[i]; // minimun count of collision if(max < count[i]) max = count[i]; // maximun count of collision if(count[i] == 0) zero++; ++count2[count[i]]; } fprintf(fp,"\n--------------------\n items : buckets\n--------------------\n"); for( i=min; i <= max; ++i ){ fprintf(fp," %5d : %7d\n",i,count2[i]); mean += 1.0f*i*count2[i]/SCRIPT_HASH_SIZE; // Note: this will always result in / } for( i=min; i <= max; ++i ){ n += count2[i]; if( n*2 >= SCRIPT_HASH_SIZE ) { if( SCRIPT_HASH_SIZE%2 == 0 && SCRIPT_HASH_SIZE/2 == n ) median = (i+i+1)/2.0f; else median = i; break; } } fprintf(fp,"--------------------\n min = %d, max = %d, zero = %d\n mean = %lf, median = %lf\n",min,max,zero,mean,median); fclose(fp); } } #endif if(mapreg_dirty>=0) script_save_mapreg(); mapreg_db->destroy(mapreg_db,NULL); mapregstr_db->destroy(mapregstr_db,NULL); scriptlabel_db->destroy(scriptlabel_db,NULL); userfunc_db->destroy(userfunc_db,do_final_userfunc_sub); if(sleep_db) { struct linkdb_node *n = (struct linkdb_node *)sleep_db; while(n) { struct script_state *st = (struct script_state *)n->data; script_free_stack(st->stack); aFree(st); n = n->next; } linkdb_final(&sleep_db); } if (str_data) aFree(str_data); if (str_buf) aFree(str_buf); return 0; } /*========================================== * 初期化 *------------------------------------------ */ int do_init_script() { mapreg_db= db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_BASE,sizeof(int)); mapregstr_db=db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int)); userfunc_db=db_alloc(__FILE__,__LINE__,DB_STRING,DB_OPT_RELEASE_BOTH,50); scriptlabel_db=db_alloc(__FILE__,__LINE__,DB_STRING,DB_OPT_DUP_KEY|DB_OPT_ALLOW_NULL_DATA,50); script_load_mapreg(); add_timer_func_list(script_autosave_mapreg,"script_autosave_mapreg"); add_timer_interval(gettick()+MAPREG_AUTOSAVE_INTERVAL, script_autosave_mapreg,0,0,MAPREG_AUTOSAVE_INTERVAL); return 0; } int script_reload() { if(mapreg_dirty>=0) script_save_mapreg(); mapreg_db->clear(mapreg_db, NULL); mapregstr_db->clear(mapregstr_db, NULL); userfunc_db->clear(userfunc_db,do_final_userfunc_sub); scriptlabel_db->clear(scriptlabel_db, NULL); if(sleep_db) { struct linkdb_node *n = (struct linkdb_node *)sleep_db; while(n) { struct script_state *st = (struct script_state *)n->data; if( st->sleep.timer != INVALID_TIMER ) delete_timer(st->sleep.timer, run_script_timer); script_free_stack(st->stack); aFree(st); n = n->next; } linkdb_final(&sleep_db); } script_load_mapreg(); return 0; } //----------------------------------------------------------------------------- // buildin functions // #define BUILDIN_DEF(x,args) { buildin_ ## x , #x , args } #define BUILDIN_DEF2(x,x2,args) { buildin_ ## x , x2 , args } #define BUILDIN_FUNC(x) int buildin_ ## x (struct script_state* st) BUILDIN_FUNC(mes); BUILDIN_FUNC(goto); BUILDIN_FUNC(callsub); BUILDIN_FUNC(callfunc); BUILDIN_FUNC(return); BUILDIN_FUNC(getarg); BUILDIN_FUNC(next); BUILDIN_FUNC(close); BUILDIN_FUNC(close2); BUILDIN_FUNC(menu); BUILDIN_FUNC(rand); BUILDIN_FUNC(warp); BUILDIN_FUNC(areawarp); BUILDIN_FUNC(warpchar); // [LuzZza] BUILDIN_FUNC(warpparty); //[Fredzilla] BUILDIN_FUNC(warpguild); //[Fredzilla] BUILDIN_FUNC(heal); BUILDIN_FUNC(itemheal); BUILDIN_FUNC(percentheal); BUILDIN_FUNC(jobchange); BUILDIN_FUNC(jobname); BUILDIN_FUNC(input); BUILDIN_FUNC(setlook); BUILDIN_FUNC(set); BUILDIN_FUNC(setarray); BUILDIN_FUNC(cleararray); BUILDIN_FUNC(copyarray); BUILDIN_FUNC(getarraysize); BUILDIN_FUNC(deletearray); BUILDIN_FUNC(getelementofarray); BUILDIN_FUNC(getitem); BUILDIN_FUNC(getitem2); BUILDIN_FUNC(getnameditem); BUILDIN_FUNC(grouprandomitem); BUILDIN_FUNC(makeitem); BUILDIN_FUNC(delitem); BUILDIN_FUNC(delitem2); BUILDIN_FUNC(enableitemuse); BUILDIN_FUNC(disableitemuse); BUILDIN_FUNC(viewpoint); BUILDIN_FUNC(countitem); BUILDIN_FUNC(countitem2); BUILDIN_FUNC(checkweight); BUILDIN_FUNC(readparam); BUILDIN_FUNC(getcharid); BUILDIN_FUNC(getpartyname); BUILDIN_FUNC(getpartymember); BUILDIN_FUNC(getpartyleader); BUILDIN_FUNC(getguildname); BUILDIN_FUNC(getguildmaster); BUILDIN_FUNC(getguildmasterid); BUILDIN_FUNC(strcharinfo); BUILDIN_FUNC(getequipid); BUILDIN_FUNC(getequipname); BUILDIN_FUNC(getbrokenid); // [Valaris] BUILDIN_FUNC(repair); // [Valaris] BUILDIN_FUNC(getequipisequiped); BUILDIN_FUNC(getequipisenableref); BUILDIN_FUNC(getequipisidentify); BUILDIN_FUNC(getequiprefinerycnt); BUILDIN_FUNC(getequipweaponlv); BUILDIN_FUNC(getequippercentrefinery); BUILDIN_FUNC(successrefitem); BUILDIN_FUNC(failedrefitem); BUILDIN_FUNC(cutin); BUILDIN_FUNC(statusup); BUILDIN_FUNC(statusup2); BUILDIN_FUNC(bonus); BUILDIN_FUNC(bonus2); BUILDIN_FUNC(bonus3); BUILDIN_FUNC(bonus4); BUILDIN_FUNC(skill); BUILDIN_FUNC(addtoskill); // [Valaris] BUILDIN_FUNC(guildskill); BUILDIN_FUNC(getskilllv); BUILDIN_FUNC(getgdskilllv); BUILDIN_FUNC(basicskillcheck); BUILDIN_FUNC(getgmlevel); BUILDIN_FUNC(end); BUILDIN_FUNC(checkoption); BUILDIN_FUNC(setoption); BUILDIN_FUNC(setcart); BUILDIN_FUNC(checkcart); // check cart [Valaris] BUILDIN_FUNC(setfalcon); BUILDIN_FUNC(checkfalcon); // check falcon [Valaris] BUILDIN_FUNC(setriding); BUILDIN_FUNC(checkriding); // check for pecopeco [Valaris] BUILDIN_FUNC(savepoint); BUILDIN_FUNC(gettimetick); BUILDIN_FUNC(gettime); BUILDIN_FUNC(gettimestr); BUILDIN_FUNC(openstorage); BUILDIN_FUNC(guildopenstorage); BUILDIN_FUNC(itemskill); BUILDIN_FUNC(produce); BUILDIN_FUNC(monster); BUILDIN_FUNC(areamonster); BUILDIN_FUNC(killmonster); BUILDIN_FUNC(killmonsterall); BUILDIN_FUNC(clone); BUILDIN_FUNC(doevent); BUILDIN_FUNC(donpcevent); BUILDIN_FUNC(addtimer); BUILDIN_FUNC(deltimer); BUILDIN_FUNC(addtimercount); BUILDIN_FUNC(initnpctimer); BUILDIN_FUNC(stopnpctimer); BUILDIN_FUNC(startnpctimer); BUILDIN_FUNC(setnpctimer); BUILDIN_FUNC(getnpctimer); BUILDIN_FUNC(attachnpctimer); // [celest] BUILDIN_FUNC(detachnpctimer); // [celest] BUILDIN_FUNC(playerattached); // [Skotlex] BUILDIN_FUNC(announce); BUILDIN_FUNC(mapannounce); BUILDIN_FUNC(areaannounce); BUILDIN_FUNC(getusers); BUILDIN_FUNC(getmapguildusers); BUILDIN_FUNC(getmapusers); BUILDIN_FUNC(getareausers); BUILDIN_FUNC(getareadropitem); BUILDIN_FUNC(enablenpc); BUILDIN_FUNC(disablenpc); BUILDIN_FUNC(enablearena); // Added by RoVeRT BUILDIN_FUNC(disablearena); // Added by RoVeRT BUILDIN_FUNC(hideoffnpc); BUILDIN_FUNC(hideonnpc); BUILDIN_FUNC(sc_start); BUILDIN_FUNC(sc_start2); BUILDIN_FUNC(sc_start4); BUILDIN_FUNC(sc_end); BUILDIN_FUNC(getscrate); BUILDIN_FUNC(debugmes); BUILDIN_FUNC(catchpet); BUILDIN_FUNC(birthpet); BUILDIN_FUNC(resetlvl); BUILDIN_FUNC(resetstatus); BUILDIN_FUNC(resetskill); BUILDIN_FUNC(skillpointcount); BUILDIN_FUNC(changebase); BUILDIN_FUNC(changesex); BUILDIN_FUNC(waitingroom); BUILDIN_FUNC(delwaitingroom); BUILDIN_FUNC(waitingroomkickall); BUILDIN_FUNC(enablewaitingroomevent); BUILDIN_FUNC(disablewaitingroomevent); BUILDIN_FUNC(getwaitingroomstate); BUILDIN_FUNC(warpwaitingpc); BUILDIN_FUNC(attachrid); BUILDIN_FUNC(detachrid); BUILDIN_FUNC(isloggedin); BUILDIN_FUNC(setmapflagnosave); BUILDIN_FUNC(setmapflag); BUILDIN_FUNC(removemapflag); BUILDIN_FUNC(pvpon); BUILDIN_FUNC(pvpoff); BUILDIN_FUNC(gvgon); BUILDIN_FUNC(gvgoff); BUILDIN_FUNC(emotion); BUILDIN_FUNC(maprespawnguildid); BUILDIN_FUNC(agitstart); // BUILDIN_FUNC(agitend); BUILDIN_FUNC(agitcheck); // BUILDIN_FUNC(flagemblem); // Flag Emblem BUILDIN_FUNC(getcastlename); BUILDIN_FUNC(getcastledata); BUILDIN_FUNC(setcastledata); BUILDIN_FUNC(requestguildinfo); BUILDIN_FUNC(getequipcardcnt); BUILDIN_FUNC(successremovecards); BUILDIN_FUNC(failedremovecards); BUILDIN_FUNC(marriage); BUILDIN_FUNC(wedding_effect); BUILDIN_FUNC(divorce); BUILDIN_FUNC(ispartneron); // MouseJstr BUILDIN_FUNC(getpartnerid); // MouseJstr BUILDIN_FUNC(getchildid); // Skotlex BUILDIN_FUNC(getmotherid); // Lupus BUILDIN_FUNC(getfatherid); // Lupus BUILDIN_FUNC(warppartner); // MouseJstr BUILDIN_FUNC(getitemname); BUILDIN_FUNC(getitemslots); BUILDIN_FUNC(makepet); BUILDIN_FUNC(getexp); BUILDIN_FUNC(getinventorylist); BUILDIN_FUNC(getskilllist); BUILDIN_FUNC(clearitem); BUILDIN_FUNC(classchange); BUILDIN_FUNC(misceffect); BUILDIN_FUNC(soundeffect); BUILDIN_FUNC(soundeffectall); BUILDIN_FUNC(setcastledata); BUILDIN_FUNC(mapwarp); BUILDIN_FUNC(inittimer); BUILDIN_FUNC(stoptimer); BUILDIN_FUNC(cmdothernpc); BUILDIN_FUNC(mobcount); BUILDIN_FUNC(strmobinfo); // Script for displaying mob info [Valaris] BUILDIN_FUNC(guardian); // Script for displaying mob info [Valaris] BUILDIN_FUNC(guardianinfo); // Script for displaying mob info [Valaris] BUILDIN_FUNC(petskillbonus); // petskillbonus [Valaris] BUILDIN_FUNC(petrecovery); // pet skill for curing status [Valaris] BUILDIN_FUNC(petloot); // pet looting [Valaris] BUILDIN_FUNC(petheal); // pet healing [Valaris] //BUILDIN_FUNC(petmag); // pet magnificat [Valaris] BUILDIN_FUNC(petskillattack); // pet skill attacks [Skotlex] BUILDIN_FUNC(petskillattack2); // pet skill attacks [Skotlex] BUILDIN_FUNC(petskillsupport); // pet support skill [Valaris] BUILDIN_FUNC(skilleffect); // skill effects [Celest] BUILDIN_FUNC(npcskilleffect); // skill effects for npcs [Valaris] BUILDIN_FUNC(specialeffect); // special effect script [Valaris] BUILDIN_FUNC(specialeffect2); // special effect script [Valaris] BUILDIN_FUNC(nude); // nude [Valaris] BUILDIN_FUNC(atcommand); // [MouseJstr] BUILDIN_FUNC(charcommand); // [MouseJstr] BUILDIN_FUNC(movenpc); // [MouseJstr] BUILDIN_FUNC(message); // [MouseJstr] BUILDIN_FUNC(npctalk); // [Valaris] BUILDIN_FUNC(hasitems); // [Valaris] BUILDIN_FUNC(getlook); //Lorky [Lupus] BUILDIN_FUNC(getsavepoint); //Lorky [Lupus] BUILDIN_FUNC(npcspeed); // [Valaris] BUILDIN_FUNC(npcwalkto); // [Valaris] BUILDIN_FUNC(npcstop); // [Valaris] BUILDIN_FUNC(getmapxy); //get map position for player/npc/pet/mob by Lorky [Lupus] BUILDIN_FUNC(checkoption1); // [celest] BUILDIN_FUNC(checkoption2); // [celest] BUILDIN_FUNC(guildgetexp); // [celest] BUILDIN_FUNC(guildchangegm); // [Skotlex] BUILDIN_FUNC(logmes); // [Lupus] BUILDIN_FUNC(summon); // [celest] BUILDIN_FUNC(isnight); // [celest] BUILDIN_FUNC(isday); // [celest] BUILDIN_FUNC(isequipped); // [celest] BUILDIN_FUNC(isequippedcnt); // [celest] BUILDIN_FUNC(cardscnt); // [Lupus] BUILDIN_FUNC(getrefine); // [celest] BUILDIN_FUNC(adopt); BUILDIN_FUNC(night); BUILDIN_FUNC(day); BUILDIN_FUNC(getusersname); //jA commands added [Lupus] BUILDIN_FUNC(dispbottom); BUILDIN_FUNC(recovery); BUILDIN_FUNC(getpetinfo); BUILDIN_FUNC(checkequipedcard); BUILDIN_FUNC(globalmes); BUILDIN_FUNC(jump_zero); BUILDIN_FUNC(select); BUILDIN_FUNC(prompt); BUILDIN_FUNC(getmapmobs); //jA addition end BUILDIN_FUNC(unequip); // unequip [Spectre] BUILDIN_FUNC(getstrlen); //strlen [valaris] BUILDIN_FUNC(charisalpha);//isalpha [valaris] BUILDIN_FUNC(fakenpcname); // [Lance] BUILDIN_FUNC(compare); // Lordalfa, to bring strstr to Scripting Engine BUILDIN_FUNC(getiteminfo); //[Lupus] returns Items Buy / sell Price, etc info BUILDIN_FUNC(setiteminfo); //[Lupus] set Items Buy / sell Price, etc info BUILDIN_FUNC(getequipcardid); //[Lupus] returns card id from quipped item card slot N // [zBuffer] List of mathematics commands ---> BUILDIN_FUNC(sqrt); BUILDIN_FUNC(pow); BUILDIN_FUNC(distance); BUILDIN_FUNC(checkcell); // <--- [zBuffer] List of mathematics commands // [zBuffer] List of dynamic var commands ---> BUILDIN_FUNC(getd); BUILDIN_FUNC(setd); // <--- [zBuffer] List of dynamic var commands BUILDIN_FUNC(petstat); // [Lance] Pet Stat Rq: Dubby BUILDIN_FUNC(callshop); // [Skotlex] BUILDIN_FUNC(npcshopitem); // [Lance] BUILDIN_FUNC(npcshopadditem); BUILDIN_FUNC(npcshopdelitem); BUILDIN_FUNC(npcshopattach); BUILDIN_FUNC(equip); BUILDIN_FUNC(autoequip); BUILDIN_FUNC(setbattleflag); BUILDIN_FUNC(getbattleflag); BUILDIN_FUNC(query_sql); BUILDIN_FUNC(escape_sql); BUILDIN_FUNC(atoi); BUILDIN_FUNC(axtoi); // [zBuffer] List of player cont commands ---> BUILDIN_FUNC(rid2name); BUILDIN_FUNC(pcfollow); BUILDIN_FUNC(pcstopfollow); BUILDIN_FUNC(pcblockmove); // <--- [zBuffer] List of player cont commands // [zBuffer] List of mob control commands ---> BUILDIN_FUNC(mobspawn); BUILDIN_FUNC(mobremove); BUILDIN_FUNC(getmobdata); BUILDIN_FUNC(setmobdata); BUILDIN_FUNC(mobassist); BUILDIN_FUNC(mobattach); BUILDIN_FUNC(unitwalk); BUILDIN_FUNC(unitkill); BUILDIN_FUNC(unitwarp); BUILDIN_FUNC(unitattack); BUILDIN_FUNC(unitstop); BUILDIN_FUNC(unittalk); BUILDIN_FUNC(unitemote); BUILDIN_FUNC(unitdeadsit); BUILDIN_FUNC(unitskilluseid); // originally by Qamera [celest] BUILDIN_FUNC(unitskillusepos); // originally by Qamera [celest] // <--- [zBuffer] List of mob control commands BUILDIN_FUNC(sleep); BUILDIN_FUNC(sleep2); BUILDIN_FUNC(awake); BUILDIN_FUNC(getvariableofnpc); // [blackhole89] --> BUILDIN_FUNC(warpportal); // <-- [blackhole89] BUILDIN_FUNC(homunculus_evolution) ; //[orn] BUILDIN_FUNC(eaclass); BUILDIN_FUNC(roclass); BUILDIN_FUNC(setitemscript); BUILDIN_FUNC(disguise); BUILDIN_FUNC(undisguise); BUILDIN_FUNC(getmonsterinfo); // [Lupus] BUILDIN_FUNC(checkvending); // check vending [Nab4] BUILDIN_FUNC(checkchatting); // check chatting [Marka] #ifdef PCRE_SUPPORT BUILDIN_FUNC(defpattern); // MouseJstr BUILDIN_FUNC(activatepset); // MouseJstr BUILDIN_FUNC(deactivatepset); // MouseJstr BUILDIN_FUNC(deletepset); // MouseJstr #endif struct script_function buildin_func[] = { BUILDIN_DEF(mes,"s"), BUILDIN_DEF(next,""), BUILDIN_DEF(close,""), BUILDIN_DEF(close2,""), BUILDIN_DEF(menu,"*"), BUILDIN_DEF(goto,"l"), BUILDIN_DEF(callsub,"i*"), BUILDIN_DEF(callfunc,"s*"), BUILDIN_DEF(return,"*"), BUILDIN_DEF(getarg,"i"), BUILDIN_DEF(jobchange,"i*"), BUILDIN_DEF(jobname,"i"), BUILDIN_DEF(input,"v"), BUILDIN_DEF(warp,"sii"), BUILDIN_DEF(areawarp,"siiiisii"), BUILDIN_DEF(warpchar,"siii"), // [LuzZza] BUILDIN_DEF(warpparty,"siii"), // [Fredzilla] BUILDIN_DEF(warpguild,"siii"), // [Fredzilla] BUILDIN_DEF(setlook,"ii"), BUILDIN_DEF(set,"ii"), BUILDIN_DEF(setarray,"ii*"), BUILDIN_DEF(cleararray,"iii"), BUILDIN_DEF(copyarray,"iii"), BUILDIN_DEF(getarraysize,"i"), BUILDIN_DEF(deletearray,"ii"), BUILDIN_DEF(getelementofarray,"ii"), BUILDIN_DEF(getitem,"vi?"), BUILDIN_DEF(getitem2,"iiiiiiiii*"), BUILDIN_DEF(getnameditem,"is"), BUILDIN_DEF2(grouprandomitem,"groupranditem","i"), BUILDIN_DEF(makeitem,"iisii"), BUILDIN_DEF(delitem,"ii"), BUILDIN_DEF(delitem2,"iiiiiiiii"), BUILDIN_DEF2(enableitemuse,"enable_items",""), BUILDIN_DEF2(disableitemuse,"disable_items",""), BUILDIN_DEF(cutin,"si"), BUILDIN_DEF(viewpoint,"iiiii"), BUILDIN_DEF(heal,"ii"), BUILDIN_DEF(itemheal,"ii"), BUILDIN_DEF(percentheal,"ii"), BUILDIN_DEF(rand,"i?"), BUILDIN_DEF(countitem,"i"), BUILDIN_DEF(countitem2,"iiiiiiii"), BUILDIN_DEF(checkweight,"ii"), BUILDIN_DEF(readparam,"i*"), BUILDIN_DEF(getcharid,"i*"), 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(strcharinfo,"i"), BUILDIN_DEF(getequipid,"i"), BUILDIN_DEF(getequipname,"i"), BUILDIN_DEF(getbrokenid,"i"), // [Valaris] BUILDIN_DEF(repair,"i"), // [Valaris] BUILDIN_DEF(getequipisequiped,"i"), BUILDIN_DEF(getequipisenableref,"i"), BUILDIN_DEF(getequipisidentify,"i"), BUILDIN_DEF(getequiprefinerycnt,"i"), BUILDIN_DEF(getequipweaponlv,"i"), BUILDIN_DEF(getequippercentrefinery,"i"), BUILDIN_DEF(successrefitem,"i"), BUILDIN_DEF(failedrefitem,"i"), BUILDIN_DEF(statusup,"i"), BUILDIN_DEF(statusup2,"ii"), BUILDIN_DEF(bonus,"ii"), BUILDIN_DEF2(bonus,"bonus2","iii"), BUILDIN_DEF2(bonus,"bonus3","iiii"), BUILDIN_DEF2(bonus,"bonus4","iiiii"), BUILDIN_DEF(skill,"ii?"), BUILDIN_DEF(addtoskill,"ii?"), // [Valaris] BUILDIN_DEF(guildskill,"ii"), BUILDIN_DEF(getskilllv,"i"), BUILDIN_DEF(getgdskilllv,"ii"), BUILDIN_DEF(basicskillcheck,""), BUILDIN_DEF(getgmlevel,""), BUILDIN_DEF(end,""), // BUILDIN_DEF2(end,"break",""), this might confuse advanced scripting support [Eoe] BUILDIN_DEF(checkoption,"i"), BUILDIN_DEF(setoption,"i?"), BUILDIN_DEF(setcart,"?"), BUILDIN_DEF(checkcart,""), BUILDIN_DEF(setfalcon,"?"), BUILDIN_DEF(checkfalcon,""), BUILDIN_DEF(setriding,"?"), BUILDIN_DEF(checkriding,""), BUILDIN_DEF2(savepoint,"save","sii"), BUILDIN_DEF(savepoint,"sii"), BUILDIN_DEF(gettimetick,"i"), BUILDIN_DEF(gettime,"i"), BUILDIN_DEF(gettimestr,"si"), BUILDIN_DEF(openstorage,""), BUILDIN_DEF(guildopenstorage,"*"), BUILDIN_DEF(itemskill,"ii"), BUILDIN_DEF(produce,"i"), BUILDIN_DEF(monster,"siisii*"), BUILDIN_DEF(areamonster,"siiiisii*"), BUILDIN_DEF(killmonster,"ss"), BUILDIN_DEF(killmonsterall,"s"), 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(initnpctimer,"??"), BUILDIN_DEF(stopnpctimer,"??"), BUILDIN_DEF(startnpctimer,"??"), BUILDIN_DEF(setnpctimer,"i?"), BUILDIN_DEF(getnpctimer,"i?"), 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(announce,"si*"), BUILDIN_DEF(mapannounce,"ssi*"), BUILDIN_DEF(areaannounce,"siiiisi*"), BUILDIN_DEF(getusers,"i"), BUILDIN_DEF(getmapguildusers,"si"), BUILDIN_DEF(getmapusers,"s"), BUILDIN_DEF(getareausers,"siiii"), BUILDIN_DEF(getareadropitem,"siiiii"), BUILDIN_DEF(enablenpc,"s"), BUILDIN_DEF(disablenpc,"s"), BUILDIN_DEF(enablearena,""), // Added by RoVeRT BUILDIN_DEF(disablearena,""), // Added by RoVeRT BUILDIN_DEF(hideoffnpc,"s"), BUILDIN_DEF(hideonnpc,"s"), BUILDIN_DEF(sc_start,"iii*"), BUILDIN_DEF(sc_start2,"iiii*"), BUILDIN_DEF(sc_start4,"iiiiii*"), BUILDIN_DEF(sc_end,"i"), BUILDIN_DEF(getscrate,"ii*"), BUILDIN_DEF(debugmes,"s"), BUILDIN_DEF2(catchpet,"pet","i"), BUILDIN_DEF2(birthpet,"bpet",""), BUILDIN_DEF(resetlvl,"i"), BUILDIN_DEF(resetstatus,""), BUILDIN_DEF(resetskill,""), BUILDIN_DEF(skillpointcount,""), BUILDIN_DEF(changebase,"i"), BUILDIN_DEF(changesex,""), BUILDIN_DEF(waitingroom,"si*"), BUILDIN_DEF(delwaitingroom,"*"), BUILDIN_DEF2(waitingroomkickall,"kickwaitingroomall","*"), BUILDIN_DEF(enablewaitingroomevent,"*"), BUILDIN_DEF(disablewaitingroomevent,"*"), BUILDIN_DEF(getwaitingroomstate,"i*"), BUILDIN_DEF(warpwaitingpc,"sii?"), BUILDIN_DEF(attachrid,"i"), BUILDIN_DEF(detachrid,""), BUILDIN_DEF(isloggedin,"i"), BUILDIN_DEF(setmapflagnosave,"ssii"), BUILDIN_DEF(setmapflag,"si*"), BUILDIN_DEF(removemapflag,"si"), BUILDIN_DEF(pvpon,"s"), BUILDIN_DEF(pvpoff,"s"), BUILDIN_DEF(gvgon,"s"), BUILDIN_DEF(gvgoff,"s"), BUILDIN_DEF(emotion,"i*"), BUILDIN_DEF(maprespawnguildid,"sii"), BUILDIN_DEF(agitstart,""), // BUILDIN_DEF(agitend,""), BUILDIN_DEF(agitcheck,""), // BUILDIN_DEF(flagemblem,"i"), // Flag Emblem BUILDIN_DEF(getcastlename,"s"), BUILDIN_DEF(getcastledata,"si*"), BUILDIN_DEF(setcastledata,"sii"), BUILDIN_DEF(requestguildinfo,"i*"), BUILDIN_DEF(getequipcardcnt,"i"), BUILDIN_DEF(successremovecards,"i"), BUILDIN_DEF(failedremovecards,"ii"), BUILDIN_DEF(marriage,"s"), BUILDIN_DEF2(wedding_effect,"wedding",""), BUILDIN_DEF(divorce,""), BUILDIN_DEF(ispartneron,""), BUILDIN_DEF(getpartnerid,""), BUILDIN_DEF(getchildid,""), BUILDIN_DEF(getmotherid,""), BUILDIN_DEF(getfatherid,""), BUILDIN_DEF(warppartner,"sii"), BUILDIN_DEF(getitemname,"i"), BUILDIN_DEF(getitemslots,"i"), BUILDIN_DEF(makepet,"i"), BUILDIN_DEF(getexp,"ii"), BUILDIN_DEF(getinventorylist,""), BUILDIN_DEF(getskilllist,""), BUILDIN_DEF(clearitem,""), BUILDIN_DEF(classchange,"ii"), BUILDIN_DEF(misceffect,"i"), BUILDIN_DEF(soundeffect,"si"), BUILDIN_DEF(soundeffectall,"si*"), // SoundEffectAll [Codemaster] BUILDIN_DEF(strmobinfo,"ii"), // display mob data [Valaris] BUILDIN_DEF(guardian,"siisi??"), // summon guardians BUILDIN_DEF(guardianinfo,"i"), // display guardian data [Valaris] BUILDIN_DEF(petskillbonus,"iiii"), // [Valaris] BUILDIN_DEF(petrecovery,"ii"), // [Valaris] BUILDIN_DEF(petloot,"i"), // [Valaris] BUILDIN_DEF(petheal,"iiii"), // [Valaris] // BUILDIN_DEF(petmag,"iiii"), // [Valaris] BUILDIN_DEF(petskillattack,"iiii"), // [Skotlex] BUILDIN_DEF(petskillattack2,"iiiii"), // [Valaris] BUILDIN_DEF(petskillsupport,"iiiii"), // [Skotlex] BUILDIN_DEF(skilleffect,"ii"), // skill effect [Celest] BUILDIN_DEF(npcskilleffect,"iiii"), // npc skill effect [Valaris] BUILDIN_DEF(specialeffect,"i*"), // npc skill effect [Valaris] BUILDIN_DEF(specialeffect2,"i*"), // skill effect on players[Valaris] BUILDIN_DEF(nude,""), // nude command [Valaris] BUILDIN_DEF(mapwarp,"ssii*"), // Added by RoVeRT BUILDIN_DEF(inittimer,""), BUILDIN_DEF(stoptimer,""), BUILDIN_DEF(cmdothernpc,"ss"), BUILDIN_DEF(atcommand,"*"), // [MouseJstr] BUILDIN_DEF(charcommand,"*"), // [MouseJstr] BUILDIN_DEF(movenpc,"sii"), // [MouseJstr] BUILDIN_DEF(message,"s*"), // [MouseJstr] BUILDIN_DEF(npctalk,"*"), // [Valaris] BUILDIN_DEF(hasitems,"*"), // [Valaris] BUILDIN_DEF(mobcount,"ss"), BUILDIN_DEF(getlook,"i"), BUILDIN_DEF(getsavepoint,"i"), BUILDIN_DEF(npcspeed,"i"), // [Valaris] BUILDIN_DEF(npcwalkto,"ii"), // [Valaris] BUILDIN_DEF(npcstop,""), // [Valaris] BUILDIN_DEF(getmapxy,"siii*"), //by Lorky [Lupus] 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(summon,"si*"), // summons a slave monster [Celest] BUILDIN_DEF(isnight,""), // check whether it is night time [Celest] BUILDIN_DEF(isday,""), // check whether it is day time [Celest] BUILDIN_DEF(isequipped,"i*"), // check whether another item/card has been equipped [Celest] BUILDIN_DEF(isequippedcnt,"i*"), // check how many items/cards are being equipped [Celest] BUILDIN_DEF(cardscnt,"i*"), // check how many items/cards are being equipped in the same arm [Lupus] BUILDIN_DEF(getrefine,"*"), // returns the refined number of the current item, or an item with index specified [celest] BUILDIN_DEF(adopt,"sss"), // allows 2 parents to adopt a child BUILDIN_DEF(night,""), // sets the server to night time BUILDIN_DEF(day,""), // sets the server to day time #ifdef PCRE_SUPPORT BUILDIN_DEF(defpattern,"iss"), // Define pattern to listen for [MouseJstr] 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] #endif BUILDIN_DEF(dispbottom,"s"), //added from jA [Lupus] BUILDIN_DEF(getusersname,"*"), BUILDIN_DEF(recovery,""), BUILDIN_DEF(getpetinfo,"i"), BUILDIN_DEF(checkequipedcard,"i"), BUILDIN_DEF(jump_zero,"ii"), //for future jA script compatibility BUILDIN_DEF(select,"*"), //for future jA script compatibility BUILDIN_DEF(prompt,"*"), BUILDIN_DEF(globalmes,"s*"), BUILDIN_DEF(getmapmobs,"s"), //end jA addition BUILDIN_DEF(unequip,"i"), // unequip command [Spectre] BUILDIN_DEF(getstrlen,"s"), //strlen [Valaris] BUILDIN_DEF(charisalpha,"si"), //isalpha [Valaris] BUILDIN_DEF(fakenpcname,"ssi"), // [Lance] BUILDIN_DEF(compare,"ss"), // Lordalfa - To bring strstr to scripting Engine. BUILDIN_DEF(getiteminfo,"ii"), //[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 // [zBuffer] List of mathematics commands ---> BUILDIN_DEF(sqrt,"i"), BUILDIN_DEF(pow,"ii"), BUILDIN_DEF(distance,"iiii"), BUILDIN_DEF(checkcell,"siii"), // <--- [zBuffer] List of mathematics commands // [zBuffer] List of dynamic var commands ---> BUILDIN_DEF(getd,"*"), BUILDIN_DEF(setd,"*"), // <--- [zBuffer] List of dynamic var commands BUILDIN_DEF(petstat,"i"), BUILDIN_DEF(callshop,"si"), // [Skotlex] BUILDIN_DEF(npcshopitem,"sii*"), // [Lance] BUILDIN_DEF(npcshopadditem,"sii*"), BUILDIN_DEF(npcshopdelitem,"si*"), BUILDIN_DEF(npcshopattach,"s?"), BUILDIN_DEF(equip,"i"), BUILDIN_DEF(autoequip,"ii"), BUILDIN_DEF(setbattleflag,"ss"), BUILDIN_DEF(getbattleflag,"s"), BUILDIN_DEF(setitemscript,"is*"), //Set NEW item bonus script. Lupus BUILDIN_DEF(disguise,"i"), //disguise player. Lupus BUILDIN_DEF(undisguise,"*"), //undisguise player. Lupus BUILDIN_DEF(getmonsterinfo,"ii"), //Lupus BUILDIN_DEF(axtoi,"s"), BUILDIN_DEF(query_sql,"s*"), BUILDIN_DEF(escape_sql,"s"), BUILDIN_DEF(atoi,"s"), // [zBuffer] List of player cont commands ---> BUILDIN_DEF(rid2name,"i"), BUILDIN_DEF(pcfollow,"ii"), BUILDIN_DEF(pcstopfollow,"i"), BUILDIN_DEF(pcblockmove,"ii"), // <--- [zBuffer] List of player cont commands // [zBuffer] List of mob control commands ---> BUILDIN_DEF(mobspawn,"*"), BUILDIN_DEF(mobremove,"i"), BUILDIN_DEF(getmobdata,"i*"), BUILDIN_DEF(setmobdata,"iii"), BUILDIN_DEF(mobassist,"i?"), BUILDIN_DEF(mobattach,"i?"), BUILDIN_DEF(unitwalk,"ii?"), BUILDIN_DEF(unitkill,"i"), BUILDIN_DEF(unitwarp,"isii"), BUILDIN_DEF(unitattack,"iv?"), BUILDIN_DEF(unitstop,"i"), BUILDIN_DEF(unittalk,"is"), BUILDIN_DEF(unitemote,"ii"), BUILDIN_DEF(unitdeadsit,"ii"), BUILDIN_DEF(unitskilluseid,"iii?"), // originally by Qamera [Celest] BUILDIN_DEF(unitskillusepos,"iiiii"), // [Celest] // <--- [zBuffer] List of mob control commands BUILDIN_DEF(sleep,"i"), BUILDIN_DEF(sleep2,"i"), BUILDIN_DEF(awake,"s"), BUILDIN_DEF(getvariableofnpc,"rs"), // [blackhole89] --> BUILDIN_DEF(warpportal,"iisii"), // <--- [blackhole89] BUILDIN_DEF2(homunculus_evolution,"homevolution",""), //[orn] BUILDIN_DEF(eaclass,"*"), //[Skotlex] BUILDIN_DEF(roclass,"i*"), //[Skotlex] BUILDIN_DEF(checkvending,"*"), BUILDIN_DEF(checkchatting,"*"), {NULL,NULL,NULL}, }; /*========================================== * *------------------------------------------ */ BUILDIN_FUNC(mes) { struct map_session_data *sd = script_rid2sd(st); const char *mes = script_getstr(st, 2); if (sd) clif_scriptmes(sd, st->oid, mes); return 0; } /*========================================== * *------------------------------------------ */ BUILDIN_FUNC(goto) { int pos; if( !data_islabel(script_getdata(st,2))){ ShowMessage("script: goto: not label!\n"); st->state=END; return 1; } pos=script_getnum(st,2); st->pos=pos; st->state=GOTO; return 0; } /*========================================== * ユーザー定義関数の呼び出し *------------------------------------------ */ BUILDIN_FUNC(callfunc) { struct script_code *scr, *oldscr; const char *str=script_getstr(st,2); if( (scr=strdb_get(userfunc_db,(unsigned char*)str)) ){ int i,j; struct linkdb_node **oldval = st->stack->var_function; for(i=st->start+3,j=0;iend;i++,j++) push_copy(st->stack,i); script_pushint(st,j); // 引数の数をプッシュ script_pushint(st,st->stack->defsp); // 現在の基準スタックポインタをプッシュ script_pushint(st,(int)st->script); // 現在のスクリプトをプッシュ script_pushint(st,(int)st->stack->var_function); // 現在の関数依存変数をプッシュ push_val(st->stack,C_RETINFO,st->pos); // 現在のスクリプト位置をプッシュ oldscr = st->script; st->pos=0; st->script=scr; st->stack->defsp=st->start+5+j; st->state=GOTO; st->stack->var_function = (struct linkdb_node**)aCalloc(1, sizeof(struct linkdb_node*)); // ' 変数の引き継ぎ for(i = 0; i < j; i++) { struct script_data *s = &st->stack->stack_data[st->stack->sp-6-i]; if( data_isreference(s) && !s->ref ) { char *name = str_buf+str_data[s->u.num&0x00ffffff].str; // '@ 変数の引き継ぎ if( name[0] == '.' && name[1] == '@' ) { s->ref = oldval; } else if( name[0] == '.' ) { s->ref = &oldscr->script_vars; } } } }else{ ShowWarning("script:callfunc: function not found! [%s]\n",str); st->state=END; return 1; } return 0; } /*========================================== * サブルーティンの呼び出し *------------------------------------------ */ BUILDIN_FUNC(callsub) { int pos=script_getnum(st,2); int i,j; if(!data_islabel(script_getdata(st,2)) && !data_isfunclabel(script_getdata(st,2))) { ShowError("script: callsub: not label !\n"); st->state=END; return 1; } else { struct linkdb_node **oldval = st->stack->var_function; for(i=st->start+3,j=0;iend;i++,j++) push_copy(st->stack,i); script_pushint(st,j); // 引数の数をプッシュ script_pushint(st,st->stack->defsp); // 現在の基準スタックポインタをプッシュ script_pushint(st,(int)st->script); // 現在のスクリプトをプッシュ script_pushint(st,(int)st->stack->var_function); // 現在の関数依存変数をプッシュ push_val(st->stack,C_RETINFO,st->pos); // 現在のスクリプト位置をプッシュ st->pos=pos; st->stack->defsp=st->start+5+j; st->state=GOTO; st->stack->var_function = (struct linkdb_node**)aCalloc(1, sizeof(struct linkdb_node*)); // ' 変数の引き継ぎ for(i = 0; i < j; i++) { struct script_data *s = &st->stack->stack_data[st->stack->sp-6-i]; if( data_isreference(s) && !s->ref ) { char *name = str_buf+str_data[s->u.num&0x00ffffff].str; // '@ 変数の引き継ぎ if( name[0] == '.' && name[1] == '@' ) { s->ref = oldval; } } } } return 0; } /*========================================== * 引数の所得 *------------------------------------------ */ BUILDIN_FUNC(getarg) { int num=script_getnum(st,2); int max,stsp; if( st->stack->defsp<5 || st->stack->stack_data[st->stack->defsp-1].type!=C_RETINFO ){ ShowWarning("script:getarg without callfunc or callsub!\n"); st->state=END; return 1; } max=conv_num(st,& (st->stack->stack_data[st->stack->defsp-5])); stsp=st->stack->defsp - max -5; if( num >= max ){ ShowWarning("script:getarg arg1(%d) out of range(%d) !\n",num,max); st->state=END; return 1; } push_copy(st->stack,stsp+num); return 0; } /*========================================== * サブルーチン/ユーザー定義関数の終了 *------------------------------------------ */ BUILDIN_FUNC(return) { if(script_hasdata(st,2)){ // 戻り値有り struct script_data *sd; push_copy(st->stack,st->start+2); sd = &st->stack->stack_data[st->stack->sp-1]; if(data_isreference(sd)) { char *name = str_buf + str_data[sd->u.num&0x00ffffff].str; if( name[0] == '.' && name[1] == '@') { // '@ 変数を参照渡しにすると危険なので値渡しにする get_val(st,sd); //Fix dangling pointer crash due when returning a temporary // script variable (from Rayce/jA) if(isstr(sd)) { sd->type = C_STR; sd->u.str = (char *)aStrdup(sd->u.str); } } else if( name[0] == '.' && !sd->ref) { // ' 変数は参照渡しでも良いが、参照元が設定されていないと // 元のスクリプトの値を差してしまうので補正する。 sd->ref = &st->script->script_vars; } } } st->state=RETFUNC; return 0; } /*========================================== * *------------------------------------------ */ BUILDIN_FUNC(next) { st->state=STOP; clif_scriptnext(script_rid2sd(st),st->oid); return 0; } /*========================================== * *------------------------------------------ */ BUILDIN_FUNC(close) { st->state=END; clif_scriptclose(script_rid2sd(st),st->oid); return 0; } BUILDIN_FUNC(close2) { st->state=STOP; clif_scriptclose(script_rid2sd(st),st->oid); return 0; } /*========================================== * *------------------------------------------ */ BUILDIN_FUNC(menu) { char *buf, *ptr; int len,i; struct map_session_data *sd = script_rid2sd(st); nullpo_retr(0, sd); if(sd->state.menu_or_input==0){ st->state=RERUNLINE; sd->state.menu_or_input=1; if( (st->end - st->start - 2) % 2 == 1 ) { // 引数の数が奇数なのでエラー扱い ShowError("buildin_menu: illegal argument count(%d).\n", st->end - st->start - 2); sd->state.menu_or_input=0; st->state=END; return 1; } for(i=st->start+2,len=0;iend;i+=2){ conv_str(st,& (st->stack->stack_data[i])); len+=(int)strlen(st->stack->stack_data[i].u.str)+1; } buf=(char *)aMallocA((len+1)*sizeof(char)); buf[0]=0; for(i=st->start+2,len=0;iend;i+=2){ if( st->stack->stack_data[i].u.str[0] ) { strcat(buf,st->stack->stack_data[i].u.str); strcat(buf,":"); } } ptr = buf; sd->npc_menu = 0; //Reuse to store max menu entries. Avoids the need of an extra variable. while (ptr && (ptr = strchr(ptr, ':')) != NULL) { sd->npc_menu++; ptr++; } clif_scriptmenu(sd,st->oid,buf); aFree(buf); } else if(sd->npc_menu==0xff){ // cancel sd->state.menu_or_input=0; st->state=END; } else { // goto動作 sd->state.menu_or_input=0; if(sd->npc_menu>0){ //Skip empty menu entries which weren't displayed on the client (blackhole89) for(i=st->start+2;i<=(st->start+sd->npc_menu*2) && sd->npc_menu<(st->end-st->start)/2;i+=2) { conv_str(st,& (st->stack->stack_data[i])); // we should convert variables to strings before access it [jA1983] [EoE] if((int)strlen(st->stack->stack_data[i].u.str) < 1) sd->npc_menu++; //Empty selection which wasn't displayed on the client. } if(sd->npc_menu >= (st->end-st->start)/2) { //Invalid selection. st->state=END; return 0; } if( !data_islabel(script_getdata(st, sd->npc_menu*2+1)) ){ ShowError("script: menu: not label !\n"); st->state=END; return 1; } pc_setreg(sd,add_str("@menu"),sd->npc_menu); st->pos=script_getnum(st,sd->npc_menu*2+1); st->state=GOTO; } } return 0; } /// Returns a random number from 0 to -1. /// Or returns a random number from to . /// If is greater than , their numbers are switched. /// rand() -> /// rand(,) -> BUILDIN_FUNC(rand) { int range; int min; int max; if( script_hasdata(st,3) ) {// min,max min = script_getnum(st,2); max = script_getnum(st,3); if( max < min ) swap(min, max); range = max - min + 1; } else {// range min = 0; range = script_getnum(st,2); } if( range <= 1 ) script_pushint(st, min); else script_pushint(st, rand()%range + min); return 0; } /*========================================== * *------------------------------------------ */ BUILDIN_FUNC(warp) { int x,y; const char *str; struct map_session_data *sd=script_rid2sd(st); nullpo_retr(0, sd); str=script_getstr(st,2); x=script_getnum(st,3); y=script_getnum(st,4); if(strcmp(str,"Random")==0) pc_randomwarp(sd,3); else if(strcmp(str,"SavePoint")==0){ pc_setpos(sd,sd->status.save_point.map, sd->status.save_point.x,sd->status.save_point.y,3); }else if(strcmp(str,"Save")==0){ pc_setpos(sd,sd->status.save_point.map, sd->status.save_point.x,sd->status.save_point.y,3); }else pc_setpos(sd,mapindex_name2id(str),x,y,0); return 0; } /*========================================== * エリア指定ワープ *------------------------------------------ */ static int buildin_areawarp_sub(struct block_list *bl,va_list ap) { int x,y; unsigned int map; map=va_arg(ap, unsigned int); x=va_arg(ap,int); y=va_arg(ap,int); if(map == 0) pc_randomwarp((struct map_session_data *)bl,3); else pc_setpos((struct map_session_data *)bl,map,x,y,0); return 0; } BUILDIN_FUNC(areawarp) { int x,y,m; unsigned int index; const char *str; const char *mapname; int x0,y0,x1,y1; mapname=script_getstr(st,2); x0=script_getnum(st,3); y0=script_getnum(st,4); x1=script_getnum(st,5); y1=script_getnum(st,6); str=script_getstr(st,7); x=script_getnum(st,8); y=script_getnum(st,9); if( (m=map_mapname2mapid(mapname))< 0) return 0; if(strcmp(str,"Random")==0) index = 0; else if(!(index=mapindex_name2id(str))) return 0; map_foreachinarea(buildin_areawarp_sub, m,x0,y0,x1,y1,BL_PC, index,x,y ); return 0; } /*========================================== * warpchar [LuzZza] * Useful for warp one player from * another player npc-session. * Using: warpchar "mapname",x,y,Char_ID; *------------------------------------------ */ BUILDIN_FUNC(warpchar) { int x,y,a,i; const char *str; struct map_session_data *sd, **pl_allsd; int users; str=script_getstr(st,2); x=script_getnum(st,3); y=script_getnum(st,4); a=script_getnum(st,5); pl_allsd = map_getallusers(&users); for(i=0; istatus.char_id == a) { if(strcmp(str, "Random") == 0) pc_randomwarp(sd, 3); else if(strcmp(str, "SavePoint") == 0) pc_setpos(sd, sd->status.save_point.map, sd->status.save_point.x, sd->status.save_point.y, 3); else pc_setpos(sd, mapindex_name2id(str), x, y, 3); } } return 0; } /*========================================== * Warpparty - [Fredzilla] * Syntax: warpparty "mapname",x,y,Party_ID; *------------------------------------------ */ BUILDIN_FUNC(warpparty) { int x,y; const char *str; int p_id; int i; unsigned short mapindex; struct map_session_data *pl_sd; struct party_data *p=NULL; str=script_getstr(st,2); x=script_getnum(st,3); y=script_getnum(st,4); p_id=script_getnum(st,5); if(p_id < 1) return 0; p = party_search(p_id); if (!p) return 0; if(strcmp(str,"Random")==0) { for (i = 0; i < MAX_PARTY; i++) { if ((pl_sd = p->data[i].sd)) { if(map[pl_sd->bl.m].flag.nowarp) continue; pc_randomwarp(pl_sd,3); } } } else if(strcmp(str,"SavePointAll")==0) { for (i = 0; i < MAX_PARTY; i++) { if ((pl_sd = p->data[i].sd)) { if(map[pl_sd->bl.m].flag.noreturn) continue; pc_setpos(pl_sd,pl_sd->status.save_point.map,pl_sd->status.save_point.x,pl_sd->status.save_point.y,3); } } } else if(strcmp(str,"SavePoint")==0) { pl_sd=script_rid2sd(st); if (!pl_sd) return 0; mapindex=pl_sd->status.save_point.map; x=pl_sd->status.save_point.x; y=pl_sd->status.save_point.y; for (i = 0; i < MAX_PARTY; i++) { if ((pl_sd = p->data[i].sd)) { if(map[pl_sd->bl.m].flag.noreturn) continue; pc_setpos(pl_sd,mapindex,x,y,3); } } } else { mapindex = mapindex_name2id(str); if (!mapindex) //Show source of npc error. return 1; for (i = 0; i < MAX_PARTY; i++) { if ((pl_sd = p->data[i].sd)) { if(map[pl_sd->bl.m].flag.noreturn || map[pl_sd->bl.m].flag.nowarp) continue; pc_setpos(pl_sd,mapindex,x,y,3); } } } return 0; } /*========================================== * Warpguild - [Fredzilla] * Syntax: warpguild "mapname",x,y,Guild_ID; *------------------------------------------ */ BUILDIN_FUNC(warpguild) { int x,y; unsigned short mapindex; const char *str; int g; int i; struct map_session_data *pl_sd, **pl_allsd; int users; struct map_session_data *sd; str=script_getstr(st,2); x=script_getnum(st,3); y=script_getnum(st,4); g=script_getnum(st,5); sd=script_rid2sd(st); if(map[sd->bl.m].flag.noreturn || map[sd->bl.m].flag.nowarpto) return 0; if(g < 1) return 0; pl_allsd = map_getallusers(&users); if(strcmp(str,"Random")==0) { for (i = 0; i < users; i++) { if ((pl_sd = pl_allsd[i]) && pl_sd->status.guild_id == g) { if(map[pl_sd->bl.m].flag.nowarp) continue; pc_randomwarp(pl_sd,3); } } } else if(strcmp(str,"SavePointAll")==0) { if(map[sd->bl.m].flag.noreturn) return 0; for (i = 0; i < users; i++) { if ((pl_sd = pl_allsd[i]) && pl_sd->status.guild_id == g) { if(map[pl_sd->bl.m].flag.noreturn) continue; pc_setpos(pl_sd,pl_sd->status.save_point.map,pl_sd->status.save_point.x,pl_sd->status.save_point.y,3); } } } else if(strcmp(str,"SavePoint")==0) { if(map[sd->bl.m].flag.noreturn) return 0; mapindex=sd->status.save_point.map; x=sd->status.save_point.x; y=sd->status.save_point.y; for (i = 0; i < users; i++) { if ((pl_sd = pl_allsd[i]) && pl_sd->status.guild_id == g) { if(map[pl_sd->bl.m].flag.noreturn) continue; pc_setpos(pl_sd,mapindex,x,y,3); } } } else { mapindex = mapindex_name2id(str); for (i = 0; i < users; i++) { if ((pl_sd = pl_allsd[i]) && pl_sd->status.guild_id == g) { if(map[pl_sd->bl.m].flag.noreturn || map[pl_sd->bl.m].flag.nowarp) continue; pc_setpos(pl_sd,mapindex,x,y,3); } } } return 0; } /*========================================== * *------------------------------------------ */ BUILDIN_FUNC(heal) { struct map_session_data *sd; int hp,sp; sd = script_rid2sd(st); if (!sd) return 0; hp=script_getnum(st,2); sp=script_getnum(st,3); status_heal(&sd->bl, hp, sp, 1); return 0; } /*========================================== * *------------------------------------------ */ BUILDIN_FUNC(itemheal) { struct map_session_data *sd; int hp,sp; hp=script_getnum(st,2); sp=script_getnum(st,3); if(potion_flag==1) { potion_hp = hp; potion_sp = sp; return 0; } sd = script_rid2sd(st); if (!sd) return 0; pc_itemheal(sd,sd->itemid,hp,sp); return 0; } /*========================================== * *------------------------------------------ */ BUILDIN_FUNC(percentheal) { int hp,sp; hp=script_getnum(st,2); sp=script_getnum(st,3); if(potion_flag==1) { potion_per_hp = hp; potion_per_sp = sp; return 0; } pc_percentheal(script_rid2sd(st),hp,sp); return 0; } /*========================================== * *------------------------------------------ */ BUILDIN_FUNC(jobchange) { int job, upper=-1; job=script_getnum(st,2); if( script_hasdata(st,3) ) upper=script_getnum(st,3); if ((job >= 0 && job < MAX_PC_CLASS)) { pc_jobchange(script_rid2sd(st),job, upper); if(use_irc && irc_announce_jobchange_flag) irc_announce_jobchange(script_rid2sd(st)); } return 0; } /*========================================== * *------------------------------------------ */ BUILDIN_FUNC(jobname) { int class_=script_getnum(st,2); script_pushconststr(st,job_name(class_)); return 0; } /*========================================== * *------------------------------------------ */ BUILDIN_FUNC(input) { struct map_session_data *sd = script_rid2sd(st); struct script_data *data = script_getdata(st,2); int num = data->u.num; char *name=str_buf+str_data[num&0x00ffffff].str; char postfix = name[strlen(name)-1]; if (!sd) return 1; if( !data_isreference(data) ){ ShowError("script: buildin_input: given argument is not a variable!\n"); return 1; } if(sd->state.menu_or_input){ sd->state.menu_or_input=0; if( postfix=='$' ) { set_reg(st,sd,num,name,(void*)sd->npc_str, script_getref(st,2)); return 0; } // Yor, Lupus & Fritz have messed with this. // Basicly it prevents negative input since most scripts do not account for them. sd->npc_amount = cap_value(sd->npc_amount, 0, battle_config.vending_max_value); set_reg(st,sd,num,name,(void*)sd->npc_amount, script_getref(st,2)); return 0; } //state.menu_or_input = 0 st->state=RERUNLINE; if( postfix=='$' ) clif_scriptinputstr(sd,st->oid); else clif_scriptinput(sd,st->oid); sd->state.menu_or_input=1; return 0; } /*========================================== * 変数設定 *------------------------------------------ */ BUILDIN_FUNC(set) { struct map_session_data *sd=NULL; int num=st->stack->stack_data[st->start+2].u.num; char *name=str_buf+str_data[num&0x00ffffff].str; char prefix=*name; char postfix=name[strlen(name)-1]; if( !data_isreference(script_getdata(st,2)) ){ ShowError("script: buildin_set: not name\n"); return 1; } if(not_server_variable(prefix)) sd=script_rid2sd(st); if( postfix=='$' ){ // 文字列 const char *str = script_getstr(st,3); set_reg(st,sd,num,name,(void*)str,script_getref(st,2)); }else{ // 数値 int val = script_getnum(st,3); set_reg(st,sd,num,name,(void*)val,script_getref(st,2)); } return 0; } /*========================================== * 配列変数設定 *------------------------------------------ */ BUILDIN_FUNC(setarray) { struct map_session_data *sd=NULL; int num=st->stack->stack_data[st->start+2].u.num; char *name=str_buf+str_data[num&0x00ffffff].str; char prefix=*name; char postfix=name[strlen(name)-1]; int i,j; if( prefix!='$' && prefix!='@' && prefix!='.'){ ShowWarning("buildin_setarray: illegal scope !\n"); return 1; } if(not_server_variable(prefix)) sd=script_rid2sd(st); for(j=0,i=st->start+3; iend && j<128;i++,j++){ void *v; if( postfix=='$' ) v=(void*)conv_str(st,& (st->stack->stack_data[i])); else v=(void*)conv_num(st,& (st->stack->stack_data[i])); set_reg(st, sd, num+(j<<24), name, v, script_getref(st,2)); } return 0; } /*========================================== * 配列変数クリア *------------------------------------------ */ BUILDIN_FUNC(cleararray) { struct map_session_data *sd=NULL; int num=st->stack->stack_data[st->start+2].u.num; char *name=str_buf+str_data[num&0x00ffffff].str; char prefix=*name; char postfix=name[strlen(name)-1]; int sz=script_getnum(st,4); int i; void *v; if( prefix!='$' && prefix!='@' && prefix!='.'){ ShowWarning("buildin_cleararray: illegal scope !\n"); return 1; } if( not_server_variable(prefix) ) sd=script_rid2sd(st); if( postfix=='$' ) v=(void*)script_getstr(st,3); else v=(void*)script_getnum(st,3); for(i=0;istack->stack_data[st->start+2].u.num; char *name=str_buf+str_data[num&0x00ffffff].str; char prefix=*name; char postfix=name[strlen(name)-1]; int num2=st->stack->stack_data[st->start+3].u.num; char *name2=str_buf+str_data[num2&0x00ffffff].str; char prefix2=*name2; char postfix2=name2[strlen(name2)-1]; int sz=script_getnum(st,4); int i; if( prefix!='$' && prefix!='@' && prefix!='.' ){ printf("buildin_copyarray: illeagal scope !\n"); return 0; } if( prefix2!='$' && prefix2!='@' && prefix2!='.' ) { printf("buildin_copyarray: illeagal scope !\n"); return 0; } if( (postfix=='$' || postfix2=='$') && postfix!=postfix2 ){ printf("buildin_copyarray: type mismatch !\n"); return 0; } if( not_server_variable(prefix) || not_server_variable(prefix2) ) sd=script_rid2sd(st); if((num & 0x00FFFFFF) == (num2 & 0x00FFFFFF) && (num & 0xFF000000) > (num2 & 0xFF000000)) { // 同じ配列で、num > num2 の場合大きい方からコピーしないといけない for(i=sz-1;i>=0;i--) set_reg( st,sd,num+(i<<24),name, get_val2(st,num2+(i<<24),script_getref(st,3)), script_getref(st,2) ); } else { for(i=0;i>24), c = (i==0?-1:i); // Moded to -1 because even if the first element is 0, it will still report as 1 [Lance] if(postfix == '$') { for(; i < 128; i++) { void* v = get_val2(st, (num & 0x00FFFFFF) + (i<<24), ref); if(*((char*)v)) c = i; } } else { for(; i < 128; i++) { void* v = get_val2(st, (num & 0x00FFFFFF) + (i<<24), ref); if((int)v) c = i; } } return c + 1; } BUILDIN_FUNC(getarraysize) { int num = st->stack->stack_data[st->start+2].u.num; char* name = str_buf + str_data[num&0x00ffffff].str; char prefix = name[0], postfix = name[strlen(name)-1]; if( prefix != '$' && prefix != '@' && prefix != '.' ) { ShowWarning("buildin_getarraysize: illegal scope !\n"); script_pushint(st,0); return 1; } script_pushint(st,getarraysize(st, num, postfix, script_getref(st,2))); return 0; } /*========================================== * 配列変数から要素削除 *------------------------------------------ */ BUILDIN_FUNC(deletearray) { struct map_session_data *sd=NULL; int num=st->stack->stack_data[st->start+2].u.num; char *name=str_buf+str_data[num&0x00ffffff].str; char prefix=*name; char postfix=name[strlen(name)-1]; int count=1; int i,sz=getarraysize(st,num,postfix,script_getref(st,2))-(num>>24)-count+1; if( script_hasdata(st,3) ) count=script_getnum(st,3); if( prefix!='$' && prefix!='@' && prefix!='.' ){ ShowWarning("buildin_deletearray: illegal scope !\n"); return 1; } if( not_server_variable(prefix) ) sd=script_rid2sd(st); for(i=0;i>24));i++) set_reg(st,sd,num+(i<<24),name, 0,script_getref(st,2)); } else { for(;i<(128-(num>>24));i++) set_reg(st,sd,num+(i<<24),name, (void *) "",script_getref(st,2)); } return 0; } /*========================================== * 指定要素を表す値(キー)を所得する *------------------------------------------ */ BUILDIN_FUNC(getelementofarray) { if( data_isreference(script_getdata(st, 2)) ){ int i=script_getnum(st,3); if(i>127 || i<0){ ShowWarning("script: getelementofarray (operator[]): param2 illegal number %d\n",i); script_pushint(st,0); return 1; }else{ push_val2(st->stack,C_NAME, (i<<24) | st->stack->stack_data[st->start+2].u.num, script_getref(st,2) ); } }else{ ShowError("script: getelementofarray (operator[]): param1 not name !\n"); script_pushint(st,0); } return 0; } /*========================================== * *------------------------------------------ */ BUILDIN_FUNC(setlook) { int type,val; type=script_getnum(st,2); val=script_getnum(st,3); pc_changelook(script_rid2sd(st),type,val); return 0; } /*========================================== * *------------------------------------------ */ BUILDIN_FUNC(cutin) { clif_cutin(script_rid2sd(st),script_getstr(st,2),script_getnum(st,3)); return 0; } /*========================================== * *------------------------------------------ */ BUILDIN_FUNC(viewpoint) { int type,x,y,id,color; type=script_getnum(st,2); x=script_getnum(st,3); y=script_getnum(st,4); id=script_getnum(st,5); color=script_getnum(st,6); clif_viewpoint(script_rid2sd(st),st->oid,type,x,y,id,color); return 0; } /*========================================== * *------------------------------------------ */ BUILDIN_FUNC(countitem) { int nameid=0,count=0,i; struct map_session_data *sd; struct script_data *data; sd = script_rid2sd(st); if (!sd) { script_pushint(st,0); return 0; } data=script_getdata(st,2); get_val(st,data); if( data_isstring(data) ){ const char *name=conv_str(st,data); struct item_data *item_data; if( (item_data = itemdb_searchname(name)) != NULL) nameid=item_data->nameid; }else nameid=conv_num(st,data); if (nameid>=500) //if no such ID then skip this iteration for(i=0;istatus.inventory[i].nameid==nameid) count+=sd->status.inventory[i].amount; } else{ if(battle_config.error_log) ShowError("wrong item ID : countitem(%i)\n",nameid); script_pushint(st,0); return 1; } script_pushint(st,count); return 0; } /*========================================== * countitem2(nameID,Identified,Refine,Attribute,Card0,Card1,Card2,Card3) [Lupus] * returns number of items that met the conditions *------------------------------------------ */ BUILDIN_FUNC(countitem2) { int nameid=0,count=0,i; int iden,ref,attr,c1,c2,c3,c4; struct map_session_data *sd; struct script_data *data; sd = script_rid2sd(st); if (!sd) { script_pushint(st,0); return 0; } data=script_getdata(st,2); get_val(st,data); if( data_isstring(data) ){ const char *name=conv_str(st,data); struct item_data *item_data; if( (item_data = itemdb_searchname(name)) != NULL) nameid=item_data->nameid; }else nameid=conv_num(st,data); iden=script_getnum(st,3); ref=script_getnum(st,4); attr=script_getnum(st,5); c1=script_getnum(st,6); c2=script_getnum(st,7); c3=script_getnum(st,8); c4=script_getnum(st,9); if (nameid>=500) //if no such ID then skip this iteration for(i=0;istatus.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 || sd->status.inventory[i].attribute!=attr || sd->status.inventory[i].card[0]!=c1 || sd->status.inventory[i].card[1]!=c2 || sd->status.inventory[i].card[2]!=c3 || sd->status.inventory[i].card[3]!=c4) continue; count+=sd->status.inventory[i].amount; } else{ if(battle_config.error_log) ShowError("wrong item ID : countitem2(%i)\n",nameid); script_pushint(st,0); return 1; } script_pushint(st,count); return 0; } /*========================================== * 重量チェック *------------------------------------------ */ BUILDIN_FUNC(checkweight) { int nameid=0,amount,i; unsigned long weight; struct map_session_data *sd; struct script_data *data; sd = script_rid2sd(st); data=script_getdata(st,2); get_val(st,data); if( data_isstring(data) ){ const char *name=conv_str(st,data); struct item_data *item_data = itemdb_searchname(name); if( item_data ) nameid=item_data->nameid; }else nameid=conv_num(st,data); amount=script_getnum(st,3); if ( amount<=0 || nameid<500 ) { //if get wrong item ID or amount<=0, don't count weight of non existing items script_pushint(st,0); ShowError("buildin_checkweight: Wrong item ID or amount.\n"); return 1; } weight = itemdb_weight(nameid)*amount; if(amount > MAX_AMOUNT || weight + sd->weight > sd->max_weight){ script_pushint(st,0); } else { //Check if the inventory ain't full. //TODO: Currently does not checks if you can just stack it on top of another item you already have.... i = pc_search_inventory(sd,0); if (i >= 0) //Empty slot available. script_pushint(st,1); else //Inventory full script_pushint(st,0); } return 0; } /*========================================== * getitem ,{,}; * getitem "",{,}; *------------------------------------------ */ BUILDIN_FUNC(getitem) { int nameid,amount,flag = 0; struct item it; struct map_session_data *sd; struct script_data *data; data=script_getdata(st,2); get_val(st,data); if( data_isstring(data) ) {// "" const char *name=conv_str(st,data); struct item_data *item_data = itemdb_searchname(name); if( item_data == NULL ){ ShowError("buildin_getitem: Nonexistant item %s requested.\n", name); report_src(st); return 1; //No item created. } nameid=item_data->nameid; } else if( data_isint(data) ) {// nameid=conv_num(st,data); //Violet Box, Blue Box, etc - random item pick if( nameid < 0 ) { nameid=itemdb_searchrandomid(-nameid); flag = 1; } if( nameid <= 0 || !itemdb_exists(nameid) ){ ShowError("buildin_getitem: Nonexistant item %d requested.\n", nameid); report_src(st); return 1; //No item created. } } else { ShowError("buildin_getitem: invalid data type for argument #1 (%d).", data->type); report_src(st); return 1; } // if( (amount=script_getnum(st,3)) <= 0) return 0; //return if amount <=0, skip the useles iteration memset(&it,0,sizeof(it)); it.nameid=nameid; if(!flag) it.identify=1; else it.identify=itemdb_isidentified(nameid); if( script_hasdata(st,4) ) {// sd=map_id2sd(script_getnum(st,4)); } else {// attached player sd=script_rid2sd(st); } if( sd == NULL ) // no target return 0; if( pet_create_egg(sd, nameid) ) amount = 1; //This is a pet! else if( (flag=pc_additem(sd,&it,amount)) ){ clif_additem(sd,0,0,flag); if( pc_candrop(sd,&it) ) map_addflooritem(&it,amount,sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0); } //Logs items, got from (N)PC scripts [Lupus] if(log_config.enable_logs&LOG_SCRIPT_TRANSACTIONS) log_pick_pc(sd, "N", nameid, amount, NULL); return 0; } /*========================================== * *------------------------------------------ */ BUILDIN_FUNC(getitem2) { int nameid,amount,flag = 0; int iden,ref,attr,c1,c2,c3,c4; struct item_data *item_data; struct item item_tmp; struct map_session_data *sd; struct script_data *data; sd = script_rid2sd(st); data=script_getdata(st,2); get_val(st,data); if( data_isstring(data) ){ const char *name=conv_str(st,data); struct item_data *item_data = itemdb_searchname(name); if( item_data ) nameid=item_data->nameid; else nameid=UNKNOWN_ITEM_ID; }else nameid=conv_num(st,data); amount=script_getnum(st,3); iden=script_getnum(st,4); ref=script_getnum(st,5); attr=script_getnum(st,6); c1=script_getnum(st,7); c2=script_getnum(st,8); c3=script_getnum(st,9); c4=script_getnum(st,10); if( script_hasdata(st,11) ) //アイテムを指定したIDに渡す sd=map_id2sd(script_getnum(st,11)); if(sd == NULL) //アイテムを渡す相手がいなかったらお帰り return 0; if(nameid<0) { // ランダム nameid=itemdb_searchrandomid(-nameid); flag = 1; } if(nameid > 0) { memset(&item_tmp,0,sizeof(item_tmp)); item_data=itemdb_exists(nameid); if (item_data == NULL) return -1; if(item_data->type==IT_WEAPON || item_data->type==IT_ARMOR){ if(ref > 10) ref = 10; } else if(item_data->type==IT_PETEGG) { iden = 1; ref = 0; } else { iden = 1; ref = attr = 0; } item_tmp.nameid=nameid; if(!flag) item_tmp.identify=iden; else if(item_data->type==IT_WEAPON || item_data->type==IT_ARMOR) item_tmp.identify=0; item_tmp.refine=ref; item_tmp.attribute=attr; item_tmp.card[0]=c1; item_tmp.card[1]=c2; item_tmp.card[2]=c3; item_tmp.card[3]=c4; if((flag = pc_additem(sd,&item_tmp,amount))) { clif_additem(sd,0,0,flag); map_addflooritem(&item_tmp,amount,sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0); } //Logs items, got from (N)PC scripts [Lupus] if(log_config.enable_logs&0x40) log_pick_pc(sd, "N", nameid, amount, &item_tmp); } return 0; } /*========================================== * gets an item with someone's name inscribed [Skotlex] * getinscribeditem item_num, character_name * Returned Qty is always 1, only works on equip-able * equipment *------------------------------------------ */ BUILDIN_FUNC(getnameditem) { int nameid; struct item item_tmp; struct map_session_data *sd, *tsd; struct script_data *data; sd = script_rid2sd(st); if (sd == NULL) { //Player not attached! script_pushint(st,0); return 0; } data=script_getdata(st,2); get_val(st,data); if( data_isstring(data) ){ const char *name=conv_str(st,data); struct item_data *item_data = itemdb_searchname(name); if( item_data == NULL) { //Failed script_pushint(st,0); return 0; } nameid = item_data->nameid; }else nameid = conv_num(st,data); if(!itemdb_exists(nameid)/* || itemdb_isstackable(nameid)*/) { //Even though named stackable items "could" be risky, they are required for certain quests. script_pushint(st,0); return 0; } data=script_getdata(st,3); get_val(st,data); if( data_isstring(data) ) //Char Name tsd=map_nick2sd(conv_str(st,data)); else //Char Id was given tsd=map_charid2sd(conv_num(st,data)); if( tsd == NULL ) { //Failed script_pushint(st,0); return 0; } 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; if(pc_additem(sd,&item_tmp,1)) { script_pushint(st,0); return 0; //Failed to add item, we will not drop if they don't fit } //Logs items, got from (N)PC scripts [Lupus] if(log_config.enable_logs&0x40) log_pick_pc(sd, "N", item_tmp.nameid, item_tmp.amount, &item_tmp); script_pushint(st,1); return 0; } /*========================================== * gets a random item ID from an item group [Skotlex] * groupranditem group_num *------------------------------------------ */ BUILDIN_FUNC(grouprandomitem) { int group; group = script_getnum(st,2); script_pushint(st,itemdb_searchrandomid(group)); return 0; } /*========================================== * *------------------------------------------ */ BUILDIN_FUNC(makeitem) { int nameid,amount,flag = 0; int x,y,m; const char *mapname; struct item item_tmp; struct script_data *data; data=script_getdata(st,2); get_val(st,data); if( data_isstring(data) ){ const char *name=conv_str(st,data); struct item_data *item_data = itemdb_searchname(name); if( item_data ) nameid=item_data->nameid; else nameid=UNKNOWN_ITEM_ID; }else nameid=conv_num(st,data); amount=script_getnum(st,3); mapname =script_getstr(st,4); x =script_getnum(st,5); y =script_getnum(st,6); if(strcmp(mapname,"this")==0) { struct map_session_data *sd; sd = script_rid2sd(st); if (!sd) return 0; //Failed... m=sd->bl.m; } else m=map_mapname2mapid(mapname); if(nameid<0) { // ランダム nameid=itemdb_searchrandomid(-nameid); flag = 1; } if(nameid > 0) { memset(&item_tmp,0,sizeof(item_tmp)); item_tmp.nameid=nameid; if(!flag) item_tmp.identify=1; else item_tmp.identify=itemdb_isidentified(nameid); map_addflooritem(&item_tmp,amount,m,x,y,NULL,NULL,NULL,0); } return 0; } /*========================================== * script DELITEM command (fixed 2 bugs by Lupus, added deletion priority by Lupus) *------------------------------------------ */ BUILDIN_FUNC(delitem) { int nameid=0,amount,i,important_item=0; struct map_session_data *sd; struct script_data *data; sd = script_rid2sd(st); data=script_getdata(st,2); get_val(st,data); if( data_isstring(data) ){ const char *name=conv_str(st,data); struct item_data *item_data = itemdb_searchname(name); //nameid=UNKNOWN_ITEM_ID; if( item_data ) nameid=item_data->nameid; }else nameid=conv_num(st,data); amount=script_getnum(st,3); if (nameid<500 || amount<=0 ) {//by Lupus. Don't run FOR if u got wrong item ID or amount<=0 //eprintf("wrong item ID or amount<=0 : delitem %i,\n",nameid,amount); return 0; } //1st pass //here we won't delete items with CARDS, named items but we count them for(i=0;istatus.inventory[i].nameid<=0 || sd->inventory_data[i] == NULL || sd->status.inventory[i].amount<=0 || sd->status.inventory[i].nameid!=nameid) continue; //1 egg uses 1 cell in the inventory. so it's ok to delete 1 pet / per cycle if(sd->inventory_data[i]->type==IT_PETEGG && sd->status.inventory[i].card[0] == CARD0_PET) { if (!intif_delete_petdata(MakeDWord(sd->status.inventory[i].card[1], sd->status.inventory[i].card[2]))) continue; //pet couldn't be sent for deletion. } else //is this item important? does it have cards? or Player's name? or Refined/Upgraded if(itemdb_isspecial(sd->status.inventory[i].card[0]) || sd->status.inventory[i].card[0] || sd->status.inventory[i].refine) { //this is important item, count it (except for pet eggs) if(sd->status.inventory[i].card[0] != CARD0_PET) important_item++; continue; } if(sd->status.inventory[i].amount>=amount){ //Logs items, got from (N)PC scripts [Lupus] if(log_config.enable_logs&0x40) log_pick_pc(sd, "N", sd->status.inventory[i].nameid, -amount, &sd->status.inventory[i]); pc_delitem(sd,i,amount,0); return 0; //we deleted exact amount of items. now exit } else { amount-=sd->status.inventory[i].amount; //Logs items, got from (N)PC scripts [Lupus] if(log_config.enable_logs&0x40) { log_pick_pc(sd, "N", sd->status.inventory[i].nameid, -sd->status.inventory[i].amount, &sd->status.inventory[i]); } //Logs pc_delitem(sd,i,sd->status.inventory[i].amount,0); } } //2nd pass //now if there WERE items with CARDs/REFINED/NAMED... and if we still have to delete some items. we'll delete them finally if (important_item>0 && amount>0) for(i=0;istatus.inventory[i].nameid<=0 || sd->inventory_data[i] == NULL || sd->status.inventory[i].amount<=0 || sd->status.inventory[i].nameid!=nameid ) continue; if(sd->status.inventory[i].amount>=amount){ //Logs items, got from (N)PC scripts [Lupus] if(log_config.enable_logs&0x40) log_pick_pc(sd, "N", sd->status.inventory[i].nameid, -amount, &sd->status.inventory[i]); pc_delitem(sd,i,amount,0); return 0; //we deleted exact amount of items. now exit } else { amount-=sd->status.inventory[i].amount; //Logs items, got from (N)PC scripts [Lupus] if(log_config.enable_logs&0x40) log_pick_pc(sd, "N", sd->status.inventory[i].nameid, -sd->status.inventory[i].amount, &sd->status.inventory[i]); pc_delitem(sd,i,sd->status.inventory[i].amount,0); } } return 0; } /*========================================== * advanced version of delitem [modified by Mihilion] *------------------------------------------ */ BUILDIN_FUNC(delitem2) { int nameid=0,amount,i=0; int iden,ref,attr,c1,c2,c3,c4; struct map_session_data *sd; struct script_data *data; sd = script_rid2sd(st); data=script_getdata(st,2); get_val(st,data); if( data_isstring(data) ){ const char *name=conv_str(st,data); struct item_data *item_data = itemdb_searchname(name); //nameid=UNKNOWN_ITEM_ID; if( item_data ) nameid=item_data->nameid; }else nameid=conv_num(st,data); amount=script_getnum(st,3); iden=script_getnum(st,4); ref=script_getnum(st,5); attr=script_getnum(st,6); c1=script_getnum(st,7); c2=script_getnum(st,8); c3=script_getnum(st,9); c4=script_getnum(st,10); if (!itemdb_exists(nameid) || amount<=0 ) {//by Lupus. Don't run FOR if u got wrong item ID or amount<=0 //eprintf("wrong item ID or amount<=0 : delitem %i,\n",nameid,amount); return 0; } for(i=0;istatus.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 || sd->status.inventory[i].attribute!=attr || sd->status.inventory[i].card[0]!=c1 || sd->status.inventory[i].card[1]!=c2 || sd->status.inventory[i].card[2]!=c3 || sd->status.inventory[i].card[3]!=c4) continue; //1 egg uses 1 cell in the inventory. so it's ok to delete 1 pet / per cycle if(sd->inventory_data[i]->type==IT_PETEGG && sd->status.inventory[i].card[0] == CARD0_PET) { if (!intif_delete_petdata( MakeDWord(sd->status.inventory[i].card[1], sd->status.inventory[i].card[2]))) continue; //Failed to send delete the pet. } if(sd->status.inventory[i].amount>=amount){ //Logs items, got from (N)PC scripts [Lupus] if(log_config.enable_logs&0x40) log_pick_pc(sd, "N", sd->status.inventory[i].nameid, -amount, &sd->status.inventory[i]); pc_delitem(sd,i,amount,0); return 0; //we deleted exact amount of items. now exit } else { amount-=sd->status.inventory[i].amount; //Logs items, got from (N)PC scripts [Lupus] if(log_config.enable_logs&0x40) log_pick_pc(sd, "N", sd->status.inventory[i].nameid, -sd->status.inventory[i].amount, &sd->status.inventory[i]); pc_delitem(sd,i,sd->status.inventory[i].amount,0); } } return 0; } /*========================================== * Enables/Disables use of items while in an NPC [Skotlex] *------------------------------------------ */ BUILDIN_FUNC(enableitemuse) { struct map_session_data *sd; sd=script_rid2sd(st); if (sd) sd->npc_item_flag = st->oid; return 0; } BUILDIN_FUNC(disableitemuse) { struct map_session_data *sd; sd=script_rid2sd(st); if (sd) sd->npc_item_flag = 0; return 0; } /*========================================== *キャラ関係のパラメータ取得 *------------------------------------------ */ BUILDIN_FUNC(readparam) { int type; struct map_session_data *sd; type=script_getnum(st,2); if( script_hasdata(st,3) ) sd=map_nick2sd(script_getstr(st,3)); else sd=script_rid2sd(st); if(sd==NULL){ script_pushint(st,-1); return 0; } script_pushint(st,pc_readparam(sd,type)); return 0; } /*========================================== *キャラ関係のID取得 *------------------------------------------ */ BUILDIN_FUNC(getcharid) { int num; struct map_session_data *sd; num=script_getnum(st,2); if( script_hasdata(st,3) ) sd=map_nick2sd(script_getstr(st,3)); else sd=script_rid2sd(st); if(sd==NULL || num<0 || num>3){ script_pushint(st,0); //return 0, according docs return 0; } if(num==0) script_pushint(st,sd->status.char_id); if(num==1) script_pushint(st,sd->status.party_id); if(num==2) script_pushint(st,sd->status.guild_id); if(num==3) script_pushint(st,sd->status.account_id); return 0; } /*========================================== *指定IDのPT名取得 *------------------------------------------ */ char *buildin_getpartyname_sub(int party_id) { struct party_data *p; p=party_search(party_id); if(p!=NULL){ char *buf; buf=(char *)aMallocA(NAME_LENGTH*sizeof(char)); memcpy(buf, p->party.name, NAME_LENGTH-1); buf[NAME_LENGTH-1] = '\0'; return buf; } return 0; } BUILDIN_FUNC(getpartyname) { char *name; int party_id; party_id=script_getnum(st,2); name=buildin_getpartyname_sub(party_id); if(name != NULL) script_pushstr(st,name); else script_pushconststr(st,"null"); return 0; } /*========================================== *指定IDのPT人数とメンバーID取得 *------------------------------------------ */ BUILDIN_FUNC(getpartymember) { struct party_data *p; int i,j=0,type=0; p=party_search(script_getnum(st,2)); if( script_hasdata(st,3) ) type=script_getnum(st,3); if(p!=NULL){ for(i=0;iparty.member[i].account_id){ switch (type) { case 2: mapreg_setreg(add_str("$@partymemberaid")+(j<<24),p->party.member[i].account_id); break; case 1: mapreg_setreg(add_str("$@partymembercid")+(j<<24),p->party.member[i].char_id); break; default: mapreg_setregstr(add_str("$@partymembername$")+(j<<24),p->party.member[i].name); } j++; } } } mapreg_setreg(add_str("$@partymembercount"),j); return 0; } /*========================================== * Retrieves party leader. if flag is specified, * return some of the leader data. Otherwise, return name. *------------------------------------------ */ BUILDIN_FUNC(getpartyleader) { int party_id, type = 0, i=0; struct party_data *p; party_id=script_getnum(st,2); if( script_hasdata(st,3) ) type=script_getnum(st,3); p=party_search(party_id); if (p) //Search leader for(i = 0; i < MAX_PARTY && !p->party.member[i].leader; i++); if (!p || i == MAX_PARTY) { //leader not found if (type) script_pushint(st,-1); else script_pushconststr(st,"null"); return 0; } 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 4: script_pushstr(st,aStrdup(mapindex_id2name(p->party.member[i].map))); break; case 5: script_pushint(st,p->party.member[i].lv); break; default: script_pushstr(st,aStrdup(p->party.member[i].name)); break; } return 0; } /*========================================== *指定IDのギルド名取得 *------------------------------------------ */ char *buildin_getguildname_sub(int guild_id) { struct guild *g=NULL; g=guild_search(guild_id); if(g!=NULL){ char *buf; buf=(char *)aMallocA(NAME_LENGTH*sizeof(char)); memcpy(buf, g->name, NAME_LENGTH-1); buf[NAME_LENGTH-1] = '\0'; return buf; } return NULL; } BUILDIN_FUNC(getguildname) { char *name; int guild_id=script_getnum(st,2); name=buildin_getguildname_sub(guild_id); if(name != NULL) script_pushstr(st,name); else script_pushconststr(st,"null"); return 0; } /*========================================== *指定IDのGuildMaster名取得 *------------------------------------------ */ char *buildin_getguildmaster_sub(int guild_id) { struct guild *g=NULL; g=guild_search(guild_id); if(g!=NULL){ char *buf; buf=(char *)aMallocA(NAME_LENGTH*sizeof(char)); memcpy(buf, g->master, NAME_LENGTH-1); buf[NAME_LENGTH-1] = '\0'; return buf; } return 0; } BUILDIN_FUNC(getguildmaster) { char *master; int guild_id=script_getnum(st,2); master=buildin_getguildmaster_sub(guild_id); if(master!=0) script_pushstr(st,master); else script_pushconststr(st,"null"); return 0; } BUILDIN_FUNC(getguildmasterid) { char *master; struct map_session_data *sd=NULL; int guild_id=script_getnum(st,2); master=buildin_getguildmaster_sub(guild_id); if(master!=0){ if((sd=map_nick2sd(master)) == NULL){ script_pushint(st,0); return 0; } script_pushint(st,sd->status.char_id); }else{ script_pushint(st,0); } return 0; } /*========================================== * キャラクタの名前 *------------------------------------------ */ BUILDIN_FUNC(strcharinfo) { struct map_session_data *sd; int num; char *buf; sd=script_rid2sd(st); if (!sd) { //Avoid crashing.... script_pushconststr(st,""); return 0; } num=script_getnum(st,2); switch(num){ case 0: script_pushstr(st,aStrdup(sd->status.name)); break; case 1: buf=buildin_getpartyname_sub(sd->status.party_id); if(buf!=0) script_pushstr(st,buf); else script_pushconststr(st,""); break; case 2: buf=buildin_getguildname_sub(sd->status.guild_id); if(buf != NULL) script_pushstr(st,buf); else script_pushconststr(st,""); break; default: ShowWarning("buildin_strcharinfo: unknown parameter."); script_pushconststr(st,""); break; } return 0; } unsigned int equip[10]={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}; /*========================================== * GetEquipID(Pos); Pos: 1-10 *------------------------------------------ */ BUILDIN_FUNC(getequipid) { int i,num; struct map_session_data *sd; struct item_data* item; sd=script_rid2sd(st); if(sd == NULL) { ShowError("getequipid: sd == NULL\n"); return 0; } num=script_getnum(st,2); i=pc_checkequip(sd,equip[num-1]); if(i >= 0){ item=sd->inventory_data[i]; if(item) script_pushint(st,item->nameid); else script_pushint(st,0); }else{ script_pushint(st,-1); } return 0; } /*========================================== * 装備名文字列(精錬メニュー用) *------------------------------------------ */ BUILDIN_FUNC(getequipname) { int i,num; struct map_session_data *sd; struct item_data* item; char *buf; buf=(char *)aMallocA(64*sizeof(char)); sd=script_rid2sd(st); num=script_getnum(st,2); i=pc_checkequip(sd,equip[num-1]); if(i >= 0){ item=sd->inventory_data[i]; if(item) sprintf(buf,"%s-[%s]",pos[num-1],item->jname); else sprintf(buf,"%s-[%s]",pos[num-1],pos[10]); }else{ sprintf(buf,"%s-[%s]",pos[num-1],pos[10]); } script_pushstr(st,buf); return 0; } /*========================================== * getbrokenid [Valaris] *------------------------------------------ */ BUILDIN_FUNC(getbrokenid) { int i,num,id=0,brokencounter=0; struct map_session_data *sd; sd=script_rid2sd(st); num=script_getnum(st,2); for(i=0; istatus.inventory[i].attribute==1){ brokencounter++; if(num==brokencounter){ id=sd->status.inventory[i].nameid; break; } } } script_pushint(st,id); return 0; } /*========================================== * repair [Valaris] *------------------------------------------ */ BUILDIN_FUNC(repair) { int i,num; int repaircounter=0; struct map_session_data *sd; sd=script_rid2sd(st); num=script_getnum(st,2); for(i=0; istatus.inventory[i].attribute==1){ repaircounter++; if(num==repaircounter){ sd->status.inventory[i].attribute=0; clif_equiplist(sd); clif_produceeffect(sd, 0, sd->status.inventory[i].nameid); clif_misceffect(&sd->bl, 3); break; } } } return 0; } /*========================================== * 装備チェック *------------------------------------------ */ BUILDIN_FUNC(getequipisequiped) { int i,num; struct map_session_data *sd; num=script_getnum(st,2); sd=script_rid2sd(st); if ((num - 1) >= (sizeof(equip) / sizeof(equip[0]))) i = -1; else i=pc_checkequip(sd,equip[num-1]); if(i >= 0) script_pushint(st,1); else script_pushint(st,0); return 0; } /*========================================== * 装備品精錬可能チェック *------------------------------------------ */ BUILDIN_FUNC(getequipisenableref) { int i,num; struct map_session_data *sd; num=script_getnum(st,2); sd=script_rid2sd(st); i=pc_checkequip(sd,equip[num-1]); if(i >= 0 && sd->inventory_data[i] && !sd->inventory_data[i]->flag.no_refine) { script_pushint(st,1); } else { script_pushint(st,0); } return 0; } /*========================================== * 装備品鑑定チェック *------------------------------------------ */ BUILDIN_FUNC(getequipisidentify) { int i,num; struct map_session_data *sd; num=script_getnum(st,2); sd=script_rid2sd(st); i=pc_checkequip(sd,equip[num-1]); if(i >= 0) script_pushint(st,sd->status.inventory[i].identify); else script_pushint(st,0); return 0; } /*========================================== * 装備品精錬度 *------------------------------------------ */ BUILDIN_FUNC(getequiprefinerycnt) { int i,num; struct map_session_data *sd; num=script_getnum(st,2); sd=script_rid2sd(st); i=pc_checkequip(sd,equip[num-1]); if(i >= 0) script_pushint(st,sd->status.inventory[i].refine); else script_pushint(st,0); return 0; } /*========================================== * 装備品武器LV *------------------------------------------ */ BUILDIN_FUNC(getequipweaponlv) { int i,num; struct map_session_data *sd; num=script_getnum(st,2); sd=script_rid2sd(st); i=pc_checkequip(sd,equip[num-1]); if(i >= 0 && sd->inventory_data[i]) script_pushint(st,sd->inventory_data[i]->wlv); else script_pushint(st,0); return 0; } /*========================================== * 装備品精錬成功率 *------------------------------------------ */ BUILDIN_FUNC(getequippercentrefinery) { int i,num; struct map_session_data *sd; num=script_getnum(st,2); sd=script_rid2sd(st); i=pc_checkequip(sd,equip[num-1]); if(i >= 0 && sd->status.inventory[i].nameid && sd->status.inventory[i].refine < MAX_REFINE) script_pushint(st,percentrefinery[itemdb_wlv(sd->status.inventory[i].nameid)][(int)sd->status.inventory[i].refine]); else script_pushint(st,0); return 0; } /*========================================== * 精錬成功 *------------------------------------------ */ BUILDIN_FUNC(successrefitem) { int i,num,ep; struct map_session_data *sd; num=script_getnum(st,2); sd=script_rid2sd(st); i=pc_checkequip(sd,equip[num-1]); if(i >= 0) { ep=sd->status.inventory[i].equip; //Logs items, got from (N)PC scripts [Lupus] if(log_config.enable_logs&0x40) log_pick_pc(sd, "N", sd->status.inventory[i].nameid, -1, &sd->status.inventory[i]); sd->status.inventory[i].refine++; pc_unequipitem(sd,i,2); clif_refine(sd->fd,0,i,sd->status.inventory[i].refine); clif_delitem(sd,i,1); //Logs items, got from (N)PC scripts [Lupus] if(log_config.enable_logs&0x40) log_pick_pc(sd, "N", sd->status.inventory[i].nameid, 1, &sd->status.inventory[i]); clif_additem(sd,i,1,0); pc_equipitem(sd,i,ep); clif_misceffect(&sd->bl,3); if(sd->status.inventory[i].refine == MAX_REFINE && 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; } } } return 0; } /*========================================== * 精錬失敗 *------------------------------------------ */ BUILDIN_FUNC(failedrefitem) { int i,num; struct map_session_data *sd; num=script_getnum(st,2); sd=script_rid2sd(st); i=pc_checkequip(sd,equip[num-1]); if(i >= 0) { //Logs items, got from (N)PC scripts [Lupus] if(log_config.enable_logs&0x40) log_pick_pc(sd, "N", sd->status.inventory[i].nameid, -1, &sd->status.inventory[i]); sd->status.inventory[i].refine = 0; pc_unequipitem(sd,i,3); // 精錬失敗エフェクトのパケット clif_refine(sd->fd,1,i,sd->status.inventory[i].refine); pc_delitem(sd,i,1,0); // 他の人にも失敗を通知 clif_misceffect(&sd->bl,2); } return 0; } /*========================================== * *------------------------------------------ */ BUILDIN_FUNC(statusup) { int type; struct map_session_data *sd; type=script_getnum(st,2); sd=script_rid2sd(st); pc_statusup(sd,type); return 0; } /*========================================== * *------------------------------------------ */ BUILDIN_FUNC(statusup2) { int type,val; struct map_session_data *sd; type=script_getnum(st,2); val=script_getnum(st,3); sd=script_rid2sd(st); pc_statusup2(sd,type,val); return 0; } /// See 'doc/item_bonus.txt' /// bonus , /// bonus2 ,, /// bonus3 ,,, /// bonus4 ,,,, BUILDIN_FUNC(bonus) { int type; int type2; int type3; int type4; int val; TBL_PC* sd; sd = script_rid2sd(st); if( sd == NULL ) return 1; // no player attached type = script_getnum(st,2); switch( script_lastdata(st) ){ case 3: val = script_getnum(st,3); pc_bonus(sd, type, val); break; case 4: type2 = script_getnum(st,3); val = script_getnum(st,4); pc_bonus2(sd, type, type2, val); break; case 5: type2 = script_getnum(st,3); type3 = script_getnum(st,4); val = script_getnum(st,5); pc_bonus3(sd, type, type2, type3, val); break; case 6: type2 = script_getnum(st,3); type3 = script_getnum(st,4); type4 = script_getnum(st,5); val = script_getnum(st,6); pc_bonus4(sd, type, type2, type3, type4, val); break; default: ShowDebug("buildin_bonus: unexpected last data (%d)\n", script_lastdata(st)); } return 0; } /// Changes the level of a player skill. /// skill ,{,} /// @see pc_skill() for flag BUILDIN_FUNC(skill) { int id; int level; int flag = 1; TBL_PC* sd; sd = script_rid2sd(st); if( sd == NULL ) return 1;// no player attached, report source id = script_getnum(st,2); level = script_getnum(st,3); if( script_hasdata(st,4) ) flag = script_getnum(st,4); pc_skill(sd, id, level, flag); return 0; } /// Changes the level of a player skill. /// addtoskill ,{,} /// @see pc_skill() for flag BUILDIN_FUNC(addtoskill) { int id; int level; int flag = 2; TBL_PC* sd; sd = script_rid2sd(st); if( sd == NULL ) return 1;// no player attached, report source id = script_getnum(st,2); level = script_getnum(st,3); if( script_hasdata(st,4) ) flag = script_getnum(st,4); pc_skill(sd, id, level, flag); return 0; } /// Increases the level of the guild skill. /// guildskill , BUILDIN_FUNC(guildskill) { int id; int level; TBL_PC* sd; int i; sd = script_rid2sd(st); if( sd == NULL ) return 1;// no player attached, report source id = script_getnum(st,2); level = script_getnum(st,3); for( i=0; i < level; i++ ) guild_skillup(sd, id); return 0; } /// Returns the level of the player skill. /// getskilllv() -> BUILDIN_FUNC(getskilllv) { int id; TBL_PC* sd; sd = script_rid2sd(st); if( sd == NULL ) return 1;// no player attached, report source id = script_getnum(st,2); script_pushint(st, pc_checkskill(sd,id)); return 0; } /// Returns the level of the guild skill. /// getgdskilllv(,) -> BUILDIN_FUNC(getgdskilllv) { int guild_id; int skill_id; struct guild* g; guild_id = script_getnum(st,2); skill_id = script_getnum(st,3); g = guild_search(guild_id); if( g == NULL ) script_pushint(st, -1); else script_pushint(st, guild_checkskill(g,skill_id)); return 0; } /// Returns the 'basic_skill_check' setting. /// basicskillcheck() -> BUILDIN_FUNC(basicskillcheck) { script_pushint(st, battle_config.basic_skill_check); return 0; } /// Returns the GM level of the player. /// getgmlevel() -> BUILDIN_FUNC(getgmlevel) { TBL_PC* sd; sd = script_rid2sd(st); if( sd == NULL ) return 1;// no player attached, report source script_pushint(st, pc_isGM(sd)); return 0; } /// Terminates the execution of this script instance. /// end BUILDIN_FUNC(end) { st->state = END; return 0; } /// Checks if the player has that option. /// checkoption(