Skip to content
Permalink
master
Switch branches/tags
Go to file
 
 
Cannot retrieve contributors at this time
#include "script.h"
FILE * node_dbg = NULL;
/* re */ static int stack_eng_item_work(struct rbt_node *, void *, int);
/* re */ static int stack_eng_skill_work(struct rbt_node *, void *, int);
/* re */ static int stack_eng_map_work(struct rbt_node *, void *, int);
/* re */ static int stack_eng_db_work(struct rbt_node *, void *, int);
/* re */ static int stack_eng_group_name(char **, const char *);
/* re */ static int stack_eng_int_re(block_r *, node *, int, int);
/* re */ static int stack_eng_int_signed_re(block_r *, node *, int, const char *, const char *, int);
/* re */ static int evaluate_expression_formula_concat(struct rbt_node *, void *, int);
/* re */ static int evaluate_expression_formula_length(struct rbt_node *, void *, int);
/* re */ static int evaluate_expression_formula_re(block_r *, rbt_logic *, rbt_tree *);
/* re */ static int evaluate_expression_formula(block_r *, rbt_logic *, char **);
/* re */ static node * evaluate_expression_recursive(block_r *, char **, int, int, rbt_logic *, rbt_tree * id_tree, int);
/* re */ static int evaluate_expression_end_parenthesis(char **, int, int, int *);
/* re */ static int evaluate_expression_sub(block_r *, char **, int *, int, rbt_logic *, rbt_tree *, int, node **);
/* re */ static int evaluate_expression_var(block_r *, char **, int *, int, rbt_logic *, int, node **);
/* re */ static int evaluate_function(block_r *, char **, int, int, var_res *, node *);
/* re */ static int evaluate_function_rand(block_r *, int, int, var_res *, node *);
/* re */ static int evaluate_function_groupranditem(block_r *, int, int, var_res *, node *);
/* re */ static int evaluate_function_readparam(block_r *, int, int, var_res *, node *);
/* re */ static int evaluate_function_getskilllv(block_r *, int, int, var_res *, node *);
/* re */ static int evaluate_function_isequipped(block_r *, int, int, var_res *, node *);
/* re */ static int evaluate_function_getequiprefinerycnt(block_r *, int, int, var_res *, node *);
/* re */ static int evaluate_function_getiteminfo(block_r *, int, int, var_res *, node *);
/* re */ static int evaluate_function_getequipid(block_r *, int, int, var_res *, node *);
/* re */ static int evaluate_function_gettime(block_r *, int, int, var_res *, node *);
/* re */ static int evaluate_function_callfunc(block_r *, int, int, var_res *, node *);
/* re */ static int evaluate_function_countitem(block_r *, int, int, var_res *, node *);
/* re */ static int evaluate_function_pow(block_r *, int, int, var_res *, node *);
/* re */ static int evaluate_function_strcharinfo(block_r *, int, int, var_res *, node *);
/* re */ static int evaluate_function_setoption(block_r *, int, int, var_res *, node *);
/* re */ static int script_generate_write_class_work(struct rbt_node *, void *, int);
/* re */ static int script_generate_write_strcharinfo_work(struct rbt_node *, void *, int);
/* re */ static int script_generate_write_getequipid_work(struct rbt_node *, void *, int);
/* re */ static int script_generate_write_getiteminfo_work(struct rbt_node *, void *, int);
int block_init(block_r ** block) {
block_r * _block = NULL;
exit_null_safe(1, block);
_block = calloc(1, sizeof(block_r));
if(NULL == _block)
return CHECK_FAILED;
_block->next = _block;
_block->prev = _block;
_block->free = 1;
*block = _block;
return CHECK_PASSED;
}
int block_deit(block_r ** block) {
block_r * _block = NULL;
exit_null_safe(2, block, *block);
_block = *block;
free(_block);
*block = NULL;
return CHECK_PASSED;
}
int block_append(block_r * parent, block_r * child) {
exit_null_safe(2, parent, child);
parent->next->prev = child->prev;
child->prev->next = parent->next;
parent->next = child;
child->prev = parent;
return CHECK_PASSED;
}
int block_remove(block_r * block) {
exit_null_safe(1, block);
block->prev->next = block->next;
block->next->prev = block->prev;
block->next = block;
block->prev = block;
return CHECK_PASSED;
}
int block_reset(block_r * block) {
exit_null_safe(1, block);
/* reset block and item id */
block->item_id = 0;
SAFE_FREE(block->name);
block->type = 0;
/* reset block stack */
block->arg_cnt = 0;
block->ptr_cnt = 0;
block->eng_cnt = 0;
/* reset block linking */
block->link = NULL;
block->set = NULL;
/* free set block's node */
if(NULL != block->set_node) {
node_free(block->set_node);
block->set_node = NULL;
}
/* if, else, and for blocks uses a logic tree
* to manage predicates for child blocks */
rbt_logic_deit(&block->logic);
block->free = 1;
return CHECK_PASSED;
}
int block_stack_vararg(block_r * block, int type, const char * format, ...) {
int len = 0;
int cnt = 0;
va_list args;
exit_null_safe(2, block, format);
/* write the formatted arguments */
va_start(args, format);
cnt = BUF_SIZE - block->arg_cnt;
len = vsnprintf(&block->arg[block->arg_cnt], cnt, format, args);
va_end(args);
/* check whether the arguments is truncated */
if(len >= cnt)
return exit_func_safe("truncated formatted a"
"rgument string in item %d", block->item_id);
/* replace the previous null with newline */
if(type & FLAG_CONCAT) {
/* error on invalid top string */
if(block->arg_cnt < 0)
return CHECK_FAILED;
if(type & FLAG_EMPTY)
block->arg[block->arg_cnt - 1] = ' ';
else if(type & FLAG_COMMA)
block->arg[block->arg_cnt - 1] = ',';
else
block->arg[block->arg_cnt - 1] = '\n';
} else {
/* set the stack pointers */
switch (type & 0x3) {
case TYPE_PTR:
cnt = block->ptr_cnt;
if (cnt >= PTR_SIZE)
return exit_func_safe("exceed translated string ar"
"ray size %d in item %d", PTR_SIZE, block->item_id);
block->ptr[cnt] = &block->arg[block->arg_cnt];
block->ptr_cnt++;
break;
case TYPE_ENG:
cnt = block->eng_cnt;
if (cnt >= PTR_SIZE)
return exit_func_safe("exceed argument string arr"
"ay size %d in item %d", PTR_SIZE, block->item_id);
block->eng[cnt] = &block->arg[block->arg_cnt];
block->eng_cnt++;
break;
default:
return exit_func_safe("invalid type %d in item %d", type, block->item_id);
}
}
/* set the new stack top */
block->arg_cnt += (len + 1);
return CHECK_PASSED;
}
int block_stack_push(block_r * block, int type, const char * str) {
int ret = 0;
int cnt = 0;
int off = 0;
int len = 0;
char * buf = NULL;
/* check for infidels */
exit_null_safe(2, block, str);
/* get buffer state */
off = block->arg_cnt;
buf = &block->arg[off];
/* check buffer size */
len = (int) strlen(str) + 1;
if ((off + len) >= BUF_SIZE)
return exit_func_safe("buffer overflow in item %d", block->item_id);
/* get translated or argument string pointer */
switch (type) {
case TYPE_PTR:
cnt = block->ptr_cnt;
if (cnt >= PTR_SIZE)
return exit_func_safe("exceed translated string array"
" size %d in item %d", PTR_SIZE, block->item_id);
block->ptr[cnt] = buf;
block->ptr_cnt++;
break;
case TYPE_ENG:
cnt = block->eng_cnt;
if (cnt >= PTR_SIZE)
return exit_func_safe("exceed argument string array"
" size %d in item %d", PTR_SIZE, block->item_id);
block->eng[cnt] = buf;
block->eng_cnt++;
break;
default:
return exit_func_safe("invalid type %d in item %d", type, block->item_id);
}
/* update buffer state */
ret = sprintf(buf, "%s", str);
if (ret + 1 != len)
return exit_func_safe("failed to write buffer in item %d", block->item_id);
/* length of string + null character + 1 to pointer pass the null character */
block->arg_cnt += len;
return SCRIPT_PASSED;
}
int block_stack_pop(block_r * block, int type) {
int cnt = 0;
int len = 0;
char * buf = NULL;
switch (type) {
case TYPE_PTR:
cnt = --block->ptr_cnt;
if (cnt < 0)
return exit_func_safe("empty buffer in item %d", block->item_id);
buf = block->ptr[cnt];
block->ptr[cnt] = NULL;
break;
case TYPE_ENG:
cnt = --block->eng_cnt;
if (cnt < 0)
return exit_func_safe("empty buffer in item %d", block->item_id);
buf = block->eng[cnt];
block->eng[cnt] = NULL;
break;
default:
return exit_func_safe("invalid type %d in item %d", type, block->item_id);
}
/* update buffer state */
len = (int) strlen(buf) + 1;
block->arg_cnt -= len;
/* removing the last string at block->eng[0] or block->ptr[0]
* causes the arg_cnt to be -1, which corrupts the block->type. */
if (block->arg_cnt < 0)
block->arg_cnt = 0;
return SCRIPT_PASSED;
}
int block_stack_dump(block_r * block, FILE * stream) {
int i = 0;
block_r * iter = NULL;
iter = block;
do {
/* dump the used block only */
if(iter->free == 0) {
fprintf(stream,
" item id: %d\n"
" block addr: %p\n"
" block name: %s [%d]\n"
" block link: %p\n"
" block set: %p\n"
" arg stack: %d\n"
" eng stack: %d\n"
" logic tree: %p\n",
iter->item_id,
(void *) iter,
iter->name,
iter->type,
(void *) iter->link,
(void *) iter->set,
iter->ptr_cnt,
iter->eng_cnt,
(void *) iter->logic);
/* dump the stack */
for(i = 0; i < iter->ptr_cnt; i++)
fprintf(stream, " arg[%5d]: %s\n", i, iter->ptr[i]);
for(i = 0; i < iter->eng_cnt; i++)
fprintf(stream, " eng[%5d]: %s\n", i, iter->eng[i]);
fprintf(stream, " buf: ");
for(i = 0; i < iter->arg_cnt; i++)
(iter->arg[i] == '\0') ?
putc(iter->arg[i], stream):
putc(iter->arg[i], stream);
putc('\n', stream);
fprintf(stream, "------------;\n");
}
iter = iter->next;
} while(iter != block);
return CHECK_PASSED;
}
int script_block_new(script_t * script, block_r ** block) {
block_r * _block = NULL;
exit_null_safe(2, script, block);
if(NULL != script->free_blocks) {
/* grab a block from the free list */
_block = script->free_blocks->next;
if(block_remove(_block))
return CHECK_FAILED;
/* last block was grab from the free list */
if(_block == script->free_blocks)
script->free_blocks = NULL;
} else {
/* create a new block */
if(block_init(&_block))
return CHECK_FAILED;
}
/* add the block to the used list */
if(NULL != script->blocks)
block_append(script->blocks, _block);
/* :D */
_block->script = script;
/* script->blocks->next is the start of the list
* and the script->blocks is the end of the list */
script->blocks = _block;
script->blocks->free = 0;
*block = script->blocks;
return CHECK_PASSED;
}
int script_block_free(script_t * script, block_r ** block) {
block_r * _block = NULL;
exit_null_safe(3, script, block, *block);
_block = *block;
if(script->blocks == _block) {
/* move the tail to the last added block */
script->blocks = script->blocks->prev;
/* if no blocks are added, then set empty list */
if(script->blocks == _block)
script->blocks = NULL;
}
/* reset and remove the block
* then append to the tail of
* the block list */
if( block_reset(_block) ||
block_remove(_block))
return CHECK_FAILED;
if(NULL != script->free_blocks)
block_append(script->free_blocks, _block);
script->free_blocks = _block;
*block = NULL;
return CHECK_PASSED;
}
int script_block_free_all(script_t * script) {
block_r * temp = NULL;
exit_null_safe(1, script);
/* free all the blocks */
while(script->blocks) {
temp = script->blocks;
script_block_free(script, &temp);
}
return CHECK_PASSED;
}
int script_block_release(script_t * script) {
block_r * iter = NULL;
block_r * temp = NULL;
exit_null_safe(1, script);
if(script_block_free_all(script))
return CHECK_FAILED;
if(NULL == script->free_blocks)
return CHECK_PASSED;
iter = script->free_blocks->next;
while(iter != script->free_blocks) {
temp = iter;
iter = iter->next;
/* remove the block from the
* list and free the memory */
if(block_remove(temp) ||
block_deit(&temp))
return CHECK_FAILED;
}
/* free the root block */
return block_deit(&script->free_blocks);
}
int script_map_init(script_t * script, const char * map_path) {
exit_null_safe(2, script, map_path);
script->map = luaL_newstate();
if(NULL == script->map ||
luaL_loadfile(script->map, map_path) ||
lua_pcall(script->map, 0, 0, 0))
return exit_func_safe("failed to load mapping tables from %s", map_path);
return CHECK_PASSED;
}
int script_map_id(block_r * block, char * map, int id, char ** val) {
int ret = 0;
int index = 0;
lua_State * maps = NULL;
exit_null_safe(3, block, map, val);
maps = block->script->map;
/* push table */
lua_getglobal(maps, map);
if(!lua_istable(maps, -1)) {
exit_func_safe("%s is not a valid map", map);
goto failed;
}
/* push key */
index = lua_gettop(maps);
lua_pushinteger(maps, id);
lua_gettable(maps, index);
if(!lua_isstring(maps, -1))
goto failed;
/* table[key] */
*val = convert_string(lua_tostring(maps, -1));
clean:
/* reset the lua stack */
lua_settop(maps, 0);
return ret;
failed:
ret = CHECK_FAILED;
goto clean;
}
int script_map_deit(script_t * script) {
exit_null_safe(1, script);
if(NULL != script->map)
lua_close(script->map);
return CHECK_PASSED;
}
int script_init(script_t ** script, const char * rdb_path, const char * sdb_path, const char * map_path, int mode) {
script_t * _script = NULL;
/* error on invalid reference */
exit_null_safe(4, script, rdb_path, sdb_path, map_path);
/* error on invalid server mode */
if(!(mode & DB_MODE))
return exit_func_safe("%d is an invalid mode", mode);
/* construct script struct */
_script = calloc(1, sizeof(script_t));
if(NULL == _script)
return CHECK_FAILED;
/* set the mode */
_script->mode = mode;
/* initialize resource and server databases and mapping tables */
if (init_db_load(&_script->db, rdb_path, sdb_path, _script->mode) ||
script_map_init(_script, map_path)) {
script_deit(&_script);
return CHECK_FAILED;
}
*script = _script;
return CHECK_PASSED;
}
int script_deit(script_t ** script) {
script_t * _script = NULL;
exit_null_safe(2, script, *script);
_script = *script;
/* release all mappings */
script_map_deit(_script);
/* release all databases */
if(_script->db)
deit_db_load(&_script->db);
/* release all blocks */
script_block_release(_script);
/* release all nodes */
node_release(_script);
/* release script struct */
SAFE_FREE(_script);
*script = NULL;
return CHECK_PASSED;
}
int script_check(const char * script) {
int i;
int level = 0;
for (i = 0; script[i] != '\0'; i++) {
if (script[i + 1] != '\0' && script[i] == '/' && script[i + 1] == '*') {
level++;
} else if (script[i] != '\0' && script[i] == '*' && script[i + 1] == '/') {
level--;
i += 2;
}
if (!level && ';' == script[i])
return CHECK_PASSED;
}
return CHECK_FAILED;
}
int script_lexical(token_r * token, char * script) {
int i = 0;
int id = 0; /* indicate previous token is an identifer */
int len = 0; /* length of script */
char * buf = NULL; /* token buffer */
char ** ptr = NULL; /* token string pointers */
int buf_cnt = 0; /* track the token buffer index */
int ptr_cnt = 0; /* track the script buffer index */
int comment_level = 0; /* counter for multiline comment level */
char * swap = NULL; /* temporary value to swap string pointers */
/* check null paramaters */
exit_null_safe(2, token, script);
/* check script length */
len = (int) strlen(script);
if(len >= BUF_SIZE) {
exit_func_safe("script %s exceed buffer size of %d", script, BUF_SIZE);
return SCRIPT_FAILED;
}
/* remove whitespace and newline */
for(i = 0; i < len; i++)
script[i] = (isspace(script[i])) ? ' ' : script[i];
/* dereference token structure */
buf = token->script;
ptr = token->script_ptr;
/* tokenize operators, identifiers, and syntax */
for(i = 0; i < len; i++) {
/* skipped multiline comments embedded in script */
if(i+1 < len && script[i] == '/' && script[i+1] == '*')
comment_level++;
else if(i+1 < len && script[i] == '*' && script[i+1] == '/') {
comment_level--;
i += 2;
}
if(comment_level) continue;
/* read an identifier and continue */
if(SCRIPT_STRING(script[i]) || SCRIPT_SYMBOL(script[i])) {
/* add identifier tokens */
ptr[ptr_cnt++] = &buf[buf_cnt];
while(SCRIPT_STRING(script[i]) || SCRIPT_SYMBOL(script[i]))
buf[buf_cnt++] = script[i++];
buf[buf_cnt++] = '\0';
/* lexer skips a character with continue */
i--;
id = 1;
continue;
}
/* skip space / single quote / double quote / slash characters */
if(isspace(script[i]) || script[i] == '\'' || script[i] == '"' || script[i] == '\\')
continue;
/* replace assignment operator with set block */
if( id && /* last token must be an identifier */
(i+1 < len && script[i+1] != '=') &&
(i-1 > 0 && script[i-1] != '=') &&
script[i] == '=') {
/* convert = into a set block */
ptr[ptr_cnt] = &buf[buf_cnt];
buf_cnt += sprintf(ptr[ptr_cnt], "set");
buf[buf_cnt++] = '\0';
/* swap identifier with the set identifier */
swap = ptr[ptr_cnt];
ptr[ptr_cnt] = ptr[ptr_cnt - 1];
ptr[ptr_cnt - 1] = swap;
ptr_cnt++;
/* add the , operator or syntax */
ptr[ptr_cnt++] = &buf[buf_cnt];
buf[buf_cnt++] = ',';
buf[buf_cnt++] = '\0';
/* add the operator or syntax */
} else {
ptr[ptr_cnt++] = &buf[buf_cnt];
buf[buf_cnt++] = script[i];
buf[buf_cnt++] = '\0';
}
id = 0;
}
/* check that the script was tokenize */
if(ptr_cnt <= 0)
return SCRIPT_FAILED;
/* set the total number of tokens */
token->script_cnt = ptr_cnt;
return SCRIPT_PASSED;
}
int script_analysis(script_t * script, token_r * token_list, block_r * parent, block_r ** var) {
int i = 0;
int block_cnt = 0;
char ** token = NULL;
int token_cnt = 0;
block_res block_type; /* block id in block_db.txt */
block_r * block = NULL; /* current block being parsed */
block_r * link = parent; /* link blocks to if, else-if, else, and for */
block_r * set = (var != NULL) ? *var : NULL;
/* check null paramaters */
exit_null_safe(2, script, token_list);
/* dereference the token paramaters */
token = token_list->script_ptr;
token_cnt = token_list->script_cnt;
/* check empty token list */
if(token_cnt <= 0)
return SCRIPT_FAILED;
/* parse tokens to build blocks */
for(i = 0; i < token_cnt; i++) {
if(token[i][0] == '{' || token[i][0] == '}') continue;
if(!block_name(script->db, &block_type, token[i], (int) strlen(token[i]))) {
/* count the number of blocks parsed */
block_cnt++;
/* allocate and initialize a block */
if(script_block_new(script, &block))
return SCRIPT_FAILED;
block->name = convert_string(token[i]);
block->item_id = script->item.id;
block->type = block_type.id;
/* link to parent or link else-if and else with if blocks */
link = is_nil(link) ? parent : link;
block->link = link;
/* link the end of the current set list; blocks will not
* search set blocks defined after it, but might within
* loop, but I don't know. */
block->set = set;
/* parser does not set the first pointer */
block->ptr[0] = block->arg;
block->ptr_cnt++;
/* retrieve all the arguments of the block */
switch(block->type) {
case 26: /* parse if blocks */
/* else-if and else will link this block */
link = block;
/* parse the condition ( <condition> ) */
if( token[i+1][0] != '(' ||
script_parse(token_list, &i, block, '\0', ')', FLAG_PARSE_LAST_TOKEN) ||
token[i][0] != ')')
return exit_abt_safe("failed to parsed the if condition");
/* compound or simple statement and perform
* script_analysis to create create the new
* blocks */
if( ((token[i+1][0] == '{') ?
script_parse(token_list, &i, block, '\0', '}', FLAG_PARSE_LAST_TOKEN):
script_parse(token_list, &i, block, '\0', ';', FLAG_PARSE_LAST_TOKEN | FLAG_PARSE_KEEP_COMMA)) ||
script_analysis_(script, block->ptr[1], block, &set)) {
return exit_abt_safe("failed to extend if block");
}
break;
case 27: /* parse else blocks */
/* link else-if and else blocks */
block->link = link;
if(strcmp(token[i + 1], "if") == 0) {
/* skip the 'if' */
i += 1;
/* parse the condition ( <condition> ) */
if(token[i+1][0] != '(' ||
script_parse(token_list, &i, block, '\0', ')', FLAG_PARSE_LAST_TOKEN) ||
token[i][0] != ')')
return SCRIPT_FAILED;
/* compound or simple statement and perform
* script_analysis to create create the new
* blocks */
if( ((token[i+1][0] == '{') ?
script_parse(token_list, &i, block, '\0', '}', FLAG_PARSE_LAST_TOKEN):
script_parse(token_list, &i, block, '\0', ';', FLAG_PARSE_LAST_TOKEN | FLAG_PARSE_KEEP_COMMA)) ||
script_analysis_(script, block->ptr[1], block, &set))
return SCRIPT_FAILED;
/* else if continues the linking */
link = block;
} else {
/* compound or simple statement */
if( ((token[i+1][0] == '{') ?
script_parse(token_list, &i, block, '\0', '}', FLAG_PARSE_LAST_TOKEN):
script_parse(token_list, &i, block, '\0', ';', FLAG_PARSE_LAST_TOKEN | FLAG_PARSE_KEEP_COMMA)) ||
script_analysis_(script, block->ptr[0], block, &set))
return SCRIPT_FAILED;
/* else ends the local linking */
link = parent;
}
break;
case 63: /* getmapxy */
if (script_parse(token_list, &i, block, ',', ';', FLAG_PARSE_NORMAL))
return SCRIPT_FAILED;
/* parsing is off-by-one */
block->ptr_cnt--;
/* getmapxy sets the first variable to the current map
* which is equivalent to strcharinfo(3) and second and
* third argument is the player's current coordinates. */
if(block_stack_vararg(block, TYPE_PTR, "set %s,strcharinfo(3); set %s,%d; set %s,"
"%d;",block->ptr[0], block->ptr[1], COORD_CURRENT, block->ptr[2], COORD_CURRENT) ||
script_analysis_(script, block->ptr[4], block, &set))
return CHECK_FAILED;
break;
case 5: /* autobonus */
case 6: /* autobonus2 */
case 7: /* autobonus3 */
case 51: /* bonus_script */
/* parse the blocks before extending */
if(script_parse(token_list, &i, block, ',', ';', FLAG_PARSE_NORMAL))
return SCRIPT_FAILED;
/* extend the block list by using subscript */
if(script_analysis_(script, block->ptr[0], block, &set))
return SCRIPT_FAILED;
break;
case 62: /* for */
/* to-do: add support for for blocks */
return exit_stop("for block are currently not supported");
case 28:
/* add new set block as the tail */
if(var != NULL) *var = block;
set = block;
default: /* parse all blocks */
if(script_parse(token_list, &i, block, ',', ';', FLAG_PARSE_NORMAL))
return SCRIPT_FAILED;
break;
}
/* parsing is off-by-one; Q.Q I suck */
block->ptr_cnt--;
} else {
/* skip single semicolon; treat as empty statement */
if(token[i][0] != ';') {
/* to-do: translate script engine variables */
continue;
}
}
}
return SCRIPT_PASSED;
}
int script_analysis_(script_t * script, char * subscript, block_r * parent, block_r ** var) {
token_r * token = NULL;
exit_null_safe(2, script, subscript);
token = calloc(1, sizeof(token_r));
if(NULL == token)
return CHECK_FAILED;
if( script_lexical(token, subscript) ||
script_analysis(script, token, parent, var)) {
SAFE_FREE(token);
return SCRIPT_FAILED;
}
SAFE_FREE(token);
return SCRIPT_PASSED;
}
int check_loop_expression(script_t * script, char * expr, char * end) {
token_r token;
if(script_lexical(&token, expr)) {
exit_func_safe("failed to tokenize '%s' for item id %d", expr, script->item.id);
return SCRIPT_FAILED;
}
/* check whether ; semicolon exist */
if(token.script_cnt <= 0) {
exit_func_safe("missing '%s' in for loop for item id %d", end, script->item.id);
return SCRIPT_FAILED;
}
/* check whether ; is the first token */
return (strcmp(token.script_ptr[0], end) == 0) ? SCRIPT_FAILED : SCRIPT_PASSED;
}
int script_parse(token_r * token, int * position, block_r * block, char delimit, char end, int flag) {
/* token reading data */
int i = 0;
int j = 0;
int len = 0;
char ** script_ptr = NULL;
int script_cnt = 0;
int bracket_level = 0;
int subexpr_level = 0;
/* block parsing data */
char * arg = NULL;
char ** ptr = NULL;
int arg_cnt = 0;
int ptr_cnt = 0;
/* check null paramaters */
exit_null_safe(3, token, position, block);
/* initialize token set */
script_ptr = token->script_ptr;
script_cnt = token->script_cnt;
/* initialize block */
arg = block->arg;
ptr = block->ptr;
arg_cnt = block->arg_cnt;
ptr_cnt = block->ptr_cnt;
if(*position < 0 || *position >= script_cnt)
return SCRIPT_FAILED;
/* parse the block */
for(i = *position + 1; i < script_cnt; i++) {
/* indicate bracket level or subexpression level */
switch(script_ptr[i][0]) {
case '{': bracket_level++; break;
case '}': bracket_level--; break;
case '(': subexpr_level++; break;
case ')': subexpr_level--; break;
default: break;
}
/* parsing top-level script; delimit exit only at top-level
* flag 4 allows us to bypass any leveling */
if((!bracket_level && !subexpr_level) || flag & FLAG_PARSE_SKIP_LEVEL) {
/* delimiter, parse another argument */
if(script_ptr[i][0] == delimit) {
if(arg_cnt > 0 && arg[arg_cnt-1] == ' ') {
arg[arg_cnt-1] = '\0';
ptr[ptr_cnt++] = &arg[arg_cnt];
} else {
arg[arg_cnt++] = '\0';
ptr[ptr_cnt++] = &arg[arg_cnt];
}
continue;
/* semicolon ends the block */
} else if(script_ptr[i][0] == end) {
/* include last token */
if(flag & FLAG_PARSE_LAST_TOKEN) {
/* write the argument */
len = (int) strlen(script_ptr[i]);
for(j = 0; j < len; j++)
arg[arg_cnt++] = script_ptr[i][j];
/* don't add space after athena symbol prefixes */
if(!SCRIPT_SYMBOL(script_ptr[i][0]))
arg[arg_cnt++] = ' ';
}
if(arg_cnt > 0 && arg[arg_cnt-1] == ' ') {
arg[arg_cnt-1] = '\0';
ptr[ptr_cnt++] = &arg[arg_cnt];
} else {
arg[arg_cnt++] = '\0';
ptr[ptr_cnt++] = &arg[arg_cnt];
}
break;
}
}
/* skip the comma at top level */
if(script_ptr[i][0] == ',' && !bracket_level && !subexpr_level && !(flag & FLAG_PARSE_KEEP_COMMA))
continue;
/* write the argument */
len = (int) strlen(script_ptr[i]);
for (j = 0; j < len; j++)
arg[arg_cnt++] = script_ptr[i][j];
/* don't add space after athena symbol prefixes */
if (!SCRIPT_SYMBOL(script_ptr[i][0]) && script_ptr[i][0] != '=')
arg[arg_cnt++] = ' ';
}
block->arg_cnt = arg_cnt;
block->ptr_cnt = ptr_cnt;
*position = i;
return SCRIPT_PASSED;
}
int script_translate(script_t * script) {
int status = 0;
int flag = 0;
rbt_logic * logic = NULL;
node * node = NULL;
block_r * iter = NULL;
/* check null paramaters */
if( exit_zero(1, script) ||
is_nil(script->blocks) )
return 1;
/* translate each block */
iter = script->blocks->next;
do {
/* child block inherit logic tree from parent block */
if(iter->link != NULL && iter->link->logic != NULL)
if(rbt_logic_copy(&iter->logic, iter->link->logic))
return exit_stop("failed to copy parent logic tree");
/* translate each block using a specific handle;
* block numbers are define in the block_db.txt;
* for example, 0 = bonus, 1 = bonus2, etc. */
switch(iter->type) {
case 0: status = translate_bonus(iter, "bonus"); break; /* bonus */
case 1: status = translate_bonus(iter, "bonus2"); break; /* bonus2 */
case 2: status = translate_bonus(iter, "bonus3"); break; /* bonus3 */
case 3: status = translate_bonus(iter, "bonus4"); break; /* bonus4 */
case 4: status = translate_bonus(iter, "bonus5"); break; /* bonus5 */
case 5: status = translate_autobonus(iter, 0x01); break; /* autobonus */
case 6: status = translate_autobonus(iter, 0x02); break; /* autobonus2 */
case 7: status = translate_autobonus(iter, 0x04); break; /* autobonus3 */
case 8: status = translate_heal(iter); break; /* heal */
case 9: status = translate_heal(iter); break; /* percentheal */
case 10: status = translate_heal(iter); break; /* itemheal */
case 11: status = translate_skill(iter); break; /* skill */
case 12: status = translate_itemskill(iter); break; /* itemskill */
case 13: status = translate_skill_block(iter); break; /* unitskilluseid */
case 14: status = translate_status(iter); break; /* sc_start */
case 15: status = translate_status(iter); break; /* sc_start4 */
case 16: status = translate_status_end(iter); break; /* sc_end */
case 17: status = translate_getitem(iter); break; /* getitem */
case 18: status = translate_rentitem(iter); break; /* rentitem */
case 19: status = translate_delitem(iter); break; /* delitem */
case 20: status = translate_getrandgroupitem(iter); break; /* getrandgroupitem */
case 23: status = block_stack_push(iter, TYPE_ENG, "Set new font."); break; /* setfont */
case 24: status = translate_buyingstore(iter); break; /* buyingstore */
case 25: status = translate_searchstore(iter); break; /* searchstores */
case 30: status = block_stack_push(iter, TYPE_ENG, "Send a message through the announcement system."); break; /* announce */
case 31: status = translate_callfunc(iter); break; /* callfunc */
case 33: status = translate_warp(iter); break; /* warp */
case 35: status = block_stack_push(iter, TYPE_ENG, "Hatch a pet egg."); break; /* bpet */
case 34: status = translate_pet_egg(iter); break; /* pet */
case 36: status = translate_hire_mercenary(iter); break; /* mercenary_create */
case 37: status = translate_heal(iter); break; /* mercenary_heal */
case 38: status = translate_status(iter); break; /* mercenary_sc_status */
case 39: status = translate_produce(iter, iter->type); break; /* produce */
case 40: status = translate_produce(iter, iter->type); break; /* cooking */
case 41: status = translate_makerune(iter); break; /* makerune */
case 42: status = translate_getguildexp(iter); break; /* getguildexp */
case 43: status = translate_getexp(iter); break; /* getexp */
case 44: status = translate_monster(iter); break; /* monster */
case 45: status = block_stack_push(iter, TYPE_ENG, "Evolve homunculus when requirements are met."); break; /* homevolution */
case 46: status = block_stack_push(iter, TYPE_ENG, "Change to Summer Outfit when worn."); break; /* setoption */
case 47: status = block_stack_push(iter, TYPE_ENG, "Summon a creature to mount. [Work for all classes]."); break; /* setmounting */
case 48: status = translate_setfalcon(iter); break; /* setfalcon */
case 49: status = translate_getgroupitem(iter); break; /* getgroupitem */
case 50: status = block_stack_push(iter, TYPE_ENG, "Reset all status points."); break; /* resetstatus */
case 51: status = translate_bonus_script(iter); break; /* bonus_script */
case 52: status = block_stack_push(iter, TYPE_ENG, "Play another background song."); break; /* playbgm */
case 53: status = translate_transform(iter); break; /* transform */
case 54: status = translate_status(iter); break; /* sc_start2 */
case 55: status = translate_petloot(iter); break; /* petloot */
case 56: status = translate_petrecovery(iter); break; /* petrecovery */
case 57: status = translate_petskillbonus(iter); break; /* petskillbonus */
case 58: status = translate_petskillattack(iter); break; /* petskillattack */
case 59: status = translate_petskillattack2(iter); break; /* petskillattack2 */
case 60: status = translate_petskillsupport(iter); break; /* petskillsupport */
case 61: status = translate_petheal(iter); break; /* petheal */
default: status = 1; break;
case 62:
case 21: /* skilleffect */
case 22: /* specialeffect2 */
case 29: /* input */
case 32: /* end */
case 63: /* getmapxy */
case 64: /* specialeffect */
case 65: status = 0; break;
case 26: /* if */
/* if block's logical experssion is in index 0 */
flag = EVALUATE_FLAG_KEEP_LOGIC_TREE | EVALUATE_FLAG_EXPR_BOOL;
node = evaluate_expression(iter, iter->ptr[0], flag);
if(is_nil(node))
status = exit_mesg("failed to evaluate if block's expression '%s'", iter->ptr[0]);
node_free(node);
break;
case 27: /* else */
/* else block must invert the logic tree of the if block */
if(is_nil(iter->logic)) {
return exit_stop("else block's parent if block has an invalid logic tree");
} else {
if(rbt_logic_not_all(iter->logic, &logic))
return exit_stop("failed to invert else block's parent if block's logic tree");
rbt_logic_deit(&iter->logic);
iter->logic = logic;
}
/* else-if block's logical expression is in index 0 */
if(iter->ptr_cnt > 1) {
flag = EVALUATE_FLAG_KEEP_LOGIC_TREE | EVALUATE_FLAG_EXPR_BOOL;
node = evaluate_expression(iter, iter->ptr[0], flag);
if(is_nil(node))
return exit_mesg("failed to evaluate else-if block's expression '%s'", iter->ptr[0]);
node_free(node);
}
break;
case 28: /* set */
/* set block's expression is evaluated and substituted into
* any expression that references the set block by name */
iter->set_node = evaluate_expression(iter, iter->ptr[1], EVALUATE_FLAG_ALL);
if(is_nil(iter->set_node))
return exit_mesg("failed evaluate set block's expression '%s'", iter->ptr[1]);
break;
}
/* failed to translate the block */
if(status)
return 1;
iter = iter->next;
} while(iter != script->blocks->next && !iter->free);
return 0;
}
int script_generate(script_t * script) {
int top = 0;
block_r * iter = NULL;
exit_null_safe(1, script);
script->offset = 0;
script->buffer[0] = '\0';
if (NULL == script->blocks)
return CHECK_FAILED;
iter = script->blocks->next;
do {
switch(iter->type) {
case 26: /* if */
case 27: /* else */
if(is_nil(iter->logic))
return exit_mesg("missing logic tree fo"
"r if block in item %d", iter->item_id);
if(script_generate_or(iter, iter->logic))
return exit_mesg("failed to interpret logic expres"
"sion '%s' in item %d", iter->ptr[0], iter->item_id);
break;
case 28: /* set */
/* special cases for script engine
* variables and special variables */
break;
case 21: /* skilleffect */
case 22: /* specialeffect2 */
case 29: /* input */
case 32: /* end */
case 63: /* getmapxy */
case 64: /* specialeffect */
case 65: /* showscript */
/* do nothing */
break;
default:
top = iter->eng_cnt - 1;
if(NULL == iter->eng[top])
return exit_func_safe("missing item description in item %d", iter->item_id);
script->offset += sprintf(&script->buffer[script->offset], "%s\n", iter->eng[top]);
}
iter = iter->next;
} while (iter != script->blocks->next && !iter->free);
return SCRIPT_PASSED;
}
int script_combo(script_t * script) {
combo_t * combo = NULL;
combo_t * iter = NULL;
char * buffer = NULL;
/* only supported on rathena */
if(script->mode != RATHENA)
return 0;
if(item_combo_id(script->db, &combo, script->item.id)) {
return 0; /* no combo wombo jumbo */
} else {
iter = combo;
while(is_ptr(iter)) {
script->offset += sprintf(&script->buffer[script->offset], "%s\n", iter->group);
if(script_recursive(script->db, script->mode, script->map, iter->script, script->item.id, &buffer))
break;
script->offset += sprintf(&script->buffer[script->offset], "%s", buffer);
free_ptr(buffer);
iter = iter->next;
}
item_combo_free(&combo);
}
return 0;
}
int script_recursive(db_t * db, int mode, lua_State * map, char * subscript, int item_id, char ** value) {
int status = 0;
script_t * script = NULL;
if(script_check(subscript)) {
*value = convert_string("Empty script.\n");
if(is_nil(*value))
status = exit_stop("out of memory");
} else if(calloc_ptr(script)) {
status = exit_stop("out of memory");
} else {
script->db = db;
script->mode = mode;
script->map = map;
script->item.id = item_id;
if( script_lexical(&script->token, subscript) ||
script_analysis(script, &script->token, NULL, NULL) ||
script_translate(script) ||
script_generate(script) ) {
status = exit_mesg("failed to evaluate sub script '%s'", subscript);
} else {
*value = convert_string(script->buffer);
if(is_nil(*value))
status = exit_stop("out of memory");
}
script_block_release(script);
node_release(script);
free_ptr(script);
}
return status;
}
/* interpret the function call syntax by parsing each argument that
* is delimited by a comma and writing each argument's expression
* onto the block's ptr stack
*
* if the expression is "(0, 1)" then
* block->ptr[n + 0] = "0"
* block->ptr[n + 1] = "1"
* *argc = 2;
* i.e. the arguments will be pushed onto the block->ptr stack
* and the number of arguments pushed will be returned by argc
*/
int stack_ptr_call(block_r * block, char * expr, int * argc) {
int status = 0;
int len = 0;
token_r * token = NULL;
len = (int) strlen(expr);
if( 0 >= len || /* check empty expression */
(expr[0] != '(' && expr[len] != ')') || /* check empty argument */
calloc_ptr(token) )
return 1;
if( script_lexical(token, expr) ||
0 >= token->script_cnt ||
stack_ptr_call_(block, token, argc) )
status = exit_mesg("failed to parse call syntax '%s'", expr);
free_ptr(token);
return status;
}
int stack_ptr_call_(block_r * block, token_r * token, int * argc) {
int pos = 0;
int top = 0;
/* remove the ending parenthesis */
token->script_cnt--;
/* prior to calling script_parse, the block->ptr stack must be
* initialized by setting the top of block->ptr to the point to
* the end of the block->arg buffer */
top = block->ptr_cnt;
block->ptr[top] = &block->arg[block->arg_cnt];
block->ptr_cnt++;
/* script_parse will push the arguments to the block->ptr stack
* but script_parse just works but totally full of problems =.=
* bandaids must be used. */
if(script_parse(token, &pos, block, ',', '\0', FLAG_PARSE_NORMAL))
return 1;
/* script_parse will not set the NULL for the lasted argument */
block->arg[block->arg_cnt - 1] = '\0';
/* number of arguments pushed onto block->ptr can be calculated
* by taking the difference of the previous and current stack
* size. */
*argc = block->ptr_cnt - top;
return 0;
}
/* evaluate the expression for a single or multiple item id
* and convert each item id to item name and write the item
* name onto the block->eng stack
*
* if the expression is "1101" then
* block->eng[n + 0] = "sword"
* *argc = 1;
*
* if the expression is "1101 + 2" then
* block->eng[n + 0] = "sword" (1101)
* block->eng[n + 1] = "sword" (1102)
* block->eng[n + 2] = "sword" (1103)
*
* but to prevent stack overflow on the block->eng stack;
* if more than x item names, then the list will cut off.
*/
struct work {
block_r * block;
void * search;
int count;
int total;
int flag;
};
static int stack_eng_item_work(struct rbt_node * node, void * context, int flag) {
int i;
struct range * range = node->val;
struct work * work = context;
item_t * item = work->search;
for(i = range->min; i <= range->max && work->count < work->total; i++, work->count++)
if( item_id(work->block->script->db, item, i) ||
block_stack_push(work->block, TYPE_ENG, item->name))
return exit_mesg("failed to find item id %"
"d in item id %d", i, work->block->item_id);
return 0;
}
int stack_eng_item(block_r * block, char * expr, int * argc, int flag) {
int status = 0;
int len = 0;
int top = 0;
item_t * item = NULL;
node * node = NULL;
struct work work;
/* track stack argument count */
top = block->eng_cnt;
/* check for empty expression */
len = (int) strlen(expr);
if(0 >= len || calloc_ptr(item))
return 1;
/* handle item name expressions */
if((isalpha(expr[0]) || '_' == expr[0]) &&
!item_name(block->script->db, item, expr, len)) {
if(block_stack_push(block, TYPE_ENG, item->name))
status = exit_stop("failed to push item name onto the stack");
} else {
/* handle item id expressions */
node = evaluate_expression(block, expr, 0);
if(is_nil(node)) {
status = exit_mesg("failed to evaluate item id expression '%s'", expr);
} else {
/* getitem's item id expression may be a function call,
* which returns either a item group name or item name. */
if(node->return_type & ITEM_TYPE_FLAG && node->formula) {
status = (flag & FLAG_GETITEM) ?
block_stack_vararg(block, TYPE_ENG, "items from %s", node->formula):
block_stack_vararg(block, TYPE_ENG, "%s", node->formula);
} else {
work.block = block;
work.search = item;
work.count = 0;
work.total = MAX_STR_LIST;
status = rbt_range_work(node->value, stack_eng_item_work, &work);
}
}
}
*argc = block->eng_cnt - top;
free_ptr(item);
node_free(node);
return status;
}
/* stack_eng_skill allows the same pattern as stack_eng_item */
static int stack_eng_skill_work(struct rbt_node * node, void * context, int flag) {
int i;
struct range * range = node->val;
struct work * work = context;
skill_t * skill = work->search;
for(i = range->min; i <= range->max && work->count < work->total; i++, work->count++)
if( skill_id(work->block->script->db, skill, i) ||
block_stack_push(work->block, TYPE_ENG, skill->desc))
return exit_mesg("failed to find skill id %"
"d in item id %d", i, work->block->item_id);
return 0;
}
int stack_eng_skill(block_r * block, char * expr, int * argc) {
int status = 0;
int len = 0;
int top = 0;
skill_t * skill = NULL;
node * node = NULL;
struct work work;
/* track stack argument count */
top = block->eng_cnt;
/* check for empty expression */
len = (int) strlen(expr);
if(0 >= len || calloc_ptr(skill))
return 1;
/* handle skill name expressions */
if((isalpha(expr[0]) || '_' == expr[0]) &&
!skill_name(block->script->db, skill, expr, len)) {
if(block_stack_push(block, TYPE_ENG, skill->desc))
status = exit_stop("failed to push skill name onto the stack");
} else {
/* handle skill id expressions */
node = evaluate_expression(block, expr, 0);
if(is_nil(node)) {
status = exit_mesg("failed to evaluate skill id expression '%s'", expr);
} else {
work.block = block;
work.search = skill;
work.count = 0;
work.total = MAX_STR_LIST;
status = rbt_range_work(node->value, stack_eng_skill_work, &work);
}
}
*argc = block->eng_cnt - top;
free_ptr(skill);
node_free(node);
return status;
}
int stack_eng_grid(block_r * block, char * expr) {
int status = 0;
node * node = NULL;
node = evaluate_expression(block, expr, 0);
if(is_nil(node))
return exit_mesg("failed to evaluate grid expression '%s'", expr);
/* calculate the grid */
node->min = node->min * 2 + 1;
node->max = node->max * 2 + 1;
status = (node->min == node->max) ?
block_stack_vararg(block, TYPE_ENG, "%d x %d", node->min, node->min):
block_stack_vararg(block, TYPE_ENG, "%d x %d ~ %d x %d", node->min, node->min, node->max, node->max);
if(status)
exit_stop("failed to push grid onto the stack");
node_free(node);
return status;
}
int stack_eng_coordinate(block_r * block, char * expr) {
int status = 0;
node * node = NULL;
node = evaluate_expression(block, expr, 0);
if(is_nil(node))
return exit_mesg("failed to evaluate coordinate expression '%s'", expr);
switch(node->min) {
case COORD_RANDOM: status = block_stack_push(block, TYPE_ENG, "random"); break;
case COORD_CURRENT: status = block_stack_push(block, TYPE_ENG, "current"); break;
default: status = stack_eng_int(block, expr, 1, 0); break;
}
if(status)
exit_stop("failed to push coordinate onto the stack");
node_free(node);
return status;
}
/* evaluate the expression and write the integer range
* onto the block->eng stack along with any dependency
*
* if the expression is "getrefine() + 10" then
* block->eng[n + 0] = "10 ~ 25 (refine rate)"
*/
static int stack_eng_int_re(block_r * block, node * node, int modifier, int flag) {
int i = 0;
int status = 0;
double min;
double max;
char fmt[32];
char buf[64];
int off = 0;
if(0 == modifier)
return exit_stop("division by zero modifier");
/* evaluate integer expression */
min = ((double) node->min) / modifier;
max = ((double) node->max) / modifier;
/* build the conversion specifier;
* %[+](.2f|d)[%%] ~ %[+](.2f|d)[%%] */
if( (node->min != 0 && node->min / modifier == 0) ||
(node->max != 0 && node->max / modifier == 0) ) {
flag |= FORMAT_FLOAT;
} else {
node->min /= modifier;
node->max /= modifier;
}
for(i = 0; i < 2; i++) {
fmt[off++] = '%';
if(flag & FORMAT_PLUS)
fmt[off++] = '+';
if(flag & FORMAT_FLOAT) {
fmt[off++] = '.';
fmt[off++] = '2';
fmt[off++] = 'f';
} else {
fmt[off++] = 'd';
}
if(flag & FORMAT_RATIO) {
fmt[off++] = '%';
fmt[off++] = '%';
}
/* one %[+](.2f|d)[%%] required don't
* compare equality using doubles o.o */
if (node->min == node->max)
break;
if(i == 0) {
fmt[off++] = ' ';
fmt[off++] = '~';
fmt[off++] = ' ';
}
}
fmt[off++] = '\0';
/* write buffer with formula */
if(node->min != node->max) {
status = flag & FORMAT_FLOAT ?
!sprintf(buf, fmt, min, max) :
!sprintf(buf, fmt, node->min, node->max);
} else {
status = flag & FORMAT_FLOAT ?
!sprintf(buf, fmt, max) :
!sprintf(buf, fmt, node->max);
}
if (status || stack_aux_formula(block, node, buf) )
status = exit_stop("failed to write integer expression");
return status;
}
int stack_eng_int(block_r * block, char * expr, int modifier, int flag) {
int status = 0;
node * node;
node = evaluate_expression(block, expr, 0);
if(is_nil(node)) {
status = exit_mesg("failed to evaluate integer expression '%s'", expr);
} else if(stack_eng_int_re(block, node, modifier, flag)) {
status = exit_stop("failed to write integer expression");
}
node_free(node);
return status;
}
/* evaluate an expression and select the prefix
* depending on whether the result is positive,
* negative, or both.
*
* if the expression is "10 - getrefine()" then
* block->eng[n + 0] = "positive 0 ~ 10 or"
* "negative 0 ~ 5"
* (since 10 - getrefine() = -5 to 10)
* *argc = 1;
*
* if the expression is "10 " then
* block->eng[n + 0] = "positive 10"
* *argc = 1;
*
* if the expression is "-10 " then
* block->eng[n + 0] = "negative 10"
* *argc = 1;
*/
static int stack_eng_int_signed_re(block_r * block, node * node, int modifier, const char * pos, const char * neg, int flag) {
int status = 0;
double min;
double max;
char cnv[16];
int off = 0;
int len = 0;
char * buf = NULL;
char fmt[64];
if(0 == modifier)
return exit_stop("division by zero modifier");
/* evaluate integer expression */
min = ((double) node->min) / modifier;
max = ((double) node->max) / modifier;
/* build the conversion specifier */
if( (node->min != 0 && node->min / modifier == 0) ||
(node->max != 0 && node->max / modifier == 0) ) {
flag |= FORMAT_FLOAT;
} else {
node->min /= modifier;
node->max /= modifier;
}
cnv[off++] = '%';
if(flag & FORMAT_PLUS)
cnv[off++] = '+';
if(flag & FORMAT_FLOAT) {
cnv[off++] = '.';
cnv[off++] = '2';
cnv[off++] = 'f';
} else {
cnv[off++] = 'd';
}
if(flag & FORMAT_RATIO) {
cnv[off++] = '%';
cnv[off++] = '%';
}
cnv[off++] = '\0';
/* build buffer */
len = (int) strlen(pos) + (int) strlen(neg) + 128;
buf = calloc(len, sizeof(char));
if(is_nil(buf)) {
status = exit_stop("out of memory");
} else {
if(node->min == node->max) {
status = !sprintf(fmt, "%%s %s", cnv) ||
(flag & FORMAT_FLOAT) ?
!sprintf(buf, fmt, (node->max >= 0) ? pos : neg, max) :
!sprintf(buf, fmt, (node->max >= 0) ? pos : neg, node->max);
} else if(node->min >= 0 && node->max >= 0) {
/* positive */
status = (!sprintf(fmt, "%%s %s ~ %s", cnv, cnv) ||
(flag & FORMAT_FLOAT) ?
!sprintf(buf, fmt, pos, min, max) :
!sprintf(buf, fmt, pos, node->min, node->max) );
} else if(node->min < 0 && node->max >= 0) {
/* negative - positive */
status = (!sprintf(fmt, "%%s 0 ~ %s or %%s 0 ~ %s", cnv, cnv) ||
(flag & FORMAT_FLOAT) ?
!sprintf(buf, fmt, neg, fabs(min), pos, max) :
!sprintf(buf, fmt, neg, abs(node->min), pos, node->max) );
} else if(node->min >= 0 && node->max < 0) {
/* positive - negative */
status = (!sprintf(fmt, "%%s 0 ~ %s or %%s 0 ~ %s", cnv, cnv) ||
(flag & FORMAT_FLOAT) ?
!sprintf(buf, fmt, neg, fabs(max), pos, min) :
!sprintf(buf, fmt, neg, abs(node->max), pos, node->min) );
} else {
/* negative */
status = (!sprintf(fmt, "%%s %s ~ %s", cnv, cnv) ||
(flag & FORMAT_FLOAT) ?
!sprintf(buf, fmt, neg, fabs(min), fabs(max)) :
!sprintf(buf, fmt, neg, abs(node->min), abs(node->max)) );
}
if (status ||
(flag & FORMAT_NO_FORMULA) ?
block_stack_push(block, TYPE_ENG, buf) :
stack_aux_formula(block, node, buf) )
status = exit_stop("failed to write integer expression");
}
free_ptr(buf);
return status;
}
int stack_eng_int_signed(block_r * block, char * expr, int modifier, const char * pos, const char * neg, int flag) {
int status = 0;
node * node;
node = evaluate_expression(block, expr, 0);
if(is_nil(node)) {
status = exit_mesg("failed to evaluate integer expression '%s'", expr);
} else if(stack_eng_int_signed_re(block, node, modifier, pos, neg, flag)) {
status = exit_stop("failed write integer expression");
}
node_free(node);
return status;
}
/* evaluate the expression for a single or multiple item id
* and convert each item id to item name and write the item
* name onto the block->eng stack
*
* if the expression is "1101" then
* block->eng[n + 0] = "sword"
* *argc = 1;
*/
int stack_eng_time(block_r * block, char * expr, int modifier) {
int status = 0;
node * node = NULL;
int tick_min = 0;
int tick_max = 0;
char * time_suffix = NULL;
int time_unit = 0;
/* maximum 32-bit integer require up to 11 characters plus -
* enough padding room for additional suffix and formatting
* characters. */
int len = 0;
char buf[64];
/* evaluate the expression and convert to time string */
node = evaluate_expression(block, expr, 0);
if(is_nil(node))
return exit_mesg("failed to evaluate time expression '%s'", expr);
/* get the minimum and maximum of the time expression */
tick_min = node->min;
tick_max = node->max;
/* get the closest time metric that can divide the total number of milliseconds */
if (tick_min / 86400000 != 0) {
time_suffix = "day";
time_unit = 86400000;
}
else if (tick_min / 3600000 != 0) {
time_suffix = "hour";
time_unit = 3600000;
}
else if (tick_min / 60000 != 0) {
time_suffix = "minute";
time_unit = 60000;
} else if (tick_min / 1000 != 0) {
time_suffix = "second";
time_unit = 1000;
} else {
time_suffix = "millisecond";
time_unit = 1;
}
/* convert millisecond to time metric */
tick_min /= time_unit;
tick_max /= time_unit;
/* write time string to buffer */
len = (tick_min == tick_max) ?
sprintf(buf, "%d %s%s", tick_min, time_suffix, tick_min > 1 ? "s" : ""):
sprintf(buf, "%d ~ %d %s%s", tick_min, tick_max, time_suffix, tick_max > 1 ? "s" : "");
/* check overflow but sprintf can crash in sprintf */
if(len > 64 || stack_aux_formula(block, node, buf))
status = exit_stop("failed to write time expression");
node_free(node);
return status;
}
/* evaluate the expression into an item level constant
* which used to search for all item recipes with the
* same item level.
*
* if the expression is "21" then
* (depend on the produce table)
* block->eng[n + 1] = Recipe for Flame Heart
* block->eng[n + 2] = 10 Red Blood
* block->eng[n + 3] = Recipe for Mystic Frozen
* block->eng[n + 4] = 10 Crystal Blue
* block->eng[n + 5] = Recipe for Rough Wind
* block->eng[n + 6] = 10 Wind of Verdure
* block->eng[n + 7] = Recipe for Great Nature
* block->eng[n + 8] = 10 Green Live
* block->eng[n + 9] = Recipe for Iron
* block->eng[n + 10] = 1 Iron Ore
* block->eng[n + 11] = Recipe for Steel
* block->eng[n + 12] = 5 Iron
* block->eng[n + 13] = 1 Coal
* block->eng[n + 14] = Recipe for Star Crumb
* block->eng[n + 15] = 10 Star Dust
* *argc = 16;
*/
int stack_eng_produce(block_r * block, char * expr, int * argc) {
int i;
int status = 0;
int top = 0;
int lvl = 0;
item_t * item = NULL;
produce_t * list = NULL;
produce_t * iter = NULL;
char buf[MAX_NAME_SIZE + 32];
if(calloc_ptr(item))
return exit_stop("out of memory");
/* track stack argument count */
top = block->eng_cnt;
if(evaluate_numeric_constant(block, expr, &lvl)) {
status = exit_mesg("failed to evaluate level expression into a constant '%s'", expr);
} else if(produce_id(block->script->db, &list, lvl)) {
status = exit_mesg("failed to find produce entries for item level %d", lvl);
} else {
/* write produce header */
switch(lvl) {
case 1: status = block_stack_push(block, TYPE_ENG, "Use to craft level 1 weapons."); break; /* lv1 weapons */
case 2: status = block_stack_push(block, TYPE_ENG, "Use to craft level 2 weapons."); break; /* lv2 weapons */
case 3: status = block_stack_push(block, TYPE_ENG, "Use to craft level 3 weapons."); break; /* lv3 weapons */
case 11: status = block_stack_push(block, TYPE_ENG, "Use to cook recipes with rank 5 success rate."); break; /* cooking sets */
case 12: status = block_stack_push(block, TYPE_ENG, "Use to cook recipes with rank 4 success rate."); break;
case 13: status = block_stack_push(block, TYPE_ENG, "Use to cook recipes with rank 3 success rate."); break;
case 14: status = block_stack_push(block, TYPE_ENG, "Use to cook recipes with rank 2 success rate."); break;
case 15: status = block_stack_push(block, TYPE_ENG, "Use to cook recipes with rank 1 success rate."); break;
case 21: status = block_stack_push(block, TYPE_ENG, "Use to manufacture metals."); break; /* metals */
case 24: break;
default: status = exit_mesg("unsupported item level %d in item %d", lvl, block->item_id);
}
if(status) {
exit_stop("failed to write produce header");
} else {
/* write the produce recipes */
iter = list;
while(iter && !status) {
if(item_id(block->script->db, item, iter->item_id)) {
status = exit_mesg("failed to find item id %d", iter->item_id);
/* write the produce item name */
} else if(!sprintf(buf, "Recipe for %s", item->name) || block_stack_push(block, TYPE_ENG, buf)) {
status = exit_stop("failed to write item name");
/* write the produce ingredient item name */
} else {
for(i = 0; i < iter->ingredient_count && !status; i++) {
if( item_id(block->script->db, item, iter->item_id_req[i]) ||
!sprintf(buf, " * %d %s", iter->item_amount_req[i], item->name) ||
block_stack_push(block, TYPE_ENG, buf) )
status = exit_mesg("failed to find item id %d or write item name", iter->item_id);
}
}
iter = iter->next;
}
}
}
*argc = block->eng_cnt - top;
produce_free(&list);
free_ptr(item);
return status;
}
/* evaluate the expression into an id
* and use the id as key for the maps
* (see athena_db.txt for all maps)
*
* if the expression is "1" and flag MAP_AMMO_FLAG then
* block->eng[n + 1] = ammo_type[i]
* *argc = 1;
*/
static int stack_eng_map_work(struct rbt_node * node, void * context, int flag) {
int i;
struct range * range = node->val;
struct work * work = context;
char * value = NULL;
for(i = range->min; i <= range->max && work->count < work->total; i++, work->count++) {
if( (work->flag & MAP_AMMO_FLAG && !script_map_id(work->block, "ammo_type", i, &value)) ||
(work->flag & MAP_CAST_FLAG && !script_map_id(work->block, "cast_flag", i, &value)) ||
(work->flag & MAP_CLASS_FLAG && !script_map_id(work->block, "class_type", i, &value)) ||
(work->flag & MAP_EFFECT_FLAG && !script_map_id(work->block, "effect_type", i, &value)) ||
(work->flag & MAP_ELEMENT_FLAG && !script_map_id(work->block, "element_type", i, &value)) ||
(work->flag & MAP_LOCATION_FLAG && !script_map_id(work->block, "item_location", i, &value)) ||
(work->flag & MAP_ITEM_FLAG && !script_map_id(work->block, "item_type", i, &value)) ||
(work->flag & MAP_JOB_FLAG && !script_map_id(work->block, "job_type", i, &value)) ||
(work->flag & MAP_RACE_FLAG && !script_map_id(work->block, "race_type", i, &value)) ||
(work->flag & MAP_READPARAM_FLAG && !script_map_id(work->block, "readparam", i, &value)) ||
(work->flag & MAP_REGEN_FLAG && !script_map_id(work->block, "regen_flag", i, &value)) ||
(work->flag & MAP_SEARCHSTORE_FLAG && !script_map_id(work->block, "search_store", i, &value)) ||
(work->flag & MAP_SIZE_FLAG && !script_map_id(work->block, "size_type", i, &value)) ||
(work->flag & MAP_SP_FLAG && !script_map_id(work->block, "sp_flag", i, &value)) ||
(work->flag & MAP_TARGET_FLAG && !script_map_id(work->block, "target_flag", i, &value)) ||
(work->flag & MAP_WEAPON_FLAG && !script_map_id(work->block, "weapon_type", i, &value)) ||
(work->flag & MAP_REFINE_FLAG && !script_map_id(work->block, "refine_location", i, &value)) ||
(work->flag & MAP_ITEM_INFO_FLAG && !script_map_id(work->block, "item_info", i, &value)) ||
(work->flag & MAP_TIME_FLAG && !script_map_id(work->block, "time_type", i, &value)) ||
(work->flag & MAP_STRCHARINFO_FLAG && !script_map_id(work->block, "strcharinfo", i, &value)) ||
(work->flag & MAP_STATUSEFFECT_FLAG && !script_map_id(work->block, "status_effect", i, &value)) ) {
if(block_stack_push(work->block, TYPE_ENG, value)) {
free_ptr(value);
return 1;
}
} else {
return 1;
}
free_ptr(value);
}
return 0;
}
int stack_eng_map(block_r * block, char * expr, int flag, int * argc) {
int status = 0;
int top = 0;
node * node = NULL;
struct work work;
if(0 == flag)
return 1;
/* track stack argument count */
top = block->eng_cnt;
node = evaluate_expression(block, expr, 0);
if(is_nil(node)) {
status = exit_mesg("failed to evaluate map id expression '%s'", expr);
} else {
work.block = block;
work.count = 0;
work.total = MAX_STR_LIST;
work.flag = flag;
if(rbt_range_work(node->value, stack_eng_map_work, &work))
status = !(flag & MAP_NO_ERROR) ?
exit_mesg("failed to write map values for '%s' on"
" flag %d in item %d", expr, flag, block->item_id) : 1;
}
*argc = block->eng_cnt - top;
node_free(node);
return status;
}
/* evaluate the expression into an id
* and use the id as key for the data
* base to get the name
*
* if the expression is "1101" and flag DB_ITEM_ID then
* block->eng[n + 1] = "sword"
* *argc = 1;
*/
static int stack_eng_db_work(struct rbt_node * node, void * context, int flag) {
int i;
struct range * range = node->val;
struct work * work = context;
int status = 0;
skill_t * skill = NULL;
item_t * item = NULL;
mob_t * mob = NULL;
merc_t * merc = NULL;
pet_t * pet = NULL;
map_res * map = NULL;
if(work->flag & DB_SKILL_ID)
calloc_ptr(skill);
if(work->flag & DB_ITEM_ID)
calloc_ptr(item);
if(work->flag & DB_MOB_ID)
calloc_ptr(mob);
if(work->flag & DB_MERC_ID)
calloc_ptr(merc);
if(work->flag & DB_PET_ID)
calloc_ptr(pet);
if(work->flag & DB_MAP_ID)
calloc_ptr(map);
for(i = range->min; i <= range->max && work->count < work->total && !status; i++, work->count++) {
if(work->flag & DB_SKILL_ID) {
if( is_nil(skill) ||
skill_id(work->block->script->db, skill, i) ||
block_stack_push(work->block, TYPE_ENG, skill->desc) )
status = (DB_NO_ERROR & work->flag) ? 1 : exit_mesg("failed to write or search for skill id %d", i);
} else if(work->flag & DB_ITEM_ID) {
if( is_nil(item) ||
item_id(work->block->script->db, item, i) ||
block_stack_push(work->block, TYPE_ENG, item->name) )
status = (DB_NO_ERROR & work->flag) ? 1 : exit_mesg("failed to write or search for item id %d", i);
} else if(work->flag & DB_MOB_ID) {
/* negative map id is special map */
switch(i) {
case -1: status = block_stack_push(work->block, TYPE_ENG, "random monster"); break;
case -2: status = block_stack_push(work->block, TYPE_ENG, "random poring monster"); break;
case -3: status = block_stack_push(work->block, TYPE_ENG, "random mvp monster"); break;
case -4: status = block_stack_push(work->block, TYPE_ENG, "random monster"); break;
default: status = is_nil(mob) ||
mob_id(work->block->script->db, mob, i) ||
block_stack_push(work->block, TYPE_ENG, mob->name);
}
if(status)
(DB_NO_ERROR & work->flag) ? 1 : exit_mesg("failed to write or search for mob id %d", i);
} else if(work->flag & DB_MERC_ID) {
if( is_nil(merc) ||
merc_id(work->block->script->db, merc, i) ||
block_stack_push(work->block, TYPE_ENG, merc->name) )
status = (DB_NO_ERROR & work->flag) ? 1 : exit_mesg("failed to write or search for mercenary id %d", i);
} else if(work->flag & DB_PET_ID) {
if( is_nil(pet) ||
pet_id(work->block->script->db, pet, i) ||
block_stack_push(work->block, TYPE_ENG, pet->name) )
status = (DB_NO_ERROR & work->flag) ? 1 : exit_mesg("failed to write or search for pet id %d", i);
} else if(work->flag & DB_MAP_ID) {
if( is_nil(map) ||
map_id(work->block->script->db, map, i) ||
block_stack_push(work->block, TYPE_ENG, map->name) )
status = (DB_NO_ERROR & work->flag) ? 1 : exit_mesg("failed to write or search for mob id %d", i);
}
}
free_ptr(skill);
free_ptr(item);
free_ptr(mob);
free_ptr(merc);
free_ptr(pet);
free_ptr(map);
return status;
}
int stack_eng_db(block_r * block, char * expr, int flag, int * argc) {
int status = 0;
int top = 0;
node * node = NULL;
struct work work;
if(0 == flag)
return 1;
/* track stack argument count */
top = block->eng_cnt;
node = evaluate_expression(block, expr, 0);
if(is_nil(node)) {
status = exit_mesg("failed to evaluate id expression '%s'", expr);
} else {
work.block = block;
work.count = 0;
work.total = MAX_STR_LIST;
work.flag = flag;
if(rbt_range_work(node->value, stack_eng_db_work, &work))
status = (DB_NO_ERROR & flag) ? 1 : exit_mesg("failed to resolve db "
"values for '%s' on flag %d in item %d", expr, flag, block->item_id);
}
*argc = block->eng_cnt - top;
node_free(node);
return status;
}
static int stack_eng_group_name(char ** target, const char * source) {
int i = 0;
int j = 0;
int length;
char * buffer;
length = (int) strlen(source);
if(0 >= length)
return 1;
buffer = calloc(length * 3, sizeof(char));
if(is_nil(buffer))
return 1;
/* =.=; i know */
for(i = 3 /* skip IG_ */; i < length; i++) {
/* add spacing on change in casing from upper to lower */
if(i != 3 && i + 1 < length && islower(source[i + 1]) && isupper(source[i])) {
buffer[j++] = ' ';
buffer[j++] = source[i];
} else {
/* word ending with digit; capatialize next character */
if((i + 1 < length && isdigit(source[i]) && !isdigit(source[i + 1])) ||
(i + 1 == length && isdigit(source[i])) ) {
buffer[j++] = ' ';
buffer[j++] = toupper(source[i]);
} else if(source[i] == '_') {
/* skip under scores */
continue;
} else {
/* capitalize first character, lower case remaining */
buffer[j++] = (i == 3) ? toupper(source[i]) : tolower(source[i]);
}
}
}
/* append group */
buffer[j++] = ' ';
snprintf(&buffer[j], length * 3 - j, "group");
*target = buffer;
return 0;
}
int stack_eng_item_group(block_r * block, char * expr) {
int status = 0;
int group_id = 0;
const_t * group_const = NULL;
char * group_name = NULL;
if( evaluate_numeric_constant(block, expr, &group_id) ||
calloc_ptr(group_const) )
return 1;
switch(block->script->mode) {
case EATHENA:
case RATHENA:
if(item_group_name(block->script->db, group_const, group_id)) {
status = exit_mesg("failed to get group name for group id %d", group_id);
} else if(stack_eng_group_name(&group_name, group_const->name)) {
status = exit_mesg("failed to convert %s into a group name", group_const->name);
} else if(block_stack_push(block, TYPE_ENG, group_name)) {
status = exit_stop("failed to write group name onto stack");
}
break;
default: status = exit_mesg("item group is not supported for %d mode", block->script->mode);
}
free_ptr(group_const);
free_ptr(group_name);
return 0;
}
int stack_eng_trigger_bt(block_r * block, char * expr) {
int status = 0;
int off = 0;
int val = 0;
char buf[256];
node * node = NULL;
node = evaluate_expression(block, expr, 0);
if(is_nil(node))
return exit_mesg("failed to evaluate trigger expression '%s'", expr);
if(node->min != node->max) {
status = exit_mesg("trigger expression is not a constant '%s'", expr);
} else {
val = node->min;
/* write using the format:
* trigger on
* [meelee and range]
* [weapon and magic or misc]
* [normal attacks or skills]
*/
off += sprintf(&buf[off], "on ");
/* trigger range (inclusive) */
if(BF_RANGEMASK & val) {
if((BF_LONG | BF_SHORT) & val)
off += sprintf(&buf[off], "meelee and range ");
else if(BF_LONG & val)
off += sprintf(&buf[off], "range ");
else if(BF_SHORT & val)
off += sprintf(&buf[off], "meelee ");
else
status = exit_mesg("unsupported trigger range bit %d", val);
} else {
/* default to meelee and range */
off += sprintf(&buf[off], "meelee and range ");
}
/* trigger type (exclusive?) */
if(BF_WEAPONMASK & val) {
if(BF_WEAPON & val)
off += sprintf(&buf[off], "phyiscal ");
else if((BF_MAGIC | BF_MISC) & val)
off += sprintf(&buf[off], "magical and special ");
else if(BF_MAGIC & val)
off += sprintf(&buf[off], "magical ");
else if(BF_MISC & val)
off += sprintf(&buf[off], "special ");
else
status = exit_mesg("unsupported trigger type bit %d", val);
} else {
/* default to weapon */
off += sprintf(&buf[off], "phyiscal ");
}
/* trigger method (exclusive) */
if(BF_SKILLMASK & val) {
if(BF_SKILL & val)
off += sprintf(&buf[off], "skills");
else if(BF_NORMAL & val)
off += sprintf(&buf[off], "attacks");
else
status = exit_mesg("unsupported trigger method bit %d", val);
} else {
/* default depends on trigger type */
if(BF_MISC & val || BF_MAGIC & val)
off += sprintf(&buf[off], "skills");
else
off += sprintf(&buf[off], "attacks");
}
if(stack_aux_formula(block, node, buf))
status = exit_stop("failed to write trigger expression");
}
node_free(node);
return status;
}
int stack_eng_trigger_atf(block_r * block, char * expr) {
int status = 0;
int off = 0;
int val = 0;
char buf[256];
node * node = NULL;
node = evaluate_expression(block, expr, 0);
if(is_nil(node))
return exit_mesg("failed to evaluate trigger expression '%s'", expr);
if(node->min != node->max) {
status = exit_mesg("trigger expression is not a constant '%s'", expr);
} else {
val = node->min;
/* write using the format:
* trigger on
* [meelee and range]
* [weapon or magic and misc]
* [on self or target ]
*/
off += sprintf(&buf[off], "on ");
/* trigger range (inclusive) */
if((ATF_LONG | ATF_SHORT) & val)
off += sprintf(&buf[off], "meelee and range ");
else if(ATF_LONG & val)
off += sprintf(&buf[off], "range ");
else if(ATF_SHORT & val)
off += sprintf(&buf[off], "meelee ");
else
/* default range is both */
off += sprintf(&buf[off], "meelee and range ");
/* trigger type (exclusive?) */
if(ATF_WEAPON & val)
off += sprintf(&buf[off], "weapon ");
else if((ATF_MAGIC | ATF_MISC) & val)
off += sprintf(&buf[off], "magic and misc ");
else if(ATF_MAGIC & val)
off += sprintf(&buf[off], "magic ");
else if(ATF_MISC & val)
off += sprintf(&buf[off], "misc ");
else
/* default is weapon */
off += sprintf(&buf[off], "weapon ");
/* trigger target */
if(ATF_SELF & val)
off += sprintf(&buf[off], "on self");
else if(ATF_TARGET & val)
off += sprintf(&buf[off], "on target");
else
/* default is target */
off += sprintf(&buf[off], "on target");
if(stack_aux_formula(block, node, buf))
status = exit_stop("failed to write trigger expression");
}
node_free(node);
return status;
}
int stack_eng_options(block_r * block, char * expr) {
int cnt = 0;
int opt = 0;
int flag = TYPE_ENG | FLAG_EMPTY;
if(evaluate_numeric_constant(block, expr, &opt) || 0 >= opt ||
(opt & OPT_SIGHT && block_stack_vararg(block, flag | (cnt++ ? FLAG_CONCAT : 0), "using sight,")) ||
(opt & OPT_HIDE && block_stack_vararg(block, flag | (cnt++ ? FLAG_CONCAT : 0), "using hide,")) ||
(opt & OPT_CLOAK && block_stack_vararg(block, flag | (cnt++ ? FLAG_CONCAT : 0), "using cloak,")) ||
(opt & OPT_CART1 && block_stack_vararg(block, flag | (cnt++ ? FLAG_CONCAT : 0), "equipped cart 1,")) ||
(opt & OPT_FALCON && block_stack_vararg(block, flag | (cnt++ ? FLAG_CONCAT : 0), "falcon following,")) ||
(opt & OPT_PECO && block_stack_vararg(block, flag | (cnt++ ? FLAG_CONCAT : 0), "riding peco,")) ||
(opt & OPT_INVISIBLE && block_stack_vararg(block, flag | (cnt++ ? FLAG_CONCAT : 0), "using invisible,")) ||
(opt & OPT_CART2 && block_stack_vararg(block, flag | (cnt++ ? FLAG_CONCAT : 0), "equipped cart 2,")) ||
(opt & OPT_CART3 && block_stack_vararg(block, flag | (cnt++ ? FLAG_CONCAT : 0), "equipped cart 3,")) ||
(opt & OPT_CART4 && block_stack_vararg(block, flag | (cnt++ ? FLAG_CONCAT : 0), "equipped cart 4,")) ||
(opt & OPT_CART5 && block_stack_vararg(block, flag | (cnt++ ? FLAG_CONCAT : 0), "equipped cart 5,")) ||
(opt & OPT_ORC && block_stack_vararg(block, flag | (cnt++ ? FLAG_CONCAT : 0), "having orc head,")) ||
(opt & OPT_WEDDING && block_stack_vararg(block, flag | (cnt++ ? FLAG_CONCAT : 0), "wearing wedding suit,")) ||
(opt & OPT_RUWACH && block_stack_vararg(block, flag | (cnt++ ? FLAG_CONCAT : 0), "using ruwach,")) ||
(opt & OPT_CHASEWALK && block_stack_vararg(block, flag | (cnt++ ? FLAG_CONCAT : 0), "using chasewalk,")) ||
(opt & OPT_FLYING && block_stack_vararg(block, flag | (cnt++ ? FLAG_CONCAT : 0), "flying,")) ||
(opt & OPT_XMAS && block_stack_vararg(block, flag | (cnt++ ? FLAG_CONCAT : 0), "wearing santa suit,")) ||
(opt & OPT_TRANSFORM && block_stack_vararg(block, flag | (cnt++ ? FLAG_CONCAT : 0), "transformed,")) ||
(opt & OPT_SUMMER && block_stack_vararg(block, flag | (cnt++ ? FLAG_CONCAT : 0), "summer suit,")) ||
(opt & OPT_DRAGON1 && block_stack_vararg(block, flag | (cnt++ ? FLAG_CONCAT : 0), "dragon 1,")) ||
(opt & OPT_WUG && block_stack_vararg(block, flag | (cnt++ ? FLAG_CONCAT : 0), "wug following,")) ||
(opt & OPT_WUGRIDER && block_stack_vararg(block, flag | (cnt++ ? FLAG_CONCAT : 0), "riding wug,")) ||
(opt & OPT_MADOGEAR && block_stack_vararg(block, flag | (cnt++ ? FLAG_CONCAT : 0), "riding madogear,")) ||
(opt & OPT_DRAGON2 && block_stack_vararg(block, flag | (cnt++ ? FLAG_CONCAT : 0), "dragon 2,")) ||
(opt & OPT_DRAGON3 && block_stack_vararg(block, flag | (cnt++ ? FLAG_CONCAT : 0), "dragon 3,")) ||
(opt & OPT_DRAGON4 && block_stack_vararg(block, flag | (cnt++ ? FLAG_CONCAT : 0), "dragon 4,")) ||
(opt & OPT_DRAGON5 && block_stack_vararg(block, flag | (cnt++ ? FLAG_CONCAT : 0), "dragon 5,")) ||
(opt & OPT_HANBOK && block_stack_vararg(block, flag | (cnt++ ? FLAG_CONCAT : 0), "hanbok,")) ||
(opt & OPT_OKTOBERFEST && block_stack_vararg(block, flag | (cnt++ ? FLAG_CONCAT : 0), "oktoberfest,")))
return exit_mesg("failed to evaluate option expression into constant or invalid option value from '%s'", expr);
/* remove the last comma and space */
block->arg_cnt--;
block->arg[block->arg_cnt - 1] = '\0';
return CHECK_PASSED;
}
int stack_eng_script(block_r * block, char * script) {
int status = 0;
char * buf = NULL;
if( script_recursive(block->script->db, block->script->mode, block->script->map, script, block->item_id, &buf) ||
block_stack_push(block, TYPE_ENG, buf))
status = exit_mesg("failed to write or evaluate script '%s'", script);
free_ptr(buf);
return status;
}
int stack_eng_status_value(block_r * block, char * expr, int type) {
int ignore = 0;
int status = CHECK_FAILED;
switch(type) {
/* to-do: non-renewal aspd calculation */
case 'a': status = stack_eng_aspd(block, expr); break; /* calculate aspd rate */
case 'e': status = stack_eng_map(block, expr, MAP_EFFECT_FLAG, &ignore); break; /* effect */
case 'n': status = stack_eng_int(block, expr, 1, FORMAT_PLUS); break; /* +x; add plus sign */
case 'o': status = stack_eng_int(block, expr, 10, FORMAT_PLUS); break; /* +x/10; add plus sign */
case 'p': status = stack_eng_int(block, expr, 1, FORMAT_PLUS | FORMAT_RATIO); break; /* +x%; add plus and percentage sign */
case 'l': status = stack_eng_int(block, expr, 1, 0); break; /* x; no signs */
case 's': status = stack_eng_int_signed(block, expr, 1, "Reduce", "Add", FORMAT_RATIO); break; /* reduce x on positive, add x on negative */
case 'r': status = stack_eng_int_signed(block, expr, 1, "Regenerate", "Drain", FORMAT_RATIO); break; /* regenerate x on positive, drain x on negative */
case '?': status = CHECK_PASSED; break; /* skip */
}
if(status)
exit_mesg("failed to evaluate '%s' with '%c' status type", expr, type);
return status;
}
/* renewal aspd rate is calculated in status_calc_bl_main by
* (status_calc_aspd * AGI / 200) * 10 ignoring the statuses
* part in rathena */
int stack_eng_aspd(block_r * block, char * expr) {
int status = 0;
int len;
char * buf = NULL;
len = (int) strlen(expr);
if(0 >= len)
return 1;
len += 128;
buf = calloc(len, sizeof(char));
if(is_nil(buf))
return 1;
status = (block->script->mode == RATHENA) ?
!snprintf(buf, len, "(%s) * readparam(bAgi) / 200", expr) ||
stack_eng_int(block, buf, 1, FORMAT_PLUS):
stack_eng_int(block, expr, 1, FORMAT_PLUS);
free_ptr(buf);
return status;
}
int stack_aux_formula(block_r * block, node * node, char * expr) {
int status = 0;
int len = 0;
char * buf = NULL;
if(is_nil(node->formula)) {
if(block_stack_push(block, TYPE_ENG, expr))
return 1;
} else {
len = (int) strlen(expr) + (int) strlen(node->formula) + 16;
buf = calloc(len, sizeof(char));
if(is_nil(buf))
return 1;
if( !snprintf(buf, len, "%s (%s)", expr, node->formula) ||
block_stack_push(block, TYPE_ENG, buf) )
status = exit_stop("failed to push expression onto stack");
}
free_ptr(buf);
return status;
}
/* translate getitem and delitem formats
* getitem <item_id>, <item_amount>, {<account_id>}
* delitem <item_id>, <item_amount>, {<account_id>}
* the total length of all the strings pushed on
* to the block->eng stack is return through size
* which is use to calculate the size of a buffer
* by the caller.
*/
int translate_id_amount(block_r * block, int * argc, int * size, const char * func) {
int _argc = 0;
int _size = 0;
/* check for function call syntax, i.e. (a, b, c) */
_argc = block->ptr_cnt;
if(1 == _argc && '(' == block->ptr[0][0])
/* parse the argument list */
if(stack_ptr_call(block, block->ptr[0], &_argc))
return CHECK_FAILED;
/* check for item id and amount argument */
if(2 > _argc)
return exit_func_safe("%s is missing item id "
"or amount in item %d", func, block->item_id);
/* evaluate the item id and amount expression */
_size = block->arg_cnt;
if( stack_eng_item(block, block->ptr[block->ptr_cnt - 2], &_argc, FLAG_GETITEM) ||
stack_eng_int(block, block->ptr[block->ptr_cnt - 1], 1, 0))
return CHECK_FAILED;
/* return the total length and count of
* strings push to the block->eng stack */
*size = block->arg_cnt - _size;
*argc = _argc;
return CHECK_PASSED;
}
int translate_getitem(block_r * block) {
int i = 0;
int ret = 0;
int off = 0;
int argc = 0;
int size = 0;
char * buf = NULL;
if(translate_id_amount(block, &argc, &size, "getitem"))
return CHECK_FAILED;
/* size only account for item name and amount
* 128 bytes must be added for getitem format */
buf = calloc(size + 128, sizeof(char));
if (buf == NULL)
return CHECK_FAILED;
off += sprintf(&buf[off], "Add %s %s%s",
block->eng[block->eng_cnt - 1],
block->eng[0],
(argc > 1) ? ", " : "");
for (i = 1; i < argc; i++)
off += (i + 1 == argc) ?
sprintf(&buf[off], "and %s", block->eng[i]) :
sprintf(&buf[off], "%s, ", block->eng[i]);
off += sprintf(&buf[off], " into your inventory.");
if(block_stack_push(block, TYPE_ENG, buf))
ret = CHECK_FAILED;
SAFE_FREE(buf);
return ret;
}
int translate_delitem(block_r * block) {
int i = 0;
int ret = 0;
int off = 0;
int argc = 0;
int size = 0;
char * buf = NULL;
if(translate_id_amount(block, &argc, &size, "getitem"))
return CHECK_FAILED;
/* size only account for item name and amount
* 128 bytes must be added for getitem format */
buf = calloc(size + 128, sizeof(char));
if (buf == NULL)
return CHECK_FAILED;
off += sprintf(&buf[off], "Remove %s %s%s",
block->eng[block->eng_cnt - 1],
block->eng[0],
(argc > 1) ? ", " : "");
for (i = 1; i < argc; i++)
off += (i + 1 == argc) ?
sprintf(&buf[off], "and %s", block->eng[i]) :
sprintf(&buf[off], "%s, ", block->eng[i]);
off += sprintf(&buf[off], " from your inventory.");
if(block_stack_push(block, TYPE_ENG, buf))
ret = CHECK_FAILED;
SAFE_FREE(buf);
return ret;
}
int translate_rentitem(block_r * block) {
int i = 0;
int ret = 0;
int off = 0;
int argc = 0;
int size = 0;
char * buf = NULL;
/* check for function call syntax, i.e. (a, b, c) */
argc = block->ptr_cnt;
if(1 == argc && '(' == block->ptr[0][0])
/* parse the argument list */
if(stack_ptr_call(block, block->ptr[0], &argc))
return CHECK_FAILED;
/* check for item id and time argument */
if(2 > argc)
return exit_func_safe("rentitem is missing "
"item id or time in item %d", block->item_id);
size = block->arg_cnt;
if( stack_eng_item(block, block->ptr[block->ptr_cnt - 2], &argc, 0) ||
stack_eng_time(block, block->ptr[block->ptr_cnt - 1], 1))
return CHECK_FAILED;
/* return the total length and count of
* strings push to the block->eng stack */
size = block->arg_cnt - size;
/* size only accounts for item names and time
* 128 bytes must be added for delitem format */
buf = calloc(size + 128, sizeof(char));
if (buf == NULL)
return CHECK_FAILED;
off += sprintf(&buf[off], "Rent a(n) %s%s",
block->eng[0], (argc > 1) ? ", " : "");
for (i = 1; i < argc; i++)
off += (i + 1 == argc) ?
sprintf(&buf[off], "and %s", block->eng[i]) :
sprintf(&buf[off], "%s, ", block->eng[i]);
off += sprintf(&buf[off], " for %s.", block->eng[argc]);
if(block_stack_push(block, TYPE_ENG, buf))
ret = CHECK_FAILED;
SAFE_FREE(buf);
return SCRIPT_PASSED;
}
int translate_heal(block_r * block) {
int status = 0;
node * hp = NULL;
node * sp = NULL;
int flag = TYPE_ENG;
if(2 > block->ptr_cnt)
return exit_func_safe("missing hp or sp argument "
"for %s in item %d", block->name, block->item_id);
hp = evaluate_expression(block, block->ptr[0], 0);
sp = evaluate_expression(block, block->ptr[1], 0);
if(is_nil(hp)) {
status = exit_mesg("failed to evaluate hp expression '%s'", block->ptr[0]);
} else if(is_nil(sp)) {
status = exit_mesg("failed to evaluate sp expression '%s'", block->ptr[1]);
} else {
/* format the hp or sp using positive or negative prefixes */
if( stack_eng_int_signed_re(block, hp, 1, "Recover HP by", "Drain HP by", FORMAT_NO_FORMULA) ||
stack_eng_int_signed_re(block, sp, 1, "Recover SP by", "Drain SP by", FORMAT_NO_FORMULA) ) {
status = exit_stop("failed to write hp or sp expression");
} else {
/* build the final description */
if(hp->min > 0) {
if(block_stack_vararg(block, flag, "%s.", block->eng[0]))
status = exit_stop("failed to write hp expression");
flag |= FLAG_CONCAT;
}
if(sp->min > 0) {
if(block_stack_vararg(block, flag, "%s.", block->eng[1]))
status = exit_stop("failed to write sp expression");
}
}
}
node_free(hp);
node_free(sp);
return status;
}
int translate_produce(block_r * block, int handler) {
int i = 0;
int status = 0;
int top = 0;
int cnt = 0;
if(1 > block->ptr_cnt)
return exit_func_safe("missing item level argument"
" for %s in item %d", block->name, block->item_id);
/* get total number of items pushed onto block->eng
* stack and the length bytes written to the buffer */
top = block->eng_cnt;
if(stack_eng_produce(block, block->ptr[0], &cnt))
return CHECK_FAILED;
cnt += top;
/* write the produce recipes */
status = block_stack_vararg(block, TYPE_ENG, "%s", block->eng[top]);
for(i = top + 1; i < cnt && !status; i++)
status = block_stack_vararg(block, TYPE_ENG | FLAG_CONCAT, "%s", block->eng[i]);
return status ? exit_stop("failed to write produce expression") : 0;
}
int translate_status(block_r * block) {
int id = 0;
int top = 0;
int error = 0;
status_res status;
if(block->ptr_cnt < 3)
return exit_func_safe("missing status id, time, or values"
" argument for %s in item %d", block->name, block->item_id);
if(evaluate_numeric_constant(block, block->ptr[0], &id) ||
status_id(block->script->db, &status, id) ||
stack_eng_time(block, block->ptr[1], 1))
return exit_func_safe("undefined status '%s' in item id %d", block->ptr[0], block->item_id);
/* sc_itemscript is a special case */
switch(id) {
case 21: return translate_status_endure(block); /* sc_endure */
case 289: return translate_status_itemscript(block); /* sc_itemscript */
}
/* error on empty format string which
* indicates that it is not implemented */
if(0 >= (int) strlen(status.format))
return exit_func_safe("%s is not implemented in item %d", status.name, block->item_id);
/* evaluate the values by type */
if( stack_eng_status_value(block, block->ptr[5], status.val4) ||
stack_eng_status_value(block, block->ptr[4], status.val3) ||
stack_eng_status_value(block, block->ptr[3], status.val2) ||
stack_eng_status_value(block, block->ptr[2], status.val1))
return CHECK_FAILED;
/* write the format */
top = block->eng_cnt;
switch(status.offset_count) {
case 0:
error = block_stack_vararg(block,
TYPE_ENG, "%s", status.format);
break;
case 1:
error = block_stack_vararg(block,
TYPE_ENG, status.format,
block->eng[top - status.offset[0]]);
break;
case 2:
error = block_stack_vararg(block,
TYPE_ENG, status.format,
block->eng[top - status.offset[0]],
block->eng[top - status.offset[1]]);
break;
case 3:
error = block_stack_vararg(block,
TYPE_ENG, status.format,
block->eng[top - status.offset[0]],
block->eng[top - status.offset[1]],
block->eng[top - status.offset[2]]);
break;
case 4:
error = block_stack_vararg(block,
TYPE_ENG, status.format,
block->eng[top - status.offset[0]],
block->eng[top - status.offset[1]],
block->eng[top - status.offset[2]],
block->eng[top - status.offset[3]]);
break;
default:
error = exit_func_safe("unsupport status argument count %d"
" in item %d", status.offset_count, block->item_id);
}
/* write the duration */
error = block_stack_vararg(block, TYPE_ENG | FLAG_CONCAT | FLAG_EMPTY, "for %s.", block->eng[0]);
return error;
}
int translate_status_end(block_r * block) {
int cnt = 0;
if(block->ptr_cnt < 1)
return exit_func_safe("missing status id argument"
" for %s in item %d", block->name, block->item_id);
if( stack_eng_map(block, block->ptr[0], MAP_STATUSEFFECT_FLAG, &cnt) ||
block_stack_vararg(block, TYPE_ENG, "Cures %s.", block->eng[0]))
return CHECK_FAILED;
return CHECK_PASSED;
}
int translate_pet_egg(block_r * block) {
int id = 0;
pet_t pet;
/* error on invalid argument count */
if(block->ptr_cnt < 1)
return exit_func_safe("missing pet id argument "
"for %s in item %d", block->name, block->item_id);
/* search for pet */
if( evaluate_numeric_constant(block, block->ptr[0], &id) ||
pet_id(block->script->db, &pet, id))
return exit_func_safe("invalid pet id %d in item %d\n", id, block->item_id);
/* evaluate pet script */
if( stack_eng_script(block, pet.pet_script) ||
stack_eng_script(block, pet.loyal_script) ||
block_stack_vararg(block, TYPE_ENG,"Egg containing %s.\n[Normal Bon"
"us]\n%s[Loyal Bonus]\n%s", pet.name, block->eng[0], block->eng[1]))
return CHECK_FAILED;
return CHECK_PASSED;
}
int translate_bonus(block_r * block, char * prefix) {
int status = 0;
int i = 0;
int j = 0;
int cnt = 0;
bonus_res * bonus = NULL;
if (0 >= block->ptr_cnt) {
status = exit_stop("bonus block has zero arguments");
} else if(calloc_ptr(bonus)) {
status = exit_stop("out of memory");
} else if(bonus_name(block->script->db, bonus, prefix, (int) strlen(prefix), block->ptr[0], (int) strlen(block->ptr[0]))) {
status = exit_mesg("failed to evaluate bonus argument '%s' from item id %d", block->ptr[0], block->item_id);
} else {
/* translate each bonus argument by argument type */
for(i = 0, j = 1; i < bonus->type_cnt && !status; i++, j++) {
/* push the argument on the block->eng stack */
switch(bonus->type[i]) {
/* integer and percentage arguments */
case '0': status = stack_eng_int(block, block->ptr[j], 1, FORMAT_PLUS | FORMAT_RATIO); break;
case '1': status = stack_eng_int(block, block->ptr[j], 10, FORMAT_PLUS | FORMAT_RATIO); break;
case '2': status = stack_eng_int(block, block->ptr[j], 100, FORMAT_PLUS | FORMAT_RATIO); break;
case '3': status = stack_eng_int(block, block->ptr[j], 1, FORMAT_RATIO); break;
case '4': status = stack_eng_int(block, block->ptr[j], 10, FORMAT_RATIO); break;
case '5': status = stack_eng_int(block, block->ptr[j], 100, FORMAT_RATIO); break;
case '6': status = stack_eng_int(block, block->ptr[j], 1, FORMAT_PLUS); break;
case '7': status = stack_eng_int(block, block->ptr[j], 10, FORMAT_PLUS); break;
case '8': status = stack_eng_int(block, block->ptr[j], 100, FORMAT_PLUS); break;
case '9': status = stack_eng_int(block, block->ptr[j], 1, 0); break;
/* special arguments */
case 'h': status = stack_eng_map(block, block->ptr[j], MAP_SP_FLAG, &cnt); break; /* SP Gain Bool */
case 'a': status = stack_eng_time(block, block->ptr[j], 1); break; /* Time */
case 'r': status = stack_eng_map(block, block->ptr[j], MAP_RACE_FLAG, &cnt); break; /* Race */
case 'l': status = stack_eng_map(block, block->ptr[j], MAP_ELEMENT_FLAG, &cnt); break; /* Element */
case 'w': status = stack_eng_grid(block, block->ptr[j]); break; /* Splash */
case 'e': status = stack_eng_map(block, block->ptr[j], MAP_EFFECT_FLAG, &cnt); break; /* Effect */
case 'k': status = stack_eng_skill(block, block->ptr[j], &cnt); break; /* Skill */
case 's': status = stack_eng_map(block, block->ptr[j], MAP_SIZE_FLAG, &cnt); break; /* Size */
case 'c': status = (stack_eng_db(block, block->ptr[j], DB_MOB_ID | DB_NO_ERROR, &cnt) &&
stack_eng_map(block, block->ptr[j], MAP_JOB_FLAG | MAP_CLASS_FLAG, &cnt)); break; /* Monster Class & Job ID * Monster ID */
case 'm': status = stack_eng_db(block, block->ptr[j], DB_ITEM_ID, &cnt); break; /* Item ID */
case 'g': status = stack_eng_map(block, block->ptr[j], MAP_REGEN_FLAG, &cnt); break; /* Regen */
case 'v': status = stack_eng_map(block, block->ptr[j], MAP_CAST_FLAG, &cnt); break; /* Cast Self, Enemy */
case 't': status = stack_eng_trigger_bt(block, block->ptr[j]); break; /* Trigger BT */
case 'y': status = (stack_eng_db(block, block->ptr[j], DB_ITEM_ID | DB_NO_ERROR, &cnt)
&& stack_eng_item_group(block, block->ptr[j])); break; /* Item Group */
case 'd': status = stack_eng_trigger_atf(block, block->ptr[j]); break; /* Triger ATF */
case 'b': status = stack_eng_map(block, block->ptr[j], MAP_TARGET_FLAG, &cnt); break; /* Flag Bitfield */
case 'i': status = stack_eng_map(block, block->ptr[j], MAP_WEAPON_FLAG, &cnt); break; /* Weapon Type */
case 'j': status = (stack_eng_map(block, block->ptr[j], MAP_CLASS_FLAG | MAP_NO_ERROR, &cnt)
&& stack_eng_db(block, block->ptr[j], DB_MOB_ID, &cnt)); break; /* Class Group & Monster */
default: status = CHECK_FAILED;
}
if(status) {
status = exit_mesg("failed to evaluate '%s' for item %d", block->ptr[j], block->item_id);
} else if(cnt > 1) {
status = exit_mesg("'%s' is not a constant value for item %d", block->ptr[j], block->item_id);
}
}
if(!status) {
switch(bonus->type_cnt) {
case 0:
block_stack_vararg(block,
TYPE_ENG, bonus->format);
break;
case 1:
block_stack_vararg(block,
TYPE_ENG, bonus->format,
block->eng[bonus->order[0]]);
break;
case 2:
block_stack_vararg(block,
TYPE_ENG, bonus->format,
block->eng[bonus->order[0]],
block->eng[bonus->order[1]]);
break;
case 3:
block_stack_vararg(block,
TYPE_ENG, bonus->format,
block->eng[bonus->order[0]],
block->eng[bonus->order[1]],
block->eng[bonus->order[2]]);
break;
case 4:
block_stack_vararg(block,
TYPE_ENG, bonus->format,
block->eng[bonus->order[0]],
block->eng[bonus->order[1]],
block->eng[bonus->order[2]],
block->eng[bonus->order[3]]);
break;
case 5:
block_stack_vararg(block,
TYPE_ENG, bonus->format,
block->eng[bonus->order[0]],
block->eng[bonus->order[1]],
block->eng[bonus->order[2]],
block->eng[bonus->order[3]],
block->eng[bonus->order[4]]);
break;
default:
status = exit_func_safe("unsupport bonus argument coun"
"t %d on %s in item %d", bonus->type_cnt, bonus->bonus,
block->item_id);
}
}
}
free_ptr(bonus);
return status;
}
int translate_skill(block_r * block) {
int err = 0;
int cnt = 0;
int flag = 0;
if(2 > block->ptr_cnt)
return exit_func_safe("missing skill id or level argu"
"ment for %s in item %d", block->name, block->item_id);
if(block->ptr_cnt == 2)
if(block_stack_push(block, TYPE_PTR, "1"))
return exit_func_safe("failed to push defa"
"ult arguments in item %d", block->item_id);
/* evaluate skill name, level, and flag */
if(stack_eng_skill(block, block->ptr[0], &cnt) || cnt != 1 ||
stack_eng_int(block, block->ptr[1], 1, 0) ||
evaluate_numeric_constant(block, block->ptr[2], &flag))
return CHECK_FAILED;
switch(flag) {
case 0:
err = block_stack_vararg(block, TYPE_ENG, "Enable "
"skill %s level %s.", block->eng[0], block->eng[1]);
break;
case 1:
err = block_stack_vararg(block, TYPE_ENG, "Temporarily e"
"nable skill %s level %s.", block->eng[0], block->eng[1]);
break;
case 2:
err = block_stack_vararg(block, TYPE_ENG, "Enable skill %s level %s.\nOr "
"add %s levels to the skill.", block->eng[0], block->eng[1], block->eng[1]);
break;
default:
break;
}
return err;
}
int translate_itemskill(block_r * block) {
int cnt = 0;
if(2 > block->ptr_cnt)
return exit_func_safe("missing skill id or level argu"
"ment for %s in item %d", block->name, block->item_id);
/* get skill name and level */
if(stack_eng_skill(block, block->ptr[0], &cnt) || cnt > 1 ||
stack_eng_int(block, block->ptr[1], 1, 0))
return CHECK_FAILED;
return block_stack_vararg(block, TYPE_ENG, "Cast %s [Lv. %s].", block->eng[0], block->eng[1]);
}
int translate_petloot(block_r * block) {
if(0 >= block->ptr_cnt)
return exit_func_safe("missing pet loot amount argum"
"ent for %s in item %d", block->name, block->item_id);
if(stack_eng_int(block, block->ptr[0], 1, 0) ||
block_stack_vararg(block, TYPE_ENG, "Pet can loot and hold upto %s items"
".\nUse pet performance to transfer items to inventory.", block->eng[0]))
return CHECK_FAILED;
return CHECK_PASSED;
}
int translate_petheal(block_r * block) {
if(4 > block->ptr_cnt)
return exit_func_safe("missing heal minimum, maximum, level, or i"
"nterval argument for %s in item %d", block->name, block->item_id);
if( stack_eng_int(block, block->ptr[0], 1, FORMAT_RATIO) ||
stack_eng_int(block, block->ptr[1], 1, FORMAT_RATIO) ||
stack_eng_int(block, block->ptr[2], 1, 0) ||
stack_eng_int(block, block->ptr[3], 1, 0) ||
block_stack_vararg(block, TYPE_ENG, "[HP < %s and SP < %s]\nCast Heal [Lv. %s] fo"
"r every %s seconds.", block->eng[2], block->eng[3], block->eng[0], block->eng[1]))
return CHECK_FAILED;
return CHECK_PASSED;
}
int translate_petrecovery(block_r * block) {
if(2 > block->ptr_cnt)
return exit_func_safe("missing status type or delay arg"
"ument for %s in item %d", block->name, block->item_id);
if(translate_status_end(block) ||
stack_eng_int(block, block->ptr[1], 1, 0) ||
block_stack_vararg(block, TYPE_ENG | FLAG_CONCAT,"%"
"s every %s seconds.", block->eng[0], block->eng[1]))
return CHECK_FAILED;
return CHECK_PASSED;
}
int translate_petskillbonus(block_r * block) {
if(4 > block->ptr_cnt)
return exit_func_safe("missing bonus type, value, duration, or "
"delay argument for %s in item %d", block->name, block->item_id);
if( translate_bonus(block, "bonus") ||
stack_eng_int(block, block->ptr[2], 1, 0) ||
stack_eng_int(block, block->ptr[3], 1, 0) ||
block_stack_vararg(block, TYPE_ENG, "Pet skill bonus is activated for %s second"
"s with a delay of %s seconds.\n%s", block->eng[1], block->eng[2], block->eng[0]))
return CHECK_FAILED;
return CHECK_PASSED;
}
int translate_petskillattack(block_r * block) {
int cnt = 0;
/* error on invalid argment */
if(4 > block->ptr_cnt)
return exit_func_safe("missing skill id, level, normal rate, or lo"
"yalty rate argument for %s in item %d", block->name, block->item_id);
if( stack_eng_skill(block, block->ptr[0], &cnt) || /* skill name */
stack_eng_int(block, block->ptr[1], 1, 0) || /* skill level */
stack_eng_int(block, block->ptr[2], 1, FORMAT_RATIO) || /* normal rate */
stack_eng_int(block, block->ptr[3], 1, FORMAT_RATIO) || /* loyalty rate */
block_stack_vararg(block, TYPE_ENG, "Add a %s (%s on loyalty) chance of casti"
"ng %s [Lv. %s].", block->eng[2], block->eng[3], block->eng[0], block->eng[1]))
return CHECK_FAILED;
return CHECK_PASSED;
}
int translate_petskillattack2(block_r * block) {
int cnt = 0;
if(5 > block->ptr_cnt)
return exit_func_safe("missing skill id, damage, attack count, normal rat"
"e, loyalty rate argument for %s in item %d", block->name, block->item_id);
if( stack_eng_skill(block, block->ptr[0], &cnt) || /* skill name */
stack_eng_int(block, block->ptr[1], 1, 0) || /* damage */
stack_eng_int(block, block->ptr[2], 1, 0) || /* number of attacks */
stack_eng_int(block, block->ptr[3], 1, FORMAT_RATIO) || /* normal rate */
stack_eng_int(block, block->ptr[4], 1, FORMAT_RATIO) || /* loyalty rate */
block_stack_vararg(block, TYPE_ENG, "Add a %s (%s on loyalty) chance of casting %s for %s da"
"mage x %s hits.", block->eng[3], block->eng[4], block->eng[0], block->eng[1], block->eng[2]))
return CHECK_FAILED;
return CHECK_PASSED;
}
int translate_petskillsupport(block_r * block) {
int cnt = 0;
if(4 > block->ptr_cnt)
return exit_func_safe("missing skill id, level, hp minimum, or sp "
"minimum argument for %s in item %d", block->name, block->item_id);
if( stack_eng_skill(block, block->ptr[0], &cnt) ||
stack_eng_int(block, block->ptr[1], 1, 0) ||
stack_eng_int(block, block->ptr[2], 1, 0) ||
stack_eng_int(block, block->ptr[3], 1, FORMAT_RATIO) ||
stack_eng_int(block, block->ptr[4], 1, FORMAT_RATIO) ||
block_stack_vararg(block, TYPE_ENG, "[HP < %s and SP < %s]\nCast %s [Lv. %s] for every "
"%s seconds.", block->eng[3], block->eng[4], block->eng[0], block->eng[1], block->eng[2]))
return CHECK_FAILED;
return CHECK_PASSED;
}
int translate_getexp(block_r * block) {
int status = 0;
int flag = TYPE_ENG;
node * base = NULL;
node * job = NULL;
if(2 > block->ptr_cnt)
return exit_mesg("missing base exprience or job exprience "
"argument for %s in item %d", block->name, block->item_id);
base = evaluate_expression(block, block->ptr[0], 0);
job = evaluate_expression(block, block->ptr[1], 0);
if(is_nil(base)) {
status = exit_mesg("failed to evaluate base expression '%s'", block->ptr[0]);
} else if(is_nil(job)) {
status = exit_mesg("failed to evaluate job expression '%s'", block->ptr[1]);
} else {
if( stack_eng_int_re(block, base, 1, 0) ||
stack_eng_int_re(block, job, 1, 0) ) {
status = exit_stop("failed to write base or job expression");
} else {
if(base->min > 0) {
if(block_stack_vararg(block, flag, "Gain %s base experience.", block->eng[0]))
status = exit_stop("failed to write base expression");
flag |= FLAG_CONCAT;
}
if(job->min > 0) {
if(block_stack_vararg(block, flag, "Gain %s job experience.", block->eng[1]))
status = exit_stop("failed to write job expression");
}
}
}
node_free(base);
node_free(job);
return status;
}
int translate_getguildexp(block_r * block) {
if(1 > block->ptr_cnt)
return exit_func_safe("missing guild exprience argum"
"ent for %s in item %d", block->name, block->item_id);
if(stack_eng_int(block, block->ptr[0], 1, 0) ||
block_stack_vararg(block, TYPE_ENG, "Gain %s guild experience.", block->eng[0]))
return CHECK_FAILED;
return CHECK_PASSED;
}
int translate_autobonus(block_r * block, int flag) {
int ret = 0;
char * buf = NULL;
if(3 > block->ptr_cnt)
return exit_func_safe("missing bonus script, rate, or durati"
"on argument for %s in item %d", block->name, block->item_id);
/* default 0 flag */
if(3 == block->ptr_cnt)
block_stack_push(block, TYPE_PTR, "0");
if( stack_eng_int(block, block->ptr[1], 10, FORMAT_RATIO) ||
stack_eng_time(block, block->ptr[2], 1) ||
stack_eng_trigger_bt(block, block->ptr[3]) ||
block_stack_vararg(block, TYPE_ENG, "Add %s chance to activa"
"te %s for %s.", block->eng[0], block->eng[2], block->eng[1]))
ret = CHECK_FAILED;
SAFE_FREE(buf);
return ret;
}
int translate_hire_mercenary(block_r * block) {
int cnt = 0;
if(2 > block->ptr_cnt)
return exit_func_safe("missing mercenary id or duration a"
"rgument for %s in item %d", block->name, block->item_id);
if( stack_eng_db(block, block->ptr[0], DB_MERC_ID, &cnt) || cnt != 1 ||
stack_eng_time(block, block->ptr[1], 1) ||
block_stack_vararg(block, TYPE_ENG, "Hire merce"
"nary %s for %s.", block->eng[0], block->eng[1]))
return CHECK_FAILED;
return CHECK_PASSED;
}
int translate_buyingstore(block_r * block) {
/* check empty block->ptr stack */
if(1 > block->ptr_cnt)
return exit_func_safe("buyingstore is missing"
" type argument in item %d", block->item_id);
if(stack_eng_int(block, block->ptr[0], 1, 0) ||
block_stack_vararg(block, TYPE_ENG, "Open a "
"buying store with %s slots.", block->eng[0]))
return CHECK_FAILED;
return CHECK_PASSED;
}
int translate_searchstore(block_r * block) {
int cnt = 0;
if(2 > block->ptr_cnt)
return exit_func_safe("searchstore is missing "
"amount or effect in item %d", block->item_id);
if(stack_eng_int(block, block->ptr[0], 1, 0) ||
stack_eng_map(block, block->ptr[1], MAP_SEARCHSTORE_FLAG, &cnt) || cnt != 1 ||
block_stack_vararg(block, TYPE_ENG, "Search for open vendors on %s. "
"Allow up to %s uses before expiring.", block->eng[1], block->eng[0]))
return CHECK_FAILED;
return CHECK_PASSED;
}
int translate_skill_block(block_r * block) {
int cnt = 0;
if(3 > block->ptr_cnt)
return exit_func_safe("unitskilluseid is missing"
" skill id or level in item %d", block->item_id);
if( stack_eng_skill(block, block->ptr[1], &cnt) || cnt != 1 ||
stack_eng_int(block, block->ptr[2], 1, 0) ||
block_stack_vararg(block, TYPE_ENG, "Cast %"
"s [Lv. %s].", block->eng[0], block->eng[1]))
return CHECK_FAILED;
return CHECK_PASSED;
}
int translate_warp(block_r * block) {
int cnt = 0;
if(3 > block->ptr_cnt)
return exit_func_safe("warp is missing map name, x coo"
"rdinate, or y coordinate in item %d", block->item_id);
if( stack_eng_db(block, block->ptr[0], DB_MAP_ID, &cnt) || cnt != 1||
stack_eng_int(block, block->ptr[1], 1, 0) ||
stack_eng_int(block, block->ptr[2], 2, 0) ||
block_stack_vararg(block, TYPE_ENG, "Warp to %s on (%s, %s)"
" coordinate.", block->eng[0], block->eng[1], block->eng[2]))
return CHECK_FAILED;
return CHECK_PASSED;
}
int translate_monster(block_r * block) {
int cnt = 0;
if(6 > block->ptr_cnt)
return exit_func_safe("monster is missing map name, x coordinate, "
"y coordinate, name, mob id, or amount in item %d", block->item_id);
if( ((0 == ncs_strcmp(block->ptr[0], "this")) ?
block_stack_push(block, TYPE_ENG, "current map"):
stack_eng_db(block, block->ptr[0], DB_MAP_ID, &cnt)) || cnt > 1 ||
stack_eng_coordinate(block, block->ptr[1]) ||
stack_eng_coordinate(block, block->ptr[2]) ||
stack_eng_db(block, block->ptr[4], DB_MOB_ID, &cnt) || cnt != 1 ||
stack_eng_int(block, block->ptr[5], 1, 0) ||
block_stack_vararg(block, TYPE_ENG, "Summon %s %s on %s on (%s, %s) coordinate"
"s.", block->eng[4], block->eng[3], block->eng[0], block->eng[1], block->eng[2]))
return CHECK_FAILED;
return CHECK_PASSED;
}
int translate_callfunc(block_r * block) {
int value = 0;
char * buf = NULL;
if(0 == ncs_strcmp(block->ptr[0],"F_CashCity")) {
if(evaluate_numeric_constant(block, block->ptr[1], &value))
return exit_mesg("failed to evaluate F_CashCity expression '%s'", block->ptr[1]);
switch(value) {
case 1: buf = "Teleport to any town or city."; break;
case 2: buf = "Teleport to Juno, Lighthalzen, Einbroch, or Hugel."; break;
case 3: buf = "Teleport to Rachel or Veins."; break;
case 4: buf = "Teleport to Ayothaya, Amatsu, Louyang, or Kunlun."; break;
case 5: buf = "Teleport to any town, city, etc."; break;
default:
return exit_mesg("unsupported F_CashCity argum"
"ent %d in item id %d", value, block->item_id);
}
} else if(0 == ncs_strcmp(block->ptr[0],"F_CashTele")) {
if(evaluate_numeric_constant(block, block->ptr[1], &value))
return exit_mesg("failed to evaluate F_CashTele expression '%s'", block->ptr[1]);
switch(value) {
case 1: buf = "Teleport to the Save Point, Prontera, Geffen, Al De Baran or Izlude."; break;
case 2: buf = "Teleport to the Save Point, Payon, Alberta, Morocc or Comodo Island."; break;
case 3: buf = "Teleport to the Save Point, Dragon City, Gonryun, Amatsu or Ayothaya."; break;
case 4: buf = "Teleport to the Save Point, Lutie, Umbala, or Niflheim."; break;
case 5: buf = "Teleport to the Save Point, Juno, Einbroch, Lighthalzen, or Hugel."; break;
case 6: buf = "Teleport to the Save Point, Rachel, or Veins."; break;
default:
return exit_mesg("unsupported F_CashTele argum"
"ent %d in item id %d", value, block->item_id);
}
} else if(0 == ncs_strcmp(block->ptr[0], "F_CashSiegeTele")) {
buf = "Warp to any guild castle.";
} else if(0 == ncs_strcmp(block->ptr[0], "F_CashStore")) {
buf = "Emergency Kafra service dispatch.";
} else if(0 == ncs_strcmp(block->ptr[0],"F_CashDungeon")) {
buf = "Teleport directly to a dungeon of choice.";
} else if(0 == ncs_strcmp(block->ptr[0],"F_CashPartyCall")) {
buf = "Allows to teleport many people at once to random areas when used "
"by party master. All the party members in the same map will move "
" to the location where the party leader is sent.";
} else if(0 == ncs_strcmp(block->ptr[0],"F_CashReset")) {
buf = "Resets the Skill Tree and gives the corresponding number of Skill Points. "
"This item can only be used in town, and the character must carry 0 weight,"
" and can not be equipped with a Pushcart, Falcon, or PecoPeco. Can not be "
"used by Novice Class.";
} else if(0 == ncs_strcmp(block->ptr[0],"F_Snowball")) {
buf = "Random effect of Restore, Endure, or Wing of Butterfly.";
} else if(0 == ncs_strcmp(block->ptr[0],"F_CashSeigeTele")) {
buf = "Teleport to a specific castle in any guild grounds.";
} else if(0 == ncs_strcmp(block->ptr[0],"F_Cat_Hard_Biscuit")) {
buf = "An unforgetable taste! May recover HP or SP.";
} else if(0 == ncs_strcmp(block->ptr[0],"F_Rice_Weevil_Bug")) {
buf = "An unforgetable taste! May recover HP or SP.";
} else if(0 == ncs_strcmp(block->ptr[0],"SetPalete")) {
buf = "Set a different palete.";
} else {
return exit_func_safe("unsupported callfunc '%"
"s' in item %d", block->ptr[0], block->item_id);
}
if(block_stack_push(block, TYPE_ENG, buf))
return exit_stop("failed to write expression");
return 0;
}
/* getrandgroupitem require special attention because of
* how item group works for rathena and hercules and how
* items are randomly selected; the code looks confusing
* but if you understand the stack, then it shouldn't be
* a problem... e.e sorry */
int translate_getrandgroupitem(block_r * block) {
int i = 0;
int err = 0;
int arg_cnt = 0;
int arg_off = 0;
/* getrandgroupitem arguments */
int group_id = 0;
int quantity = 0;
int subgroup_id = 0;
/* item group metadata */
item_group_meta_t * meta = NULL;
item_group_t items;
item_t item;
memset(&items, 0, sizeof(item_group_t));
memset(&item, 0, sizeof(item_t));
/* support for hercules soon */
if(block->script->mode != RATHENA)
return exit_abt_safe("only supported on rathena");
/* error on invalid arguments */
if(0 >= block->ptr_cnt)
return exit_func_safe("getrandgroupitem is missing group "
"id, quantity, or subgroup id in item %d", block->item_id);
/* getrandgroupitem supports function call syntax, i.e.
* getrandgroupitem x, y or getrandgroupitem(x, y) =.= */
if(block->ptr[0][0] == '(') {
/* getrandgroupitem(x, y, z) form */
if(stack_ptr_call(block, block->ptr[0], &arg_cnt))
return exit_func_safe("invalid function call synta"
"x '%s' in item %d", block->ptr[0], block->item_id);
/* starting group id, quantity, and subgroup id starts at
* offset 1 because 0 contains the function call syntax */
arg_off = 1;
} else {
/* getrandgroupitem x, y, z; form */
arg_cnt = block->ptr_cnt;
arg_off = 0;
}
/* use default quantity and subgroup id if not specified */
switch(arg_cnt) {
case 1: err = block_stack_push(block, TYPE_PTR, "0"); /* default quantity is 0 */
case 2: err = block_stack_push(block, TYPE_PTR, "1"); /* default subgroup id is 1 */
}
if(err)
return exit_func_safe("failed to push defa"
"ult arguments in item %d", block->item_id);
/* get the group id, quantity, and subgroup id constants */
if( evaluate_numeric_constant(block, block->ptr[arg_off + 0], &group_id) ||
evaluate_numeric_constant(block, block->ptr[arg_off + 1], &quantity) ||
evaluate_numeric_constant(block, block->ptr[arg_off + 2], &subgroup_id))
return exit_func_safe("failed to evaluate group id, q"
"uantity, or subgroup id in item %d", block->item_id);
/* get the group name and quantity (w/ formula if exist) very
* inefficient since group id and quantity is evaluated twice */
if(stack_eng_item_group(block, block->ptr[arg_off + 0]) ||
stack_eng_int(block, block->ptr[arg_off + 1], 1, 0))
return exit_func_safe("failed to get group name in item %d", block->item_id);
/* query item group metadata */
meta = calloc(1, sizeof(item_group_meta_t));
if(NULL == meta)
goto failed;
if(item_group_id_ra(block->script->db, meta, group_id, subgroup_id)) {
exit_func_safe("failed to find group id %d and subgroup id "
"%d for item id %d", group_id, subgroup_id, block->item_id);
goto failed;
}
if(0 >= meta->item) {
exit_func_safe("group id %d and subgroup id %d has no ite"
"ms in item id %d", group_id, subgroup_id, block->item_id);
goto failed;
}
/* write the group name and quantity */
if(group_id != 0) {
if(quantity > 0) {
if(block_stack_vararg(block, TYPE_ENG, "Select %s item%s from %s.", block->eng\
[block->eng_cnt - 1], (quantity > 1) ? "s" : "", block->eng[block->eng_cnt - 2]))
goto failed;
} else {
if(block_stack_vararg(block, TYPE_ENG, "Select random amou"
"nt of items from %s.", block->eng[block->eng_cnt - 2]))
goto failed;
}
} else {
if(block_stack_vararg(block, TYPE_ENG, "Add the items listed below to your inventory."))
goto failed;
}
/* write either the item group summary or the list of items */
if(meta->item > MAX_STR_LIST) {
if(meta->heal) err = block_stack_vararg(block, TYPE_ENG | FLAG_CONCAT, " * %d healing items", meta->heal);
if(meta->usable) err = block_stack_vararg(block, TYPE_ENG | FLAG_CONCAT, " * %d usable items", meta->usable);
if(meta->etc) err = block_stack_vararg(block, TYPE_ENG | FLAG_CONCAT, " * %d etc items", meta->etc);
if(meta->armor) err = block_stack_vararg(block, TYPE_ENG | FLAG_CONCAT, " * %d armor items", meta->armor);
if(meta->weapon) err = block_stack_vararg(block, TYPE_ENG | FLAG_CONCAT, " * %d weapon items", meta->weapon);
if(meta->card) err = block_stack_vararg(block, TYPE_ENG | FLAG_CONCAT, " * %d card items", meta->card);
if(meta->pet) err = block_stack_vararg(block, TYPE_ENG | FLAG_CONCAT, " * %d pet items", meta->pet);
if(meta->pet_equip) err = block_stack_vararg(block, TYPE_ENG | FLAG_CONCAT, " * %d pet equipment items", meta->pet_equip);
if(meta->ammo) err = block_stack_vararg(block, TYPE_ENG | FLAG_CONCAT, " * %d ammo items", meta->ammo);
if(meta->delay_usable) err = block_stack_vararg(block, TYPE_ENG | FLAG_CONCAT, " * %d delay usable items", meta->delay_usable);
if(meta->shadow) err = block_stack_vararg(block, TYPE_ENG | FLAG_CONCAT, " * %d shadow equipment items", meta->shadow);
if(meta->confirm_usable) err = block_stack_vararg(block, TYPE_ENG | FLAG_CONCAT, " * %d confirm usable items", meta->confirm_usable);
if(meta->bind) err = block_stack_vararg(block, TYPE_ENG | FLAG_CONCAT, " * %d bounded items", meta->bind);
if(meta->rent) err = block_stack_vararg(block, TYPE_ENG | FLAG_CONCAT, " * %d rental items", meta->rent);
if(err)
goto failed;
} else {
/* get a list of all item id in item group */
if(item_group_id(block->script->db, &items, group_id, subgroup_id)) {
exit_func_safe("failed to get item id for group id %d subgro"
"up_id %d in item %d", group_id, subgroup_id, block->item_id);
goto failed;
}
/* write every item in the item group */
for(i = 0; i < items.size; i++) {
if(item_id(block->script->db, &item, items.item_id[i]) ||
block_stack_vararg(block, TYPE_ENG | FLAG_CONCAT, " * %s", item.name)) {
exit_func_safe("failed to find item id %d in "
"item id %d", items.item_id[i], block->item_id);
goto failed;
}
}
}
clean:
item_group_free(&items);
SAFE_FREE(meta);
return err;
failed:
err = CHECK_FAILED;
goto clean;
}
int translate_getgroupitem(block_r * block) {
int i = 0;
int group_id = 0;
block_r * subgroup = NULL;
int subgroup_id[5];
int subgroup_len = 5;
memset(subgroup_id, 0, sizeof(subgroup_id));
/* support for hercules soon */
if(block->script->mode != RATHENA)
return exit_abt_safe("only supported on rathena");
/* error on invalid arguments */
if(1 != block->ptr_cnt)
return exit_func_safe("getgroupitem is mis"
"sing group id in item %d", block->item_id);
/* grab a empty block */
if(evaluate_numeric_constant(block, block->ptr[0], &group_id) ||
item_subgroup_id(block->script->db, subgroup_id, &subgroup_len, group_id) ||
script_block_new(block->script, &subgroup))
return SCRIPT_FAILED;
/* cheap hack to disable error from translate_getrandgroupitem */
for(i = 0; i < subgroup_len; i++) {
/* block id for getrandgroupitem */
subgroup->name = convert_string("getrandgroupitem");
subgroup->item_id = block->item_id;
subgroup->type = 20;
if( block_stack_vararg(subgroup, TYPE_PTR, "%d", group_id) ||
block_stack_vararg(subgroup, TYPE_PTR, "0") ||
block_stack_vararg(subgroup, TYPE_PTR, "%d", subgroup_id[i]) ||
translate_getrandgroupitem(subgroup)) {
/* skip */
} else {
if( (block->eng_cnt == 0) ?
block_stack_vararg(block, TYPE_ENG, subgroup->eng[subgroup->eng_cnt - 1]):
block_stack_vararg(block, TYPE_ENG | FLAG_CONCAT, subgroup->eng[subgroup->eng_cnt - 1])) {
script_block_free(block->script, &subgroup);
return CHECK_FAILED;
}
}
block_reset(subgroup);
}
/* return the block */
script_block_free(block->script, &subgroup);
return (block->eng_cnt > 0) ? CHECK_PASSED : CHECK_FAILED;
}
int translate_bonus_script(block_r * block) {
/* error on invalid arguments */
if(2 > block->ptr_cnt)
return exit_func_safe("bonus_script is missing "
"script or duration in item %d", block->item_id);
/* evaluate only script and duration */
if( stack_eng_script(block, block->ptr[0]) ||
stack_eng_time(block, block->ptr[1], 1) ||
block_stack_vararg(block, TYPE_ENG, "Item bonus lasting for %s.", block->eng[1]) ||
block_stack_vararg(block, TYPE_ENG | FLAG_CONCAT, "%s", block->eng[0]))
return CHECK_FAILED;
return CHECK_PASSED;
}
int translate_transform(block_r * block) {
int status = 0;
int count = 0;
int extra = 0;
block_r * sc_start4 = NULL;
extra = block->ptr_cnt;
if(2 > block->ptr_cnt)
return exit_mesg("transform is missing mob "
"id or duration in item %d", block->item_id);
/* set default status value arguments */
switch(block->ptr_cnt) {
case 2: status = block_stack_push(block, TYPE_PTR, "0");
case 3: status = block_stack_push(block, TYPE_PTR, "0");
case 4: status = block_stack_push(block, TYPE_PTR, "0");
case 5: status = block_stack_push(block, TYPE_PTR, "0");
case 6: status = block_stack_push(block, TYPE_PTR, "0");
}
if(status)
return exit_mesg("failed to push default"
" arguments in item %d", block->item_id);
/* handle mob name and durection */
if( stack_eng_db(block, block->ptr[0], DB_MOB_ID, &count) || count != 1 ||
stack_eng_time(block, block->ptr[1], 1)) {
status = 1;
} else if(block_stack_vararg(block, TYPE_ENG, "Transform into a %s for %s.", block->eng[0], block->eng[1])) {
status = exit_mesg("failed to write transform string for item %d", block->item_id);
} else if(extra > 2) {
if(script_block_new(block->script, &sc_start4)) {
status = exit_stop("out of memory");
} else {
/* initialize a sc_start4 block */
sc_start4->name = convert_string("sc_start4");
sc_start4->item_id = block->item_id;
sc_start4->type = 15;
if( block_stack_vararg(sc_start4, TYPE_PTR, "%s", block->ptr[2]) ||
block_stack_vararg(sc_start4, TYPE_PTR, "%s", block->ptr[1]) ||
block_stack_vararg(sc_start4, TYPE_PTR, "%s", block->ptr[3]) ||
block_stack_vararg(sc_start4, TYPE_PTR, "%s", block->ptr[4]) ||
block_stack_vararg(sc_start4, TYPE_PTR, "%s", block->ptr[5]) ||
block_stack_vararg(sc_start4, TYPE_PTR, "%s", block->ptr[6]) ||
translate_status(sc_start4) ) {
status = exit_mesg("failed to evaluate transform status in item %d", block->item_id);
} else if(block_stack_vararg(block, TYPE_ENG | FLAG_CONCAT, "%s", sc_start4->eng[sc_start4->eng_cnt - 1]) ) {
status = exit_mesg("failed to write transform string in item %d", block->item_id);
}
script_block_free(block->script, &sc_start4);
}
}
return status;
}
int translate_setfalcon(block_r * block) {
int status = 0;
node * node = NULL;
if(1 != block->ptr_cnt)
return exit_func_safe("setfalcon is missing"
" flag argument in item %d", block->item_id);
node = evaluate_expression(block, block->ptr[0], 0);
if(is_nil(node))
status = exit_mesg("failed to evaluate '%s' in item %d", block->item_id);
status = (node->min != node->max) ?
block_stack_vararg(block, TYPE_ENG, "Summon or release a falcon.") ||
stack_aux_formula(block, node, block->eng[block->eng_cnt - 1]) :
block_stack_vararg(block, TYPE_ENG, "%s", (node->max) ? "Release a falcon." : "Summon a falcon.");
if(status)
exit_mesg("failed to write setfalcon string in item %d", block->item_id);
node_free(node);
return CHECK_PASSED;
}
int translate_makerune(block_r * block) {
int i = 0;
int argc = 0;
if( block_stack_push(block, TYPE_PTR, "24") ||
stack_eng_int(block, block->ptr[0], 1, FORMAT_RATIO) ||
stack_eng_produce(block, block->ptr[1], &argc) ||
block_stack_vararg(block, TYPE_ENG, "Create runestones with %s success rate.", block->eng[0]))
return CHECK_FAILED;
/* write the produce recipes */
argc += 1;
for (i = 1; i < argc; i++)
if (block_stack_vararg(block, TYPE_ENG | FLAG_CONCAT, "%s", block->eng[i]))
return CHECK_FAILED;
return CHECK_PASSED;
}
int translate_status_itemscript(block_r * block) {
int status = 0;
int id = 0;
item_t * item = NULL;
char * buffer = NULL;
if(calloc_ptr(item)) {
status = exit_stop("out of memory");
} else if(evaluate_numeric_constant(block, block->ptr[2], &id)) {
status = exit_mesg("failed to evaluate '%s' into a constant", block->ptr[2]);
} else if(item_id(block->script->db, item, id)) {
status = exit_mesg("failed to find item id %d in item id %d", id, block->item_id);
} else if(stack_eng_script(block, item->script)) {
status = 1;
}
free_ptr(item);
free_ptr(buffer);
return status;
}
int translate_status_endure(block_r * block) {
int status = 0;
int infinite = 0;
if( stack_eng_int(block, block->ptr[2], 1, 0) ||
stack_eng_int(block, block->ptr[3], 1, 0) ||
evaluate_numeric_constant(block, block->ptr[5], &infinite) )
return 1;
if(infinite) {
status = block_stack_vararg(block, TYPE_ENG, "Increase MDEF"
" by %s and endure infinite number of hits.", block->eng[1]);
} else {
status = block_stack_vararg(block, TYPE_ENG, "Increase MDEF by %s and endu"
"re %s number of hits for %s.", block->eng[1], block->eng[2], block->eng[0]);
}
return status;
}
int evaluate_numeric_constant(block_r * block, char * expr, int * constant) {
int status = 0;
node * node;
node = evaluate_expression(block, expr, 0);
if(is_nil(node))
return 1;
if(node->min != node->max) {
status = exit_mesg("expression '%s' does not evaluate to a constant", expr);
} else {
*constant = node->min;
}
node_free(node);
return status;
}
int evaluate_expression_formula_concat(struct rbt_node * node, void * context, int flag) {
struct string {
char * ptr;
int off;
int len;
} * string = context;
string->off = sprintf(&string->ptr[string->off], string->off ? ", %s" : "%s", node->val);
return 0;
}
int evaluate_expression_formula_length(struct rbt_node * node, void * context, int flag) {
*((int *) context) += (int) strlen(node->val) + 3;
return 0;
}
int evaluate_expression_formula_re(block_r * block, rbt_logic * logic, rbt_tree * tree) {
int status = 0;
int hash = 0;
rbt_node * name = NULL;
switch(logic->type) {
case var:
hash = sdbm((unsigned char *) logic->name);
if(rbt_search(tree, &name, hash))
if( rbt_node_init(&name, hash, logic->name) ||
rbt_insert(tree, name)) {
rbt_node_deit(&name);
status = exit_stop("out of memory");
}
break;
/* iterate the logic tree recursively */
default:
status = evaluate_expression_formula_re(block, logic->l, tree) ||
evaluate_expression_formula_re(block, logic->r, tree);
break;
}
return status;
}
int evaluate_expression_formula(block_r * block, rbt_logic * logic, char ** formula) {
int status = 0;
int length = 0;
rbt_tree * tree = NULL;
rbt_node * temp = NULL;
rbt_logic * iter = NULL;
struct string {
char * ptr;
int off;
int len;
} string;
if(rbt_tree_init(&tree)) {
status = exit_stop("out of memory");
} else {
iter = logic;
do {
status = evaluate_expression_formula_re(block, iter, tree);
iter = iter->next;
} while(iter != logic && !status);
if(!status && !rbt_deploy(tree, evaluate_expression_formula_length, &length) && 0 < length) {
string.ptr = calloc(length, sizeof(char));
if(is_nil(string.ptr)) {
status = exit_stop("out of memory");
} else {
string.off = 0;
string.len = length;
if(rbt_deploy(tree, evaluate_expression_formula_concat, &string)) {
status = exit_stop("failed to write formula string");
} else {
free_ptr(*formula);
*formula = string.ptr;
}
}
}
}
while(tree->root) {
temp = tree->root;
rbt_delete(tree, temp);
free_ptr(temp);
}
rbt_tree_deit(&tree);
return status;
}
/* lowest level function for evaluating expressions
* stack_eng_* an
d evaluate_function_* are both one
* level above */
node * evaluate_expression(block_r * block, char * expr, int flag) {
int status = 0;
token_r * tokens = NULL;
node * node = NULL;
rbt_logic * logic = NULL;
if( calloc_ptr(tokens) ||
script_lexical(tokens, expr) ||
0 >= tokens->script_cnt ) {
status = 1;
} else {
node = evaluate_expression_recursive(block, tokens->script_ptr, 0, tokens->script_cnt, block->logic, NULL, flag);
if(is_nil(node)) {
status = 1;
} else if(EVALUATE_FLAG_KEEP_LOGIC_TREE & flag && node->logic) {
if(rbt_logic_copy(&logic, node->logic)) {
status = 1;
} else {
/* set the root to the latest logic tree */
if(block->logic)
rbt_logic_append(logic, block->logic);
block->logic = logic;
}
} else if(node->logic) {
/* build the dependency formula */
if(evaluate_expression_formula(block, node->logic, &node->formula))
status = 1;
}
}
free_ptr(tokens);
return node;
}
static node * evaluate_expression_recursive(block_r * block, char ** expr, int start, int end, rbt_logic * logic_tree, rbt_tree * id_tree, int flag) {
int i = 0;
int status = 0;
char operator = 0;
int operand = 0;
node * root;
node * iter;
node * temp;
node * result = NULL;
/* create the doubly linked list's root node */
if(node_init(block->script, &root)) {
exit_stop("out of memory");
return NULL;
}
for(i = start, iter = root; i < end && !status; i++) {
temp = NULL;
if(expr[i][0] == '(') {
/* create subexpression node */
status = evaluate_expression_sub(block, expr, &i, end, logic_tree, id_tree, flag, &temp);
operand++;
} else if(SCRIPT_BINARY(expr[i][0])) {
/* create operator node */
if(!node_init(block->script, &temp)) {
/* set operator symbol and type */
temp->op = expr[i][0];
temp->type = (operand) ? NODE_TYPE_OPERATOR : NODE_TYPE_UNARY;
if(NODE_TYPE_OPERATOR == temp->type && i + 1 < end) {
/* check for dual operator */
operator = expr[i + 1][0];
if(SCRIPT_BINARY(operator) && !SCRIPT_UNARY(operator)) {
temp->op += expr[i + 1][0];
i++; /* consume the operator */
}
}
/* reset operand count */
operand = 0;
}
} else if(isdigit(expr[i][0])) {
/* create literal integer node */
if(!node_init(block->script, &temp)) {
temp->type = NODE_TYPE_OPERAND;
temp->max = convert_integer(expr[i], 10);
temp->min = temp->max;
if(rbt_range_init(&temp->value, temp->min, temp->max, 0))
node_free(temp);
operand++;
}
} else if (SCRIPT_STRING(expr[i][0]) || SCRIPT_SYMBOL(expr[i][0])) {
/* create variables, functions, and set node */
status = evaluate_expression_var(block, expr, &i, end, logic_tree, flag, &temp);
operand++;
}
/* add node to singly list for freeing */
iter->free = temp;
if(operand > 1) {
status = exit_mesg("invalid token '%s' detected in item id %d", expr[i], block->item_id);
} else {
if(is_ptr(temp)) {
/* add node to doubly list for structuring */
node_append(iter, temp);
iter = temp;
} else {
status = 1;
}
}
}
if(!status) {
if( root == iter ||
node_eval_tree(root) ||
node_eval(root->next, node_dbg, logic_tree, id_tree, flag) ) {
status = exit_mesg("invalid expression detected in item id %d", block->item_id);
} else {
result = root->next;
iter = root;
while (iter != NULL) {
if (iter->free == result) {
iter->free = result->free;
break;
}
iter = iter->free;
}
/* =.= */
result->free = NULL;
result->left = NULL;
result->right = NULL;
result->next = result;
result->prev = result;
}
}
iter = root;
while(iter != NULL) {
temp = iter;
iter = iter->free;
node_free(temp);
}
return result;
}
static int evaluate_expression_end_parenthesis(char ** expr, int start, int end, int * index) {
int i;
char c = 0;
int p = 0;
/* find the ending parenthesis */
for(i = start; i < end; i++) {
c = expr[i][0];
if('(' == c) {
p++;
} else if(')' == c) {
p--;
if(0 == p) /* ending parenthesis founded */
break;
}
}
if(p || i >= end)
return 1;
*index = i;
return 0;
}
static int evaluate_expression_sub(block_r * block, char ** expr, int * start, int end, rbt_logic * logic_tree, rbt_tree * id_tree, int flag, node ** temp) {
int index = 0;
int status = 0;
node * node = NULL;
if(evaluate_expression_end_parenthesis(expr, *start, end, &index)) {
status = exit_mesg("unmatch parenthesis in item %d", block->item_id);
} else if(1 == (index - *start)) {
status = exit_mesg("empty subexpression in item %d", block->item_id);
} else {
if(index + 1 < end && expr[index + 1][0] == '?')
flag |= EVALUATE_FLAG_EXPR_BOOL;
node = evaluate_expression_recursive(block, expr, *start + 1, index, logic_tree, id_tree, flag);
if(is_nil(node)) {
status = 1;
} else {
node->type = NODE_TYPE_SUB;
/* return expression node and skip evaluated sub expression */
*temp = node;
}
}
*start = index;
return status;
}
static int evaluate_expression_var(block_r * block, char ** expr, int * start, int end, rbt_logic * logic_tree, int flag, node ** temp) {
node * object = NULL;
db_t * db = block->script->db;
char * str = expr[*start];
size_t len = (int) strlen(str);
int index = 0;
int status = 0;
var_res * var = NULL;
option_res * opt = NULL;
map_res * map = NULL;
const_t * cst = NULL;
block_r * set = NULL;
if(0 >= len)
return exit_stop("identifier is empty");
if(node_init(block->script, &object))
return exit_stop("out of memory");
if( calloc_ptr(var) ||
!var_name(db, var, str, (int) len) ) {
object->var = var->id;
object->logic_count = 1;
object->id = convert_string(var->desc);
if(is_nil(object->id)) {
status = exit_stop("out of memory");
} else {
if(var->flag & VARI_FLAG) {
object->type = NODE_TYPE_VARIABLE;
object->min = var->min;
object->max = var->max;
object->formula = convert_string(var->desc);
if( is_nil(object->formula) ||
rbt_range_init(&object->value, object->min, object->max, 0))
status = exit_stop("out of memory");
} else if(var->flag & FUNC_FLAG) {
object->type = NODE_TYPE_FUNCTION;
object->return_type |= var->type;
if(evaluate_expression_end_parenthesis(expr, *start, end, &index)) {
status = exit_mesg("unmatch parenthesis in item %d", block->item_id);
} else {
if(var->flag & FUNC_CONST_FLAG) {
object->min = var->min;
object->max = var->max;
object->formula = convert_string(var->desc);
if( is_nil(object->formula) ||
rbt_range_init(&object->value, object->min, object->max, 0) )
status = exit_stop("out of memory");
} else if(evaluate_function(block, expr, *start + 1, index, var, object)) {
status = 1;
}
/* skip function's call arguments */
*start = index;
}
} else {
status = exit_mesg("variable %d is has invalid flag", var->id);
}
}
} else {
object->id = convert_string(str);
if(is_nil(object->id)) {
status = exit_stop("out of memory");
} else if(calloc_ptr(opt) || !opt_name(db, opt, str, (int) len)) {
object->type = NODE_TYPE_CONSTANT;
object->min = opt->flag;
object->max = opt->flag;
object->formula = convert_string(opt->name);
if( is_nil(object->formula) ||
rbt_range_init(&object->value, object->min, object->max, 0) )
status = exit_stop("out of memory");
} else if(calloc_ptr(map) || !map_name(db, map, str, (int) len)) {
object->type = NODE_TYPE_CONSTANT;
object->min = map->id;
object->max = map->id;
if(rbt_range_init(&object->value, object->min, object->max, 0))
status = exit_stop("out of memory");
} else if(calloc_ptr(cst) || !const_name(db, cst, str, (int) len)) {
object->type = NODE_TYPE_CONSTANT;
object->min = cst->value;
object->max = cst->value;
if(rbt_range_init(&object->value, object->min, object->max, 0))
status = exit_stop("out of memory");
} else {
object->min = 0;
object->max = 0;
/* find the set variable in the linked list */
set = block->set;
while(set) {
if( 0 == strcmp(set->ptr[0], str) &&
!node_copy(set->set_node, object) )
break;
set = set->set;
}
/* default to zero min and max */
if(is_nil(object->value))
if(rbt_range_init(&object->value, object->min, object->max, 0))
status = exit_stop("out of memory");
object->type = NODE_TYPE_LOCAL;
}
}
/* set the result */
if(status)
node_free(object);
free_ptr(cst);
free_ptr(map);
free_ptr(opt);
free_ptr(var);
*temp = object;
return status;
}
int evaluate_function(block_r * block, char ** expr, int start, int end, var_res * func, node * node) {
int i = 0;
int status = 0;
int arg_off = 0;
int eng_off = 0;
int arg_cnt = 0;
token_r * token = NULL;
if(start > end)
return exit_stop("invalid start and end index");
if(calloc_ptr(token))
return exit_stop("out of memory");
for(i = start; i <= end; i++)
token->script_ptr[token->script_cnt++] = expr[i];
arg_off = block->ptr_cnt;
eng_off = block->eng_cnt;
if(stack_ptr_call_(block, token, &arg_cnt)) {
status = exit_mesg("failed to parse function call syntax for item %d", block->item_id);
} else {
switch(func->id) {
case 2: status = evaluate_function_getequiprefinerycnt(block, arg_off, arg_cnt, func, node); break; /* getequiprefinerycnt */
case 3: status = evaluate_function_readparam(block, arg_off, arg_cnt, func, node); break; /* readparam */
case 4: status = evaluate_function_getskilllv(block, arg_off, arg_cnt, func, node); break; /* getskilllv */
case 5: status = evaluate_function_rand(block, arg_off, arg_cnt, func, node); break; /* rand */
case 6: status = evaluate_function_pow(block, arg_off, arg_cnt, func, node); break; /* pow */
case 8: status = evaluate_function_getiteminfo(block, arg_off, arg_cnt, func, node); break; /* getiteminfo */
case 9: status = evaluate_function_getequipid(block, arg_off, arg_cnt, func, node); break; /* getequipid */
case 10: status = evaluate_function_gettime(block, arg_off, arg_cnt, func, node); break; /* gettime */
case 13: status = evaluate_function_isequipped(block, arg_off, arg_cnt, func, node); break; /* isequipped */
case 26: status = evaluate_function_callfunc(block, arg_off, arg_cnt, func, node); break; /* callfunc */
case 29: status = evaluate_function_strcharinfo(block, arg_off, arg_cnt, func, node); break; /* strcharinfo */
case 30: status = evaluate_function_countitem(block, arg_off, arg_cnt, func, node); break; /* countitem */
case 31: status = evaluate_function_setoption(block, arg_off, arg_cnt, func, node); break; /* setoption */
case 49: status = evaluate_function_groupranditem(block, arg_off, arg_cnt, func, node); break; /* groupranditem */
default: status = exit_mesg("unsupported function '%s' in item %d", func->name, block->item_id);
}
if(!status) {
/* pop function call arguments */
for(i = eng_off; i < block->eng_cnt;)
block_stack_pop(block, TYPE_ENG);
/* pop evaluate function translations */
while(block->ptr_cnt != arg_off)
block_stack_pop(block, TYPE_PTR);
}
}
free_ptr(token);
return status;
}
/* rand takes one or two arguments; [0, max) for
* one argument and [min, max] for two arguments */
int evaluate_function_rand(block_r * block, int off, int cnt, var_res * func, node * temp) {
int status = 0;
node * min = NULL;
node * max = NULL;
switch(cnt) {
case 1: /* [0, max) */
if( is_nil(min = evaluate_expression(block, block->ptr[off], 0)) ||
rbt_range_max(min->value, &temp->max) )
status = 1;
temp->min = 0;
if(rbt_range_init(&temp->value, temp->min, temp->max, 0))
status = 1;
break;
case 2: /* [min, max] */
if( is_nil(min = evaluate_expression(block, block->ptr[off], 0)) ||
is_nil(max = evaluate_expression(block, block->ptr[off + 1], 0)) ||
rbt_range_min(min->value, &temp->min) ||
rbt_range_max(max->value, &temp->max) ||
rbt_range_init(&temp->value, temp->min, temp->max, 0) )
status = 1;
break;
default:
return exit_mesg("invalid argument count to fun"
"ction '%s' in %d", func->name, block->item_id);
}
node_free(min);
node_free(max);
return status;
}
/* groupranditem takes on a constant id or value */
int evaluate_function_groupranditem(block_r * block, int off, int cnt, var_res * func, node * temp) {
int i;
int status = 0;
int min = 0;
int group_id;
int subgroup_id;
item_group_t item_group;
if(0 >= cnt) {
return exit_mesg("invalid argument count to fu"
"nction '%s' in %d", func->name, block->item_id);
} else if(cnt == 1) {
/* subgroup id default to 1 */
if(block_stack_push(block, TYPE_PTR, "1"))
return exit_stop("failed to push default subgroup id argument");
}
memset(&item_group, 0, sizeof(item_group));
/* might have gotten carry away with the error messages */
if(evaluate_numeric_constant(block, block->ptr[off + 0], &group_id)) {
status = exit_mesg("failed to group id is not constant in item %d", block->item_id);
} else if(evaluate_numeric_constant(block, block->ptr[off + 1], &subgroup_id)) {
status = exit_mesg("failed to subgroup id is not constant in item %d", block->item_id);
} else if(item_group_id(block->script->db, &item_group, group_id, subgroup_id)) {
status = exit_mesg("failed to find group id %d and subgroup id %d", group_id, subgroup_id);
} else if(stack_eng_item_group(block, block->ptr[off + 0])) {
status = exit_mesg("failed to write group name for group id %d", group_id);
} else if(0 >= item_group.size) {
status = exit_mesg("item group id %d subgroup id %d is empty", group_id, subgroup_id);
} else {
min = item_group.item_id[0];
for(i = 1; i < item_group.size && !status; i++) {
if(item_group.item_id[i - 1] + 1 != item_group.item_id[i]) {
status = is_nil(temp->value) ?
rbt_range_init(&temp->value, min, item_group.item_id[i], 0) :
rbt_range_add(temp->value, min, item_group.item_id[i], NULL) ;
min = item_group.item_id[i];
}
}
if( status ||
is_nil(temp->value) ?
rbt_range_init(&temp->value, min, item_group.item_id[i - 1], 0) :
rbt_range_add(temp->value, min, item_group.item_id[i - 1], NULL) ) {
status = exit_stop("out of memory");
} else {
temp->formula = convert_string(block->eng[block->eng_cnt - 1]);
if(is_nil(temp->formula)) {
status = exit_stop("out of memory");
} else {
if( rbt_range_min(temp->value, &temp->min) ||
rbt_range_max(temp->value, &temp->max) )
status = exit_stop("fail to get min and max from range");
}
}
}
item_group_free(&item_group);
return status;
}
int evaluate_function_readparam(block_r * block, int off, int cnt, var_res * func, node * temp) {
int status = 0;
int unused = 0;
if(1 != cnt)
return exit_mesg("invalid argument count to fun"
"ction '%s' in %d", func->name, block->item_id);
if(stack_eng_map(block, block->ptr[off], MAP_READPARAM_FLAG, &unused)) {
status = exit_mesg("failed resolve '%s' into a paramater", block->ptr[off]);
} else {
temp->formula = convert_string(block->eng[block->eng_cnt - 1]);
if(is_nil(temp->formula))
status = exit_stop("out of memory");
temp->min = func->min;
temp->max = func->max;
if(rbt_range_init(&temp->value, temp->min, temp->max, 0))
status = exit_stop("out of memory");
}
return status;
}
int evaluate_function_getskilllv(block_r * block, int off, int cnt, var_res * func, node * temp) {
int status = 1;
int len;
int id = 0;
skill_t * skill = NULL;
if(1 != cnt)
return exit_mesg("invalid argument count to fun"
"ction '%s' in %d", func->name, block->item_id);
if(calloc_ptr(skill))
return exit_stop("out of memory");
/* search by skill name or id */
if( isalpha(block->ptr[off][0]) || '_' == block->ptr[off][0] )
status = skill_name(block->script->db, skill, block->ptr[off], (int) strlen(block->ptr[off]));
if (status &&!evaluate_numeric_constant(block, block->ptr[off], &id))
status = skill_id(block->script->db, skill, id);
if(status) {
exit_mesg("failed to resolve '%s' into a skill name", block->ptr[off]);
} else {
temp->min = 0;
temp->max = skill->maxlv;
if(rbt_range_init(&temp->value, temp->min, temp->max, 0)) {
exit_stop("out of memory");
} else {
len = (int) strlen(skill->desc) + 16;
temp->formula = calloc(len, sizeof(char));
if(is_nil(temp->formula)) {
status = exit_stop("out of memory");
} else {
snprintf(temp->formula, len, "%s Level", skill->desc);
}
}
}
free_ptr(skill);
return status;
}
int evaluate_function_isequipped(block_r * block, int off, int cnt, var_res * func, node * temp) {
int i;
int len;
int top;
int status = 0;
int unused;
if(0 == cnt)
return exit_mesg("invalid argument count to fun"
"ction '%s' in %d", func->name, block->item_id);
/* track the length and offset of the items */
len = block->arg_cnt;
top = block->eng_cnt;
for(i = 0; i < cnt && !status; i++)
if(stack_eng_item(block, block->ptr[off + i], &unused, 0))
status = exit_mesg("failed to resolve '%s' to an item name", block->ptr[off + i]);
if(!status) {
len = block->arg_cnt - len + 32;
temp->formula = calloc(len, sizeof(char));
if(is_nil(temp->formula)) {
status = exit_stop("out of memory");
} else {
for(i = top, off = 0; i < block->eng_cnt - 1; i++)
off += sprintf(&temp->formula[off], "%s, ", block->eng[i]);
switch(block->eng_cnt - top) {
case 0: sprintf(&temp->formula[off], "%s is equipped", block->eng[i]); break;
case 1: sprintf(&temp->formula[off - off ? 2 : 0], "and %s is equipped", block->eng[i]); break;
default: sprintf(&temp->formula[off], "and %s is equipped", block->eng[i]); break;
}
temp->min = 0;
temp->max = 1;
if(rbt_range_init(&temp->value, temp->min, temp->max, 0))
status = exit_stop("out of memory");
}
}
return status;
}
int evaluate_function_getequiprefinerycnt(block_r * block, int off, int cnt, var_res * func, node * temp) {
int status = 0;
int len;
int unused;
if(1 != cnt)
return exit_mesg("invalid argument count to fun"
"ction '%s' in %d", func->name, block->item_id);
len = block->arg_cnt;
if(stack_eng_map(block, block->ptr[off], MAP_REFINE_FLAG, &unused)) {
status = exit_mesg("failed to resolve '%s' to an refinement slot", block->ptr[off]);
} else {
len = block->arg_cnt - len + (int) strlen(temp->id) + 16;
temp->formula = calloc(len, sizeof(char));
if(is_nil(temp->formula)) {
status = exit_stop("out of memory");
} else {
sprintf(temp->formula, "%s's %s", block->eng[block->eng_cnt - 1], temp->id);
temp->min = func->min;
temp->max = func->max;
if(rbt_range_init(&temp->value, temp->min, temp->max, 0))
status = exit_stop("out of memory");
}
}
return status;
}
int evaluate_function_getiteminfo(block_r * block, int off, int cnt, var_res * func, node * temp) {
int status = 0;
int len;
int argc;
if(2 != cnt)
return exit_mesg("invalid argument count to fun"
"ction '%s' in %d", func->name, block->item_id);
len = block->arg_cnt;
if( stack_eng_item(block, block->ptr[off], &argc, 0) || argc != 1 ||
stack_eng_map(block, block->ptr[off + 1], MAP_ITEM_INFO_FLAG, &argc) || argc != 1 ) {
status = exit_stop("failed to evaluate getiteminfo argument into constants");
} else {
len = block->arg_cnt - len + (int) strlen(block->ptr[off + 1]) + 32;
temp->formula = calloc(len, sizeof(char));
if(is_nil(temp->formula)) {
exit_stop("out of memory");
} else {
sprintf(temp->formula, "%s's %s;%s", block->eng[block->eng_cnt - 2], block->eng[block->eng_cnt - 1], block->ptr[off + 1]);
temp->min = func->min;
temp->max = func->max;
if(rbt_range_init(&temp->value, temp->min, temp->max, 0))
status = exit_stop("out of memory");
}
}
return status;
}
int evaluate_function_getequipid(block_r * block, int off, int cnt, var_res * func, node * temp) {
int status = 0;
int unused = 0;
if(1 != cnt)
return exit_mesg("invalid argument count to fun"
"ction '%s' in %d", func->name, block->item_id);
if(stack_eng_map(block, block->ptr[off], MAP_REFINE_FLAG, &unused)) {
status = exit_mesg("failed to resolve '%s' into equip id", block->ptr[off]);
} else {
temp->formula = convert_string(block->eng[block->eng_cnt - 1]);
if(is_nil(temp->formula)) {
status = exit_stop("out of memory");
} else {
temp->min = func->min;
temp->max = func->max;
if(rbt_range_init(&temp->value, temp->min, temp->max, 0))
status = exit_stop("out of memory");
}
}
return status;
}
int evaluate_function_gettime(block_r * block, int off, int cnt, var_res * func, node * temp) {
int status = 0;
int type;
if(1 != cnt)
return exit_mesg("invalid argument count to fun"
"ction '%s' in %d", func->name, block->item_id);
if(evaluate_numeric_constant(block, block->ptr[off], &type)) {
status = exit_mesg("failed to resolve '%s' into a time type", block->ptr[off]);
} else {
if(script_map_id(block, "time_type", type, &temp->formula)) {
status = exit_mesg("failed to index 'time_type' on %d", type);
} else {
switch(type) {
case 1: temp->min = 0; temp->max = 60; break;
case 2: temp->min = 0; temp->max = 60; break;
case 3: temp->min = 0; temp->max = 24; break;
case 4: temp->min = 1; temp->max = 7; break;
case 5: temp->min = 1; temp->max = 7; break;
case 6: temp->min = 1; temp->max = 12; break;
case 7: temp->min = 0; temp->max = 9999; break;
case 8: temp->min = 0; temp->max = 365; break;
}
if(rbt_range_init(&temp->value, temp->min, temp->max, 0))
status = exit_stop("out of memory");
}
}
return status;
}
int evaluate_function_callfunc(block_r * block, int off, int cnt, var_res * func, node * temp) {
int i;
int status = 0;
int count;
node * value = NULL;
rbt_range * range = NULL;
switch(block->script->mode) {
case EATHENA:
/* <function name>, <count>, <0 to count arguments> */
if( 0 == ncs_strcmp(block->ptr[off], "F_Rand") ||
0 == ncs_strcmp(block->ptr[off], "F_RandMes") ) {
if(cnt < 2) {
status = exit_mesg("invalid argument count to func"
"tion '%s' in %d", block->ptr[off], block->item_id);
} else if(evaluate_numeric_constant(block, block->ptr[off + 1], &count)) {
status = exit_mesg("failed to evaluate '%s' into a constant", block->ptr[off + 1]);
} else if(count != cnt - 2) {
status = exit_mesg("mismatch in argument count and number of arg"
"uments in function '%s' in %d", block->ptr[off], block->item_id);
} else {
for(i = 0; i < count && !status; i++) {
value = evaluate_expression(block, block->ptr[off + 2 + i], 0);
if(is_nil(value)) {
status = exit_stop("out of memory");
} else {
if( temp->value ?
rbt_range_or(value->value, temp->value, &range) :
rbt_range_dup(value->value, &range) ) {
status = exit_stop("out of memory");
} else {
rbt_range_deit(&temp->value);
temp->value = range;
}
node_free(value);
}
}
}
} else {
status = exit_mesg("unsupported function '%s' "
"in item %d", block->ptr[off], block->item_id);
}
break;
case RATHENA:
if(0 == ncs_strcmp(block->ptr[off], "F_Rand")) {
if(cnt < 1) {
status = exit_mesg("invalid argument count to func"
"tion '%s' in %d", block->ptr[off], block->item_id);
} else {
for(i = 1; i < cnt && !status; i++) {
value = evaluate_expression(block, block->ptr[off + i], 0);
if(is_nil(value)) {
status = exit_stop("out of memory");
} else {
if( temp->value ?
rbt_range_or(value->value, temp->value, &range) :
rbt_range_dup(value->value, &range) ) {
status = exit_stop("out of memory");
} else {
rbt_range_deit(&temp->value);
temp->value = range;
}
node_free(value);
}
}
}
}
break;
default:
status = exit_mesg("unsupported mode for functio"
"n '%s' in item %d", func->name, block->item_id);
}
if(!status) {
temp->formula = convert_string("random");
if( is_nil(temp->formula) ||
rbt_range_min(temp->value, &temp->min) ||
rbt_range_max(temp->value, &temp->max) )
status = exit_stop("out of memory");
}
return 0;
}
int evaluate_function_countitem(block_r * block, int off, int cnt, var_res * func, node * temp) {
int status = 0;
int len;
int unused;
if(cnt != 1)
return exit_mesg("invalid argument count to fun"
"ction '%s' in %d", func->name, block->item_id);
len = block->arg_cnt;
if(stack_eng_item(block, block->ptr[off], &unused, 0)) {
status = exit_mesg("failed to resolve '%s' into an item name", block->ptr[off]);
} else {
len = block->arg_cnt - len + 16;
temp->formula = calloc(len, sizeof(char));
if(is_nil(temp->formula)) {
status = exit_stop("out of memory");
} else {
sprintf(temp->formula, "%s Count", block->eng[block->eng_cnt - 1]);
temp->min = func->min;
temp->max = func->max;
if(rbt_range_init(&temp->value, temp->min, temp->max, 0))
status = exit_stop("out of memory");
}
}
return status;
}
int evaluate_function_pow(block_r * block, int off, int cnt, var_res * func, node * temp) {
int status = 0;
node * base = NULL;
node * expo = NULL;
if(cnt != 2)
return exit_mesg("invalid argument count to fun"
"ction '%s' in %d", func->name, block->item_id);
base = evaluate_expression(block, block->ptr[off], 0);
if(is_nil(base)) {
status = exit_mesg("failed to evaluate '%s' i"
"n item %d", block->ptr[off], block->item_id);
} else {
expo = evaluate_expression(block, block->ptr[off + 1], 0);
if(is_nil(expo)) {
status = exit_mesg("failed to evaluate '%s' in "
"item %d", block->ptr[off + 1], block->item_id);
} else {
temp->min = (int) pow(base->min, expo->min);
temp->max = (int) pow(base->max, expo->max);
if(rbt_range_init(&temp->value, temp->min, temp->max, 0))
status = exit_stop("out of memory");
}
}
node_free(base);
node_free(expo);
return status;
}
int evaluate_function_strcharinfo(block_r * block, int off, int cnt, var_res * func, node * temp) {
int status = 0;
int len;
int unused;
if(cnt != 1 && cnt != 2)
return exit_mesg("invalid argument count to fun"
"ction '%s' in %d", func->name, block->item_id);
len = block->arg_cnt;
if(stack_eng_map(block, block->ptr[off], MAP_STRCHARINFO_FLAG, &unused)) {
status = exit_mesg("failed to evaluate '%s' "
"into a strcharinfo string", block->ptr[off]);
} else {
len = block->arg_cnt - len;
temp->formula = calloc(len, sizeof(char));
if(is_nil(temp->formula)) {
status = exit_stop("out of memory");
} else {
sprintf(temp->formula, "%s", block->eng[block->eng_cnt - 1]);
temp->min = func->min;
temp->max = func->max;
if(rbt_range_init(&temp->value, temp->min, temp->max, 0))
status = exit_stop("out of memory");
}
}
return status;
}
int evaluate_function_setoption(block_r * block, int off, int cnt, var_res * func, node * temp) {
int status = 0;
int len = 0;
if(cnt != 1)
return exit_mesg("invalid argument count to fun"
"ction '%s' in %d", func->name, block->item_id);
len = block->arg_cnt;
if(stack_eng_options(block, block->ptr[off])) {
status = exit_mesg("failed to evaluate '%s"
"' into an option string", block->ptr[off]);
} else {
len = block->arg_cnt - len;
temp->formula = calloc(len, sizeof(char));
if(is_nil(temp->formula)) {
status = exit_stop("out of memory");
} else {
sprintf(temp->formula, "%s", block->eng[block->eng_cnt - 1]);
temp->min = func->min;
temp->max = func->max;
if(rbt_range_init(&temp->value, temp->min, temp->max, 0))
status = exit_stop("out of memory");
}
}
return status;
}
int node_init(script_t * script, node ** result) {
node * object = NULL;
if(is_nil(script->free_nodes)) {
if(calloc_ptr(object))
return exit_stop("out of memory");
object->script = script;
} else {
if(is_last(script->free_nodes)) {
object = script->free_nodes;
script->free_nodes = NULL;
} else {
object = script->free_nodes;
script->free_nodes = object->next;
node_remove(object);
}
}
object->next = object;
object->prev = object;
*result = object;
return 0;
}
int node_deit(script_t * script, node ** result) {
node * object = *result;
rbt_logic_deit(&object->logic);
rbt_range_deit(&object->value);
free_ptr(object->formula);
free_ptr(object->id);
memset(object, 0, sizeof(node));
object->script = script;
object->next = object;
object->prev = object;
if(is_nil(script->free_nodes)) {
script->free_nodes = object;
} else {
node_append(script->free_nodes, object);
}
*result = NULL;
return 0;
}
int node_copy(node * source, node * target) {
if( is_nil(source) ||
is_nil(target) )
return 1;
target->var = 0;
target->type = source->type;
target->op = source->op;
target->return_type = source->return_type;
if(is_ptr(source->value))
if( rbt_range_dup(source->value, &target->value) ||
rbt_range_min(target->value, &target->min) ||
rbt_range_max(target->value, &target->max) )
return 1;
if(is_ptr(source->logic))
if(rbt_logic_copy(&target->logic, source->logic))
return 1;
target->logic_count = source->logic_count;
if(is_ptr(source->formula)) {
target->formula = convert_string(source->formula);
if(is_nil(target->formula))
return 1;
}
return 0;
}
int node_eval_tree(node * root) {
int i = 0;
int j = 0;
node * iter = NULL;
/* operator precedence tree */
static int op_pred[11][5] = {
{'*' ,'/' ,'\0' ,'\0' ,'\0'},
{'+' ,'-' ,'\0' ,'\0' ,'\0'},
{'<' ,'<' + '=' ,'>' ,'>' + '=' ,'\0'},
{'&' ,'\0' ,'\0' ,'\0' ,'\0'},
{'|' ,'\0' ,'\0' ,'\0' ,'\0'},
{'=' + '=' ,'!' + '=' ,'\0' ,'\0' ,'\0'},
{'&' + '&' ,'\0' ,'\0' ,'\0' ,'\0'},
{'|' + '|' ,'\0' ,'\0' ,'\0' ,'\0'},
{':' ,'\0' ,'\0' ,'\0' ,'\0'},
{'?' ,'\0' ,'\0' ,'\0' ,'\0'},
{'=' ,'\0' ,'\0' ,'\0' ,'\0'}
};
/* structure unary operators */
iter = root;
do {
if(iter->type == NODE_TYPE_UNARY) {
/* unary operator (forward) expansion */
iter->left = iter->next;
iter->right = iter->next;
/* realign doubly linked list */
iter->next->next->prev = iter;
iter->next = iter->next->next;
}
iter = iter->next;
} while(iter != root);
/* structure binary operators */
for(i = 0; i < 11; i++) {
iter = root;
do {
if(iter->type == NODE_TYPE_OPERATOR) {
for(j = 0; j < 5; j++) {
if(iter->op == op_pred[i][j] && op_pred[i][j] != '\0') {
/* binary operator vertical expansion */
iter->left = iter->prev;
iter->right = iter->next;
/* realign doubly linked list */
iter->prev->prev->next = iter;
iter->next->next->prev = iter;
iter->next = iter->next->next;
iter->prev = iter->prev->prev;
}
}
}
iter = iter->next;
} while(iter != root);
}
return 0;
}
/* node_eval uses the post-order traversal of a binary tree;
* evaluate the left expression, then the right expression,
* and then the current expression.
*
* to get a real good understanding of how this algorithm works,
* simply draw a few examples and work through it by hand; which
* was how the algorithm was developed. !D */
int node_eval(node * node, FILE * stm, rbt_logic * logic_tree, rbt_tree * id_tree, int flag) {
int status = 0;
rbt_logic * logic = NULL;
rbt_logic * clone = NULL;
rbt_range * limit = NULL;
if(node->left == node->right) {
/* unary operator has one expression
* (operator) (expression) */
if(is_ptr(node->left))
if(node_eval(node->left, stm, logic_tree, id_tree, flag))
return exit_mesg("failed on node %p", node);
} else {
/* binary operators has two expression
* (left-expression) (operator) (right-expression) */
/* : operator node's left child is the (true) expression
* and the node's right child is the (false) expression */
if(node->type == NODE_TYPE_OPERATOR && node->op == ':' && is_ptr(logic_tree)) {
/* (condition) ? (true) : (false)
*
* explanation
* a logic tree represents a tree of conditions and each condition
* represents a range of values for a variable or function; (true)
* uses the logic tree generated from (condition) and (false) uses
* an inverse of the logic tree generated from (condition).
*
* example
* (getrefine() < 5) ? getrefine() + 5 : getrefine() - 5;
*
* (condition)
* getrefine(0 - 4) ; original logic tree use by (true)
* getrefine(5 - 15) ; inverse logic tree use by (false)
*
* (true)
* getrefine() + 5
* (0 - 4) + 5
* (5 - 9)
*
* (false)
* getrefine() - 5
* (5 - 15) - 5
* (0 - 10)
*
* (result)
* 0 ~ 10 (refine rate)
*/
/* (true) original logic tree */
if(is_ptr(node->left))
if(node_eval(node->left, stm, logic_tree, id_tree, flag))
return exit_mesg("failed on node %p", node);
/* (false) inverse logic tree */
if(rbt_logic_not_all(logic_tree, &logic))
return exit_mesg("failed on node %p", node);
if(is_ptr(node->right))
if(node_eval(node->right, stm, logic, id_tree, flag))
status = 1;
rbt_logic_deit(&logic);
if(status)
return status;
/* normal traversal
* evaluate the left expression before the right expression */
} else {
if(is_ptr(node->left))
if(node_eval(node->left, stm, logic_tree, id_tree, flag))
return exit_mesg("failed on node %p", node);
/* ? operator node's left child is the (condition) expression and
* the node's right child is the (true) : (false) expression */
if(node->type == NODE_TYPE_OPERATOR && node->op == '?' && is_ptr(node->left->logic)) {
/* stack the (condition) on the existing logic tree;
* the search algorithm will search for variable and
* function from top to bottom */
if(rbt_logic_copy(&logic, node->left->logic))
return exit_mesg("failed on node %p", node);
if(logic_tree) {
if(rbt_logic_copy(&clone, logic_tree))
return exit_mesg("failed on node %p", node);
rbt_logic_append(logic, clone);
}
/* evaluate the (true) : (false) expression */
if(node->right != NULL)
if(node_eval(node->right, stm, logic, id_tree, flag))
status = 1;
/* keep the logic tree of ? conditional */
node->logic = logic;
if(status)
return status;
} else {
if(node->right != NULL)
if(node_eval(node->right, stm, logic_tree, id_tree, flag))
return SCRIPT_FAILED;
}
}
}
if( node->type & (NODE_TYPE_FUNCTION | NODE_TYPE_VARIABLE) ) {
/* handle functions and variables */
if(is_nil(node->id))
return exit_mesg("failed on node %p", node);
/* search for predicate limit of variable or function */
if(is_ptr(logic_tree)) {
if(!rbt_logic_search(logic_tree, node->id, &limit, and)) {
rbt_range_deit(&node->value);
node->value = limit;
}
}
if(rbt_logic_init(&node->logic, node->formula ? node->formula : node->id, node->value, node->var))
return exit_mesg("failed on node %p", node);
} else if(node->type & NODE_TYPE_UNARY) {
/* handle unary operators */
if( is_nil(node->left) ||
is_nil(node->left->value) )
return exit_mesg("failed on node %p", node);
/* evaluate the unary operator */
switch(node->op) {
case '!': rbt_range_not(node->left->value, &node->value); break;
case '-': rbt_range_neg(node->left->value, &node->value); break;
default: return SCRIPT_FAILED;
}
if(node_inherit(node))
return exit_mesg("failed on node %p", node);
node->logic_count = node->left->logic_count;
/* handle binary operator nodes */
} else if(node->type & NODE_TYPE_OPERATOR) {
/* error on invalid nodes */
if( is_nil(node->left) ||
is_nil(node->right) ||
is_nil(node->left->value) ||
is_nil(node->right->value) )
return exit_mesg("failed on node %p", node);
/* calculate range for operator node */
switch(node->op) {
case '*':
case '/':
case '+':
case '-':
case '&':
case '|':
if( rbt_range_op(node->left->value, node->right->value, &node->value, node->op) ||
node_inherit(node) )
return exit_mesg("failed on node %p", node);
break;
case '<' + '=':
case '<':
case '>' + '=':
case '>':
case '!' + '=':
case '=' + '=':
status = (flag & EVALUATE_FLAG_EXPR_BOOL) ?
rbt_range_op(node->left->value, node->right->value, &node->value, node->op) :
rbt_range_init(&node->value, 0, 1, 0);
if(status || node_inherit(node))
return exit_mesg("failed on node %p", node);
break;
case '&' + '&':
if(flag & EVALUATE_FLAG_EXPR_BOOL) {
if(rbt_range_and(node->left->value, node->right->value, &node->value))
return exit_mesg("failed on node %p", node);
if(node->left->logic && node->right->logic)
if(rbt_logic_op(node->left->logic, node->right->logic, &node->logic, and))
return exit_mesg("failed on node %p", node);
} else {
if(rbt_range_init(&node->value, 0, 1, 0))
return exit_mesg("failed on node %p", node);
}
break;
case '|' + '|':
if(flag & EVALUATE_FLAG_EXPR_BOOL) {
if(rbt_range_or(node->left->value, node->right->value, &node->value))
return exit_mesg("failed on node %p", node);
if(node->left->logic && node->right->logic)
if(rbt_logic_op(node->left->logic, node->right->logic, &node->logic, or))
return exit_mesg("failed on node %p", node);
} else {
if(rbt_range_init(&node->value, 0, 1, 0))
return exit_mesg("failed on node %p", node);
}
break;
case ':':
/* iterable use the ? operator to handle the for condition */
if(rbt_range_or(node->left->value, node->right->value, &node->value))
return exit_mesg("failed on node %p", node);
break;
case '?':
/* right node is : operator */
if(rbt_range_dup(node->right->value, &node->value))
return exit_mesg("failed on node %p", node);
break;
default:
return exit_mesg("failed on node %p", node);
}
node->logic_count = node->left->logic_count + node->right->logic_count;
}
if( rbt_range_min(node->value, &node->min) ||
rbt_range_max(node->value, &node->max) )
return exit_mesg("failed on node %p", node);
/* dump the node list at each step;
* used only for debugging and you
* need to set the global node_dbg
* stream */
node_dump(node, stm);
return 0;
}
int node_inherit(node * temp) {
int status = 0;
node * copy = NULL;
if(is_nil(temp))
return 0;
/* inherit only from left or right but not both;
* predciates cannot be accurately interpreted */
if( is_ptr(temp->left->logic) && is_nil(temp->right->logic) ) {
copy = temp->left;
} else if( is_nil(temp->left->logic) && is_ptr(temp->right->logic) ) {
copy = temp->right;
} else if( temp->left == temp->right && is_ptr(temp->left->logic) ) {
copy = temp->left;
} else if( is_ptr(temp->left->logic) && is_ptr(temp->right->logic) ) {
if(rbt_logic_op(temp->left->logic, temp->right->logic, &temp->logic, and))
status = exit_stop("failed to merge logic tree");
}
if(copy) {
if(rbt_logic_copy(&temp->logic, copy->logic)) {
status = exit_stop("out of memory");
} else if(copy->formula) {
temp->formula = convert_string(copy->formula);
if(is_nil(temp->formula)) {
status = exit_stop("out of memory");
} else if(var == copy->logic->type) {
rbt_range_deit(&temp->logic->range);
if (rbt_range_dup(temp->value, &temp->logic->range))
status = exit_stop("out of memory");
}
}
if(status)
exit_stop("failed to copy logic tree");
}
/* inherit the return type */
temp->return_type |= temp->left->return_type;
temp->return_type |= temp->right->return_type;
return status;
}
void node_dump(node * node, FILE * stream) {
if (is_nil(stream) ||
is_nil(node))
return;
fprintf(stream," -- Node [%p] --\n ", (void *) node);
switch(node->type) {
case NODE_TYPE_OPERATOR:
switch(node->op) {
case '<' + '=': fprintf(stream,"type: operator <=\n"); break;
case '>' + '=': fprintf(stream,"type: operator >=\n"); break;
case '!' + '=': fprintf(stream,"type: operator !=\n"); break;
case '=' + '=': fprintf(stream,"type: operator ==\n"); break;
case '&' + '&': fprintf(stream,"type: operator &&\n"); break;
case '|' + '|': fprintf(stream,"type: operator ||\n"); break;
default: fprintf(stream,"type: operator %c\n", node->op); break;
}
break;
case NODE_TYPE_UNARY: fprintf(stream,"type: operator %c\n", node->op); break;
case NODE_TYPE_OPERAND: fprintf(stream,"type: literal %d:%d\n", node->min, node->max); break;
case NODE_TYPE_FUNCTION: fprintf(stream,"type: function %s; %d:%d\n", node->id, node->min, node->max); break;
case NODE_TYPE_VARIABLE: fprintf(stream,"type: variable %s; %d:%d\n", node->id, node->min, node->max); break;
case NODE_TYPE_LOCAL: fprintf(stream,"type: local %s; %d:%d\n", node->id, node->min, node->max); break;
case NODE_TYPE_CONSTANT: fprintf(stream,"type: constant %s; %d:%d\n", node->id, node->min, node->max); break;
case NODE_TYPE_SUB: fprintf(stream,"type: subexpr %s; %d:%d\n", node->formula, node->min, node->max); break;
default: fprintf(stream,"type: %d\n", node->op); break;
}
fprintf(stream,
" variable: %d\n"
" formula: %s\n"
"return type: %d\n",
node->var,
node->formula,
node->return_type);
rbt_range_dump(node->value, "Range: ");
rbt_logic_dump(node->logic);
}
int node_release(script_t * script) {
node * object;
if( is_nil(script) ||
is_nil(script->free_nodes) )
return 1;
while(!is_last(script->free_nodes)) {
object = script->free_nodes->next;
node_remove(object);
free_ptr(object);
}
free_ptr(script->free_nodes);
return 0;
}
int node_append(node * p, node * c) {
p->next->prev = c->prev;
c->prev->next = p->next;
p->next = c;
c->prev = p;
return CHECK_PASSED;
}
int node_remove(node * p) {
p->prev->next = p->next;
p->next->prev = p->prev;
p->next = p;
p->prev = p;
return CHECK_PASSED;
}
int script_generate_or(block_r * block, rbt_logic * logic) {
int status = 0;
block->script->count = 0;
switch(logic->type) {
case or: status = (logic->l && script_generate_or(block, logic->l)) ||
(logic->r && script_generate_or(block, logic->r)); break;
case and: status = script_generate_vararg(block->script, "[") ||
script_generate_and(block, logic) ||
script_generate_vararg(block->script, "]\n"); break;
case var: status = script_generate_vararg(block->script, "[") ||
script_generate_var(block, logic) ||
script_generate_vararg(block->script, "]\n"); break;
}
return status;
}
int script_generate_and(block_r * block, rbt_logic * logic) {
int status = 0;
switch(logic->l->type) {
case and: status = script_generate_and(block, logic->l); break;
case var: status = script_generate_var(block, logic->l); break;
default: status = 1;
}
if(!status) {
switch(logic->r->type) {
case and: status = script_generate_and(block, logic->r); break;
case var: status = script_generate_var(block, logic->r); break;
default: status = 1;
}
}
return status;
}
int script_generate_var(block_r * block, rbt_logic * logic) {
int status = 0;
if(block->script->count)
script_generate_vararg(block->script, ", ");
switch(logic->id) {
case 1: /* getrefine */
case 2: /* getequiprefinerycnt */
case 3: /* readparam */
case 4: /* getskilllv */
case 5: /* random */
case 6: /* pow */
case 10: /* gettime */
case 17: /* job level */
case 18: /* base level */
case 19: /* max hp */
case 23: /* hp */
case 24: /* zeny */
case 26: /* callfunc */
case 27: /* getrandgroupitem */
case 30: /* countitem */
case 46: /* max */
case 47: /* min */
case 49: /* groupranditem */
status = script_generate_write_range(block, logic);
break;
case 13: /* isequipped */
case 28: /* getpartnerid */
case 31: /* checkoption */
case 32: /* checkfalcon */
case 33: /* checkmadogear */
case 34: /* upper */
case 48: /* checkmount */
status = script_generate_vararg(block->script, "%s", logic->name);
break;
case 20: /* baseclass */
case 21: /* basejob */
case 22: /* class */
status = script_generate_write_class(block, logic);
break;
case 8: /* getiteminfo */
status = script_generate_write_getiteminfo(block, logic);
break;
case 9: /* getequipid */
status = script_generate_write_getequipid(block, logic);
break;
case 29: /* strcharinfo */
status = script_generate_write_strcharinfo(block, logic);
break;
default:
/*status = exit_mesg("variable or function id %d is n"
"ot supported in item %d", logic->id, block->item_id);*/
break;
}
block->script->count++;
return status;
}
int script_generate_vararg(script_t * script, const char * format, ...) {
va_list vararg;
/* to-do: buffer overflow check */
va_start(vararg, format);
script->offset += vsnprintf(&script->buffer[script->\
offset], BUF_SIZE - script->offset, format, vararg);
va_end(vararg);
return 0;
}
int script_generate_write_range(block_r * block, rbt_logic * logic) {
int min = 0;
int max = 0;
return rbt_range_min(logic->range, &min) ||
rbt_range_max(logic->range, &max) ||
(min == max) ?
script_generate_vararg(block->script, "%s %d", logic->name, min) :
script_generate_vararg(block->script, "%s %d ~ %d", logic->name, min, max) ;
}
static int script_generate_write_class_work(struct rbt_node * node, void * context, int flag) {
int i;
struct range * range = node->val;
struct work * work = context;
char * value = NULL;
for(i = range->min; i <= range->max && work->count < work->total; i++, work->count++) {
if(script_map_id(work->block, "job_type", i, &value)) {
return exit_mesg("failed to find class or job "
"id %d in item id %d", i, work->block->item_id);
} else {
if ((work->count == 0) ?
script_generate_vararg(work->search, "%s", value) :
script_generate_vararg(work->search, ", %s", value))
return exit_mesg("failed to write class or job string '%s'", value);
free_ptr(value);
}
}
return 0;
}
int script_generate_write_class(block_r * block, rbt_logic * logic) {
struct work work;
work.block = block;
work.count = 0;
work.total = MAX_STR_LIST;
work.search = block->script;
return rbt_range_work(logic->range, script_generate_write_class_work, &work);
}
static int script_generate_write_strcharinfo_work(struct rbt_node * node, void * context, int flag) {
int i;
int status = 0;
map_res * map = NULL;
struct work * work = context;
struct range * range = node->val;
if(calloc_ptr(map))
return exit_stop("out of memory");
i = range->min;
while(i <= range->max && work->count < work->total && !status) {
if(map_id(work->block->script->db, map, i)) {
status = exit_mesg("failed to find map id %"
"d in item id %d", i, work->block->item_id);
} else {
status = (work->count == 0) ?
script_generate_vararg(work->search, "%s", map->name) :
script_generate_vararg(work->search, ", %s", map->name);
}
work->count++;
i++;
}
free_ptr(map);
return status;
}
int script_generate_write_strcharinfo(block_r * block, rbt_logic * logic) {
struct work work;
if(0 == strcmp("map", logic->name)) {
work.block = block;
work.count = 0;
work.total = MAX_STR_LIST;
work.search = block->script;
return script_generate_vararg(block->script, "On map ") ||
rbt_range_work(logic->range, script_generate_write_strcharinfo_work, &work);
}
return script_generate_vararg(block->script, "%s", logic->name);
}
static int script_generate_write_getequipid_work(struct rbt_node * node, void * context, int flag) {
int i;
int status = 0;
item_t * item = NULL;
struct work * work = context;
struct range * range = node->val;
if(calloc_ptr(item))
return exit_stop("out of memory");
i = range->min;
while(i <= range->max && work->count < work->total && !status) {
if(item_id(work->block->script->db, item, i)) {
status = exit_mesg("failed to search for item id %d\n", i);
} else {
status = (work->count == 0) ?
script_generate_vararg(work->search, "%s", item->name) :
script_generate_vararg(work->search, ", %s", item->name);
}
work->count++;
i++;
}
free_ptr(item);
return status;
}
int script_generate_write_getequipid(block_r * block, rbt_logic * logic) {
struct work work;
work.block = block;
work.count = 0;
work.total = MAX_STR_LIST;
work.search = block->script;
return rbt_range_work(logic->range, script_generate_write_getequipid_work, &work) ||
script_generate_vararg(block->script, " equipped on %s", logic->name);
}
static int script_generate_write_getiteminfo_work(struct rbt_node * node, void * context, int flag) {
int i;
int status = 0;
char * value;
struct work * work = context;
struct range * range = node->val;
i = range->min;
while(i <= range->max && work->count < work->total && !status) {
value = NULL;
if( (work->flag & MAP_GENDER_FLAG && !script_map_id(work->block, "gender", i, &value)) ||
(work->flag & MAP_ITEM_FLAG && !script_map_id(work->block, "item_type", i, &value)) ||
(work->flag & MAP_LOCATION_FLAG && !script_map_id(work->block, "item_location", i, &value)) ||
(work->flag & MAP_WEAPON_FLAG && !script_map_id(work->block, "weapon_type", i, &value)) ) {
if(script_generate_vararg(work->search, (work->count == 0) ? "%s" : ", %s", value))
status = exit_mesg("failed to write map value str"
"ing %s in item %d", value, work->block->item_id);
}
if(is_nil(value))
status = exit_mesg("failed to map value %d on flag %"
"d in item %d", i, work->flag, work->block->item_id);
free_ptr(value);
work->count++;
i++;
}
return 0;
}
int script_generate_write_getiteminfo(block_r * block, rbt_logic * logic) {
int i;
int len;
int min;
int max;
int type;
struct work work;
/* get the type expression */
len = (int) strlen(logic->name);
for(i = 0; i < len; i++)
if(logic->name[i] == ';') {
logic->name[i] = '\0';
break;
}
/* get the type integer */
if( i >= len ||
block_stack_push(block, TYPE_PTR, &logic->name[i + 1]) ||
evaluate_numeric_constant(block, block->ptr[block->ptr_cnt - 1], &type) ||
block_stack_pop(block, TYPE_PTR) )
return exit_mesg("failed to get getiteminfo type for item %d\n", block->item_id);
if(script_generate_vararg(block->script, "%s is ", logic->name))
return exit_mesg("failed to write getiteminfo string for item %d\n", block->item_id);
work.block = block;
work.count = 0;
work.total = MAX_STR_LIST;
work.search = block->script;
switch (type) {
case 9: return rbt_range_min(logic->range, &min) ||
rbt_range_max(logic->range, &max) ||
script_generate_vararg(block->script, "%s %d ~ %d", logic->name, min, max);
case 2: work.flag = MAP_ITEM_FLAG; break; /* item type */
case 4: work.flag = MAP_GENDER_FLAG; break; /* gender */
case 5: work.flag = MAP_LOCATION_FLAG; break; /* equip location */
case 11: work.flag = MAP_WEAPON_FLAG; break; /* weapon type */
default: return exit_mesg("unsupported item info %d for "
"getiteminfo in item %d", type, block->item_id);
}
return rbt_range_work(logic->range, script_generate_write_getiteminfo_work, &work);
}