* Follow up 51074a0: Remove ugly fixed array size! It increases memory usage too much!

* A little optimization on skill_db for skill requirement

Signed-off-by: Cydh Ramdh <house.bad@gmail.com>
This commit is contained in:
Cydh Ramdh 2014-01-08 22:14:36 +07:00
parent 14a096a5a2
commit 5143c4c36f
4 changed files with 186 additions and 109 deletions

View File

@ -160,8 +160,8 @@ unsigned short itemdb_searchrandomid(int group_id, uint8 sub_group)
ShowError("itemdb_searchrandomid: Invalid sub_group %d\n", sub_group+1);
return UNKNOWN_ITEM_ID;
}
if (itemgroup_db[group_id].random_qty[sub_group])
return itemgroup_db[group_id].random[sub_group][rnd()%itemgroup_db[group_id].random_qty[sub_group]].nameid;
if (&itemgroup_db[group_id].random[sub_group] && itemgroup_db[group_id].random[sub_group].data_qty)
return itemgroup_db[group_id].random[sub_group].data[rand()%itemgroup_db[group_id].random[sub_group].data_qty].nameid;
ShowError("itemdb_searchrandomid: No item entries for group id %d and sub group %d\n", group_id, sub_group+1);
return UNKNOWN_ITEM_ID;
@ -187,9 +187,11 @@ uint16 itemdb_get_randgroupitem_count(uint16 group_id, uint8 sub_group, uint16 n
ShowError("itemdb_get_randgroupitem_count: Invalid sub_group id %d\n", group_id+1);
return amt;
}
ARR_FIND(0,itemgroup_db[group_id].random_qty[sub_group],i,itemgroup_db[group_id].random[sub_group][i].nameid == nameid);
if (i < MAX_ITEMGROUP_RAND)
amt = itemgroup_db[group_id].random[sub_group][i].amount;
if (!(&itemgroup_db[group_id].random[sub_group]) || !itemgroup_db[group_id].random[sub_group].data_qty)
return amt;
ARR_FIND(0,itemgroup_db[group_id].random[sub_group].data_qty,i,itemgroup_db[group_id].random[sub_group].data[i].nameid == nameid);
if (i < itemgroup_db[group_id].random[sub_group].data_qty)
amt = itemgroup_db[group_id].random[sub_group].data[i].amount;
return amt;
}
@ -251,23 +253,23 @@ char itemdb_pc_get_itemgroup(uint16 group_id, struct map_session_data *sd) {
}
//Get the 'must' item(s)
for (i = 0; i < itemgroup_db[group_id].must_qty; i++) {
if (&itemgroup_db[group_id].must[i] && itemdb_exists(itemgroup_db[group_id].must[i].nameid))
itemdb_pc_get_itemgroup_sub(sd,group_id,&itemgroup_db[group_id].must[i]);
}
if (itemgroup_db[group_id].must_qty)
for (i = 0; i < itemgroup_db[group_id].must_qty; i++)
if (&itemgroup_db[group_id].must[i] && itemdb_exists(itemgroup_db[group_id].must[i].nameid))
itemdb_pc_get_itemgroup_sub(sd,group_id,&itemgroup_db[group_id].must[i]);
//Get the 'random' item each random group
for (i = 0; i < MAX_ITEMGROUP_RANDGROUP; i++) {
uint16 rand;
if (!itemgroup_db[group_id].random_qty[i]) //Skip empty random group
if (!(&itemgroup_db[group_id].random[i]) || !itemgroup_db[group_id].random[i].data_qty) //Skip empty random group
continue;
rand = rnd()%itemgroup_db[group_id].random_qty[i];
rand = rnd()%itemgroup_db[group_id].random[i].data_qty;
//Woops, why is the data empty? Every check should be done when load the item group! So this is bad day for the player :P
if (!&itemgroup_db[group_id].random[i][rand] || !itemgroup_db[group_id].random[i][rand].nameid) {
if (!&itemgroup_db[group_id].random[i].data[rand] || !itemgroup_db[group_id].random[i].data[rand].nameid) {
continue;
}
if (itemdb_exists(itemgroup_db[group_id].random[i][rand].nameid))
itemdb_pc_get_itemgroup_sub(sd,group_id,&itemgroup_db[group_id].random[i][rand]);
if (itemdb_exists(itemgroup_db[group_id].random[i].data[rand].nameid))
itemdb_pc_get_itemgroup_sub(sd,group_id,&itemgroup_db[group_id].random[i].data[rand]);
}
return 0;
@ -282,8 +284,8 @@ int itemdb_group_bonus(struct map_session_data* sd, int itemid)
for (i=0; i < MAX_ITEMGROUP; i++) {
if (!sd->itemgrouphealrate[i])
continue;
ARR_FIND( 0, itemgroup_db[i].random_qty[0], j, itemgroup_db[i].random[0][j].nameid == itemid );
if( j < itemgroup_db[i].random_qty[0] )
ARR_FIND(0,itemgroup_db[i].random[0].data_qty,j,itemgroup_db[i].random[0].data[j].nameid == itemid );
if( j < itemgroup_db[i].random[0].data_qty )
bonus += sd->itemgrouphealrate[i];
}
return bonus;
@ -659,9 +661,10 @@ static void itemdb_read_itemgroup_sub(const char* filename, bool silent)
while (fgets(line,sizeof(line),fp)) {
uint16 nameid;
int j, group_id, prob = 1, amt = 1, group = 1, announced = 0, dur = 0, named = 0, bound = 0;
int j, group_id, prob = 1, amt = 1, rand_group = 1, announced = 0, dur = 0, named = 0, bound = 0;
char *str[3], *p, w1[1024], w2[1024];
bool found = false;
struct s_item_group_random *random;
ln++;
if (line[0] == '/' && line[1] == '/')
@ -678,7 +681,7 @@ static void itemdb_read_itemgroup_sub(const char* filename, bool silent)
for (j = 0, p = line; j < 3 && p;j++) {
str[j] = p;
if (j == 2)
sscanf(str[j],"%d,%d,%d,%d,%d,%d,%d",&prob,&amt,&group,&announced,&dur,&named,&bound);
sscanf(str[j],"%d,%d,%d,%d,%d,%d,%d",&prob,&amt,&rand_group,&announced,&dur,&named,&bound);
p = strchr(p,',');
if (p) *p++=0;
}
@ -691,7 +694,8 @@ static void itemdb_read_itemgroup_sub(const char* filename, bool silent)
}
//Checking group_id
if (atoi(str[0]))
trim(str[0]);
if (ISDIGIT(str[0][0]))
group_id = atoi(str[0]);
else //Try reads group id by const
script_get_constant(trim(str[0]),&group_id);
@ -701,13 +705,14 @@ static void itemdb_read_itemgroup_sub(const char* filename, bool silent)
}
//Checking sub group
if (group > MAX_ITEMGROUP_RANDGROUP) {
ShowWarning("itemdb_read_itemgroup: Invalid sub group %d for group id %d in %s:%d\n", group, group_id, filename, ln);
if (rand_group > MAX_ITEMGROUP_RANDGROUP) {
ShowWarning("itemdb_read_itemgroup: Invalid sub group %d for group id %d in %s:%d\n", rand_group, group_id, filename, ln);
continue;
}
//Checking item
if ((nameid = atoi(str[1])) && itemdb_exists(nameid))
trim(str[1]);
if (ISDIGIT(str[1][0]) && itemdb_exists((nameid = atoi(str[1]))))
found = true;
else if (itemdb_searchname(str[1])) {
found = true;
@ -718,20 +723,18 @@ static void itemdb_read_itemgroup_sub(const char* filename, bool silent)
continue;
}
//Checking the capacity
if ((group && itemgroup_db[group_id].random_qty[group-1]+prob >= MAX_ITEMGROUP_RAND) ||
(!group && itemgroup_db[group_id].must_qty+1 >= MAX_ITEMGROUP_MUST))
{
ShowWarning("itemdb_read_itemgroup: Group id %d is overflow (%d entries) in %s:%d\n", group_id, (!group) ? MAX_ITEMGROUP_MUST : MAX_ITEMGROUP_RAND, filename, ln);
continue;
}
amt = cap_value(amt,1,MAX_AMOUNT);
dur = cap_value(dur,0,UINT16_MAX);
bound = cap_value(bound,0,4);
if (!group) {
//Must item, place it here
if (!rand_group) {
uint16 idx = itemgroup_db[group_id].must_qty;
if (!idx)
CREATE(itemgroup_db[group_id].must,struct s_item_group,1);
else
RECREATE(itemgroup_db[group_id].must,struct s_item_group,idx+1);
itemgroup_db[group_id].must[idx].nameid = nameid;
itemgroup_db[group_id].must[idx].amount = amt;
itemgroup_db[group_id].must[idx].isAnnounced = announced;
@ -739,25 +742,34 @@ static void itemdb_read_itemgroup_sub(const char* filename, bool silent)
itemgroup_db[group_id].must[idx].isNamed = named;
itemgroup_db[group_id].must[idx].bound = bound;
itemgroup_db[group_id].must_qty++;
group = 1;
rand_group = 1;
}
prob = max(prob,0);
//Must item didn't set as random item, skip next process
if (!prob) {
entries++;
continue;
}
group -= 1;
for (j = 0; j < prob; j++) {
uint16 idx;
idx = itemgroup_db[group_id].random_qty[group];
itemgroup_db[group_id].random[group][idx].nameid = nameid;
itemgroup_db[group_id].random[group][idx].amount = amt;
itemgroup_db[group_id].random[group][idx].isAnnounced = announced;
itemgroup_db[group_id].random[group][idx].duration = dur;
itemgroup_db[group_id].random[group][idx].isNamed = named;
itemgroup_db[group_id].random[group][idx].bound = bound;
itemgroup_db[group_id].random_qty[group]++;
rand_group -= 1;
random = &itemgroup_db[group_id].random[rand_group];
//Check, if the entry for this random group already created or not
if (!random->data_qty) {
CREATE(random->data,struct s_item_group,prob);
random->data_qty = 0;
}
else
RECREATE(random->data,struct s_item_group,random->data_qty+prob);
//Now put the entry to its rand_group
for (j = random->data_qty; j < random->data_qty+prob; j++) {
random->data[j].nameid = nameid;
random->data[j].amount = amt;
random->data[j].isAnnounced = announced;
random->data[j].duration = dur;
random->data[j].isNamed = named;
random->data[j].bound = bound;
}
random->data_qty += prob;
entries++;
}
fclose(fp);
@ -1621,6 +1633,19 @@ void itemdb_reload(void)
if( itemdb_array[i] )
destroy_item_data(itemdb_array[i], true);
for (i = 0; i < MAX_ITEMGROUP; i++) {
uint8 j;
if (!(&itemgroup_db[i]))
continue;
if (itemgroup_db[i].must_qty)
aFree(itemgroup_db[i].must);
for (j = 0; j < MAX_ITEMGROUP_RANDGROUP; j++) {
if (!(&itemgroup_db[i].random[j]) || !itemgroup_db[i].random[j].data_qty)
continue;
aFree(itemgroup_db[i].random[j].data);
}
}
itemdb_other->clear(itemdb_other, itemdb_final_sub);
db_clear(itemdb_combo);
@ -1687,6 +1712,19 @@ void do_final_itemdb(void)
if( itemdb_array[i] )
destroy_item_data(itemdb_array[i], true);
for (i = 0; i < MAX_ITEMGROUP; i++) {
uint8 j;
if (!(&itemgroup_db[i]))
continue;
if (itemgroup_db[i].must_qty)
aFree(itemgroup_db[i].must);
for (j = 0; j < MAX_ITEMGROUP_RANDGROUP; j++) {
if (!(&itemgroup_db[i].random[j]) || !itemgroup_db[i].random[j].data_qty)
continue;
aFree(itemgroup_db[i].random[j].data);
}
}
itemdb_other->destroy(itemdb_other, itemdb_final_sub);
destroy_item_data(&dummy_item, false);
db_destroy(itemdb_combo);
@ -1697,7 +1735,7 @@ int do_init_itemdb(void) {
itemdb_other = idb_alloc(DB_OPT_BASE);
itemdb_combo = idb_alloc(DB_OPT_BASE);
create_dummy_data(); //Dummy data item.
itemdb_read();
return 0;
}

View File

@ -26,10 +26,8 @@
#define IG_FINDINGORE 6
#define IG_POTION 37
#define MAX_ITEMGROUP 390 ///The max. item group count (increase this when needed).
#define MAX_ITEMGROUP 390 ///The max. item group count (increase this when needed). TODO: Remove this limit and use dynamic allocaton
#define MAX_ITEMGROUP_RAND 11000 ///Max item slots for random item group (increase this when needed).
#define MAX_ITEMGROUP_MUST 15 ///Max item for 'must' item group (increase this when needed).
#define MAX_ITEMGROUP_RANDGROUP 4 ///Max group for random item (increase this when needed).
#define CARD0_FORGE 0x00FF
@ -310,6 +308,14 @@ enum e_item_job {
ITEMJ_THIRD_BABY = 0x20,
};
struct item_combo {
struct script_code *script;
unsigned short *nameid;/* nameid array */
unsigned char count;
unsigned short id;/* id of this combo */
bool isRef;/* whether this struct is a reference or the master */
};
struct item_data {
uint16 nameid;
char name[ITEM_NAME_LENGTH],jname[ITEM_NAME_LENGTH];
@ -373,7 +379,7 @@ struct item_data {
unsigned char combos_count;
};
/* Struct of item group */
/* Struct of item group entry */
struct s_item_group {
uint16 nameid, ///item id
duration; ///duration if item as rental item
@ -383,19 +389,17 @@ struct s_item_group {
char bound; ///makes the item as bound item (according to bound type)
};
/* Struct of item group that will be used for db */
struct s_item_group_db {
struct s_item_group must[MAX_ITEMGROUP_MUST];
struct s_item_group random[MAX_ITEMGROUP_RANDGROUP][MAX_ITEMGROUP_RAND]; ///NOTE: Is this good?
uint16 must_qty, random_qty[MAX_ITEMGROUP_RANDGROUP];
/* Struct of random group */
struct s_item_group_random {
struct s_item_group *data;
uint16 data_qty;
};
struct item_combo {
struct script_code *script;
unsigned short *nameid;/* nameid array */
unsigned char count;
unsigned short id;/* id of this combo */
bool isRef;/* whether this struct is a reference or the master */
/* Struct of item group that will be used for db */
struct s_item_group_db {
struct s_item_group *must;
uint16 must_qty;
struct s_item_group_random random[MAX_ITEMGROUP_RANDGROUP]; //! TODO: Move this fixed array to dynamic allocation!
};
struct item_data* itemdb_searchname(const char *name);

View File

@ -225,7 +225,7 @@ int skill_get_weapontype( uint16 skill_id ) { skill_get (skill_d
int skill_get_ammotype( uint16 skill_id ) { skill_get (skill_db[skill_id].require.ammo, skill_id); }
int skill_get_ammo_qty( uint16 skill_id, uint16 skill_lv ) { skill_get2 (skill_db[skill_id].require.ammo_qty[skill_lv-1], skill_id, skill_lv); }
int skill_get_state( uint16 skill_id ) { skill_get (skill_db[skill_id].require.state, skill_id); }
int skill_get_status( uint16 skill_id, int idx ) { skill_get3 (skill_db[skill_id].require.status[idx], skill_id, idx); }
//int skill_get_status( uint16 skill_id, int idx ) { skill_get3 (skill_db[skill_id].require.status[idx], skill_id, idx); }
int skill_get_status_count( uint16 skill_id ) { skill_get (skill_db[skill_id].require.status_count, skill_id); }
int skill_get_spiritball( uint16 skill_id, uint16 skill_lv ){ skill_get2 (skill_db[skill_id].require.spiritball[skill_lv-1], skill_id, skill_lv); }
int skill_get_itemid( uint16 skill_id, int idx ) { skill_get3 (skill_db[skill_id].require.itemid[idx], skill_id, idx); }
@ -14053,15 +14053,17 @@ int skill_check_condition_castbegin(struct map_session_data* sd, uint16 skill_id
}
//check if equiped item
for (i = 0; i < MAX_SKILL_EQUIP_REQUIRE; i++) {
int reqeqit = require.eqItem[i];
if(!reqeqit) break; //no more required item get out of here
if (!pc_checkequip2(sd,reqeqit,EQI_ACC_L,EQI_MAX)) {
char output[128];
//clif_skill_fail(sd, skill_id, USESKILL_FAIL_NEED_EQUIPMENT, reqeqit);
sprintf(output,"need to put on [%d] in order to use.",reqeqit);
clif_colormes(sd,color_table[COLOR_RED],output);
return 0;
if (require.eqItem_count) {
for (i = 0; i < require.eqItem_count; i++) {
int reqeqit = require.eqItem[i];
if(!reqeqit) break; //no more required item get out of here
if (!pc_checkequip2(sd,reqeqit,EQI_ACC_L,EQI_MAX)) {
char output[128];
//clif_skill_fail(sd, skill_id, USESKILL_FAIL_NEED_EQUIPMENT, reqeqit);
sprintf(output,"need to put on [%d] in order to use.",reqeqit);
clif_colormes(sd,color_table[COLOR_RED],output);
return 0;
}
}
}
@ -14453,10 +14455,9 @@ struct skill_condition skill_get_requirement(struct map_session_data* sd, uint16
}
req.status_count = skill_db[idx].require.status_count;
memset(req.status,SC_NONE,sizeof(req.status));
memcpy(req.status,skill_db[idx].require.status,sizeof(skill_db[idx].require.status));
memset(req.eqItem,0,sizeof(req.eqItem));
memcpy(req.eqItem,skill_db[idx].require.eqItem,sizeof(skill_db[idx].require.eqItem));
req.status = skill_db[idx].require.status;
req.eqItem_count = skill_db[idx].require.eqItem_count;
req.eqItem = skill_db[idx].require.eqItem;
for( i = 0; i < MAX_SKILL_ITEM_REQUIRE; i++ ) {
if( (skill_id == AM_POTIONPITCHER || skill_id == CR_SLIMPITCHER || skill_id == CR_CULTIVATION) && i != skill_lv%11 - 1 )
@ -14643,10 +14644,10 @@ struct skill_condition skill_get_requirement(struct map_session_data* sd, uint16
if (req_opt&0x0040) req.weapon = 0;
if (req_opt&0x0080) { req.ammo = 0; req.ammo_qty = 0; }
if (req_opt&0x0100) req.state = ST_NONE;
if (req_opt&0x0200) memset(req.status,SC_NONE,sizeof(req.status));
if (req_opt&0x0200) req.status_count = 0;
if (req_opt&0x0400) req.spiritball = 0;
if (req_opt&0x0800) { memset(req.itemid,0,sizeof(req.itemid)); memset(req.amount,0,sizeof(req.amount)); }
if (req_opt&0x1000) memset(req.eqItem,0,sizeof(req.eqItem));
if (req_opt&0x1000) req.eqItem_count = 0;
}
return req;
@ -18553,6 +18554,44 @@ static bool skill_parse_row_skilldb(char* split[], int columns, int current)
return true;
}
/** Split string to int or constanta value (const.txt)
* @param *str: String input
* @param *val: Temporary storage
* @param *delim: Delimiter (for multiple value support)
* @param useConst: 'true' uses const.txt as reference, 'false' uses atoi()
* @param min: Min value of each const. Example: SC has min value SC_NONE (-1), so the value that less or equal won't be counted
* @return count: Number of success
*/
uint8 skill_split2(char *str, int *val, const char *delim, bool useConst, short min) {
uint8 i = 0;
char *p = strtok(str,delim);
while (p != NULL) {
int n = -1;
if (useConst)
script_get_constant(trim(p),&n);
else
n = atoi(p);
if (n > min) {
val[i] = n;
i++;
}
p = strtok(NULL,delim);
}
return i;
}
/// Clear status data from skill requirement
static void skill_destroy_requirement(void) {
uint16 i;
for (i = 0; i < MAX_SKILL; i++) {
if (skill_db[i].require.status_count)
aFree(skill_db[i].require.status);
if (skill_db[i].require.eqItem_count)
aFree(skill_db[i].require.eqItem);
}
}
/**
* Read skill requirement from skill_require_db.txt
**/
@ -18618,42 +18657,36 @@ static bool skill_parse_row_requiredb(char* split[], int columns, int current)
else if( strcmpi(split[10],"elementalspirit") == 0 ) skill_db[idx].require.state = ST_ELEMENTALSPIRIT;
else if( strcmpi(split[10],"peco") == 0 ) skill_db[idx].require.state = ST_PECO;
else skill_db[idx].require.state = ST_NONE; // Unknown or no state
//Status requirements
memset(skill_db[idx].require.status,SC_NONE,sizeof(skill_db[idx].require.status));
skill_db[idx].require.status_count = 0;
p = strtok(split[11],":");
for( i = 0; i < MAX_SKILL_STATUS_REQUIRE && p != NULL; i++ ) {
int status = SC_NONE;
script_get_constant(trim(p),&status);
if (status > SC_NONE) {
skill_db[idx].require.status[skill_db[idx].require.status_count] = (enum sc_type)status;
skill_db[idx].require.status_count++;
trim(split[11]);
if (split[11][0] != '\0') {
int require[MAX_SKILL_STATUS_REQUIRE];
if ((skill_db[idx].require.status_count = skill_split2(split[11],require,":",true,SC_NONE))) {
skill_db[idx].require.status = aMalloc(skill_db[idx].require.status_count * sizeof(sc_type));
for (i = 0; i < skill_db[idx].require.status_count; i++)
skill_db[idx].require.status[i] = (sc_type)require[i];
}
p = strtok(NULL,":");
}
skill_split_atoi(split[12],skill_db[idx].require.spiritball);
for( i = 0; i < MAX_SKILL_ITEM_REQUIRE; i++ ) {
skill_db[idx].require.itemid[i] = atoi(split[13+ 2*i]);
skill_db[idx].require.amount[i] = atoi(split[14+ 2*i]);
}
//require equiped
memset(skill_db[idx].require.eqItem,0,sizeof(skill_db[idx].require.eqItem));
p = strtok(split[33],":");
for( i = 0; i < MAX_SKILL_EQUIP_REQUIRE && p != NULL; i++ ) {
int itid = atoi(p);
p = strtok(NULL,":"); //for easy continue don't read 'p' after this
if(itid <= 0) continue; //silent
if(itemdb_exists(itid)== NULL) {
ShowWarning("Invalid reqIt=%d specified for skillid=%d\n",itid,skill_id);
continue; //invalid id
//Equipped Item requirements.
//NOTE: We don't check the item is exist or not here
trim(split[33]);
if (split[33][0] != '\0') {
int require[MAX_SKILL_EQUIP_REQUIRE];
if ((skill_db[idx].require.eqItem_count = skill_split2(split[33],require,":",false,501))) {
skill_db[idx].require.eqItem = aMalloc(skill_db[idx].require.eqItem_count * sizeof(short));
for (i = 0; i < skill_db[idx].require.eqItem_count; i++)
skill_db[idx].require.eqItem[i] = require[i];
}
skill_db[idx].require.eqItem[i] = itid;
}
return true;
}
@ -19046,6 +19079,7 @@ static void skill_readdb(void) {
void skill_reload (void) {
struct s_mapiterator *iter;
struct map_session_data *sd;
skill_destroy_requirement();
skill_readdb();
/* lets update all players skill tree : so that if any skill modes were changed they're properly updated */
iter = mapit_getallusers();
@ -19083,6 +19117,7 @@ int do_init_skill (void)
int do_final_skill(void)
{
skill_destroy_requirement();
db_destroy(skilldb_name2id);
db_destroy(group_db);
db_destroy(skillunit_db);

View File

@ -119,10 +119,10 @@ struct skill_condition {
state,
spiritball,
itemid[MAX_SKILL_ITEM_REQUIRE],
amount[MAX_SKILL_ITEM_REQUIRE],
eqItem[MAX_SKILL_EQUIP_REQUIRE]; //max eq_item
uint8 status_count;
enum sc_type status[MAX_SKILL_STATUS_REQUIRE];
amount[MAX_SKILL_ITEM_REQUIRE];
short *eqItem;
enum sc_type *status;
uint8 status_count, eqItem_count;
};
struct s_skill_require {
@ -138,10 +138,10 @@ struct s_skill_require {
state,
spiritball[MAX_SKILL_LEVEL],
itemid[MAX_SKILL_ITEM_REQUIRE],
amount[MAX_SKILL_ITEM_REQUIRE],
eqItem[MAX_SKILL_EQUIP_REQUIRE]; //max eq_item
uint8 status_count;
enum sc_type status[MAX_SKILL_STATUS_REQUIRE];
amount[MAX_SKILL_ITEM_REQUIRE];
short *eqItem;
enum sc_type *status;
uint8 status_count, eqItem_count;
};
/// Database skills
@ -334,7 +334,7 @@ int skill_get_weapontype( uint16 skill_id );
int skill_get_ammotype( uint16 skill_id );
int skill_get_ammo_qty( uint16 skill_id, uint16 skill_lv );
int skill_get_state(uint16 skill_id);
int skill_get_status( uint16 skill_id, int idx );
//int skill_get_status( uint16 skill_id, int idx );
int skill_get_status_count( uint16 skill_id );
int skill_get_spiritball( uint16 skill_id, uint16 skill_lv );
int skill_get_itemid( uint16 skill_id, int idx );