From d2a04ea3158792e2fa30b69a903ea6cf2d349289 Mon Sep 17 00:00:00 2001 From: Smokexyz Date: Sat, 23 Apr 2016 01:21:26 +0800 Subject: SkillDB TXT to Libconfig + converter The conversion of 5 skill database files - skill_require_db.txt skill_unit_db.txt skill_cast_db.txt skill_castnodex_db.txt and skill_db.txt to Libconfig file skill_db.conf a PHP CLI converter is included to read from the above mentioned files and create a skill_db.conf in the same directory. --- src/map/skill.c | 1528 +++++++++++++++++++++++++++++++++++++------- src/map/skill.h | 25 +- tools/skilldbconverter.php | 1141 +++++++++++++++++++++++++++++++++ 3 files changed, 2457 insertions(+), 237 deletions(-) create mode 100644 tools/skilldbconverter.php diff --git a/src/map/skill.c b/src/map/skill.c index 31be52ae3..fb0d54e23 100644 --- a/src/map/skill.c +++ b/src/map/skill.c @@ -54,6 +54,7 @@ #include "common/strlib.h" #include "common/timer.h" #include "common/utils.h" +#include "common/conf.h" #include #include @@ -18798,220 +18799,6 @@ void skill_cooldown_load(struct map_session_data * sd) { } } -/*========================================== - * sub-function of DB reading. - * skill_db.txt - *------------------------------------------*/ -bool skill_parse_row_skilldb(char* split[], int columns, int current) { -// id,range,hit,inf,element,nk,splash,max,list_num,castcancel,cast_defence_rate,inf2,maxcount,skill_type,blow_count,name,description - uint16 skill_id = atoi(split[0]); - uint16 idx; - if( (skill_id >= GD_SKILLRANGEMIN && skill_id <= GD_SKILLRANGEMAX) - || (skill_id >= HM_SKILLRANGEMIN && skill_id <= HM_SKILLRANGEMAX) - || (skill_id >= MC_SKILLRANGEMIN && skill_id <= MC_SKILLRANGEMAX) - || (skill_id >= EL_SKILLRANGEMIN && skill_id <= EL_SKILLRANGEMAX) ) { - ShowWarning("skill_parse_row_skilldb: Skill id %d is forbidden (interferes with guild/homun/mercenary skill mapping)!\n", skill_id); - return false; - } - - idx = skill->get_index(skill_id); - if( !idx ) // invalid skill id - return false; - - skill->dbs->db[idx].nameid = skill_id; - skill->split_atoi(split[1],skill->dbs->db[idx].range); - skill->dbs->db[idx].hit = atoi(split[2]); - skill->dbs->db[idx].inf = atoi(split[3]); - skill->split_atoi(split[4],skill->dbs->db[idx].element); - skill->dbs->db[idx].nk = (int)strtol(split[5], NULL, 0); - skill->split_atoi(split[6],skill->dbs->db[idx].splash); - skill->dbs->db[idx].max = atoi(split[7]); - skill->split_atoi(split[8],skill->dbs->db[idx].num); - - if( strcmpi(split[9],"yes") == 0 ) - skill->dbs->db[idx].castcancel = 1; - else - skill->dbs->db[idx].castcancel = 0; - skill->dbs->db[idx].cast_def_rate = atoi(split[10]); - skill->dbs->db[idx].inf2 = (int)strtol(split[11], NULL, 0); - skill->split_atoi(split[12],skill->dbs->db[idx].maxcount); - if( strcmpi(split[13],"weapon") == 0 ) - skill->dbs->db[idx].skill_type = BF_WEAPON; - else if( strcmpi(split[13],"magic") == 0 ) - skill->dbs->db[idx].skill_type = BF_MAGIC; - else if( strcmpi(split[13],"misc") == 0 ) - skill->dbs->db[idx].skill_type = BF_MISC; - else - skill->dbs->db[idx].skill_type = 0; - skill->split_atoi(split[14],skill->dbs->db[idx].blewcount); - safestrncpy(skill->dbs->db[idx].name, trim(split[15]), sizeof(skill->dbs->db[idx].name)); - safestrncpy(skill->dbs->db[idx].desc, trim(split[16]), sizeof(skill->dbs->db[idx].desc)); - strdb_iput(skill->name2id_db, skill->dbs->db[idx].name, skill_id); - script->set_constant2(skill->dbs->db[idx].name, (int)skill_id, false, false); - - return true; -} - -bool skill_parse_row_requiredb(char* split[], int columns, int current) { -// skill_id,HPCost,MaxHPTrigger,SPCost,HPRateCost,SPRateCost,ZenyCost,RequiredWeapons,RequiredAmmoTypes,RequiredAmmoAmount,RequiredState,SpiritSphereCost,RequiredItemID1,RequiredItemAmount1,RequiredItemID2,RequiredItemAmount2,RequiredItemID3,RequiredItemAmount3,RequiredItemID4,RequiredItemAmount4,RequiredItemID5,RequiredItemAmount5,RequiredItemID6,RequiredItemAmount6,RequiredItemID7,RequiredItemAmount7,RequiredItemID8,RequiredItemAmount8,RequiredItemID9,RequiredItemAmount9,RequiredItemID10,RequiredItemAmount10 - char* p; - int j; - - uint16 skill_id = atoi(split[0]); - uint16 idx = skill->get_index(skill_id); - if( !idx ) // invalid skill id - return false; - - skill->split_atoi(split[1],skill->dbs->db[idx].hp); - skill->split_atoi(split[2],skill->dbs->db[idx].mhp); - skill->split_atoi(split[3],skill->dbs->db[idx].sp); - skill->split_atoi(split[4],skill->dbs->db[idx].hp_rate); - skill->split_atoi(split[5],skill->dbs->db[idx].sp_rate); - skill->split_atoi(split[6],skill->dbs->db[idx].zeny); - - //Which weapon type are required, see doc/item_db for types - p = split[7]; - for( j = 0; j < 32; j++ ) { - int l = atoi(p); - if( l == 99 ) { // Any weapon - skill->dbs->db[idx].weapon = 0; - break; - } else - skill->dbs->db[idx].weapon |= 1<dbs->db[idx].ammo = 0xFFFFFFFF; - break; - } else if( l ) // 0 stands for no requirement - skill->dbs->db[idx].ammo |= 1<split_atoi(split[9],skill->dbs->db[idx].ammo_qty); - - if( strcmpi(split[10],"hiding") == 0 ) skill->dbs->db[idx].state = ST_HIDING; - else if( strcmpi(split[10],"cloaking") == 0 ) skill->dbs->db[idx].state = ST_CLOAKING; - else if( strcmpi(split[10],"hidden") == 0 ) skill->dbs->db[idx].state = ST_HIDDEN; - else if( strcmpi(split[10],"riding") == 0 ) skill->dbs->db[idx].state = ST_RIDING; - else if( strcmpi(split[10],"falcon") == 0 ) skill->dbs->db[idx].state = ST_FALCON; - else if( strcmpi(split[10],"cart") == 0 ) skill->dbs->db[idx].state = ST_CART; - else if( strcmpi(split[10],"shield") == 0 ) skill->dbs->db[idx].state = ST_SHIELD; - else if( strcmpi(split[10],"sight") == 0 ) skill->dbs->db[idx].state = ST_SIGHT; - else if( strcmpi(split[10],"explosionspirits") == 0 ) skill->dbs->db[idx].state = ST_EXPLOSIONSPIRITS; - else if( strcmpi(split[10],"cartboost") == 0 ) skill->dbs->db[idx].state = ST_CARTBOOST; - else if( strcmpi(split[10],"recover_weight_rate") == 0 ) skill->dbs->db[idx].state = ST_RECOV_WEIGHT_RATE; - else if( strcmpi(split[10],"move_enable") == 0 ) skill->dbs->db[idx].state = ST_MOVE_ENABLE; - else if( strcmpi(split[10],"water") == 0 ) skill->dbs->db[idx].state = ST_WATER; - else if( strcmpi(split[10],"dragon") == 0 ) skill->dbs->db[idx].state = ST_RIDINGDRAGON; - else if( strcmpi(split[10],"warg") == 0 ) skill->dbs->db[idx].state = ST_WUG; - else if( strcmpi(split[10],"ridingwarg") == 0 ) skill->dbs->db[idx].state = ST_RIDINGWUG; - else if( strcmpi(split[10],"mado") == 0 ) skill->dbs->db[idx].state = ST_MADO; - else if( strcmpi(split[10],"elementalspirit") == 0 ) skill->dbs->db[idx].state = ST_ELEMENTALSPIRIT; - else if( strcmpi(split[10],"poisonweapon") == 0 ) skill->dbs->db[idx].state = ST_POISONINGWEAPON; - else if( strcmpi(split[10],"rollingcutter") == 0 ) skill->dbs->db[idx].state = ST_ROLLINGCUTTER; - else if( strcmpi(split[10],"mh_fighting") == 0 ) skill->dbs->db[idx].state = ST_MH_FIGHTING; - else if( strcmpi(split[10],"mh_grappling") == 0 ) skill->dbs->db[idx].state = ST_MH_GRAPPLING; - else if( strcmpi(split[10],"peco") == 0 ) skill->dbs->db[idx].state = ST_PECO; - /** - * Unknown or no state - **/ - else skill->dbs->db[idx].state = ST_NONE; - - skill->split_atoi(split[11],skill->dbs->db[idx].spiritball); - for( j = 0; j < MAX_SKILL_ITEM_REQUIRE; j++ ) { - skill->dbs->db[idx].itemid[j] = atoi(split[12+ 2*j]); - skill->dbs->db[idx].amount[j] = atoi(split[13+ 2*j]); - } - - return true; -} - -bool skill_parse_row_castdb(char* split[], int columns, int current) { -// skill_id,CastingTime,AfterCastActDelay,AfterCastWalkDelay,Duration1,Duration2 - uint16 skill_id = atoi(split[0]); - uint16 idx = skill->get_index(skill_id); - if( !idx ) // invalid skill id - return false; - - skill->split_atoi(split[1],skill->dbs->db[idx].cast); - skill->split_atoi(split[2],skill->dbs->db[idx].delay); - skill->split_atoi(split[3],skill->dbs->db[idx].walkdelay); - skill->split_atoi(split[4],skill->dbs->db[idx].upkeep_time); - skill->split_atoi(split[5],skill->dbs->db[idx].upkeep_time2); - skill->split_atoi(split[6],skill->dbs->db[idx].cooldown); -#ifdef RENEWAL_CAST - skill->split_atoi(split[7],skill->dbs->db[idx].fixed_cast); -#endif - return true; -} - -bool skill_parse_row_castnodexdb(char* split[], int columns, int current) { -// Skill id,Cast,Delay (optional) - uint16 skill_id = atoi(split[0]); - uint16 idx = skill->get_index(skill_id); - if( !idx ) // invalid skill id - return false; - - skill->split_atoi(split[1],skill->dbs->db[idx].castnodex); - if( split[2] ) // optional column - skill->split_atoi(split[2],skill->dbs->db[idx].delaynodex); - - return true; -} - -bool skill_parse_row_unitdb(char* split[], int columns, int current) { -// ID,unit ID,unit ID 2,layout,range,interval,target,flag - uint16 skill_id = atoi(split[0]); - uint16 idx = skill->get_index(skill_id); - if( !idx ) // invalid skill id - return false; - - skill->dbs->db[idx].unit_id[0] = (int)strtol(split[1],NULL,16); - skill->dbs->db[idx].unit_id[1] = (int)strtol(split[2],NULL,16); - skill->split_atoi(split[3],skill->dbs->db[idx].unit_layout_type); - skill->split_atoi(split[4],skill->dbs->db[idx].unit_range); - skill->dbs->db[idx].unit_interval = atoi(split[5]); - - if( strcmpi(split[6],"noenemy")==0 ) skill->dbs->db[idx].unit_target = BCT_NOENEMY; - else if( strcmpi(split[6],"friend")==0 ) skill->dbs->db[idx].unit_target = BCT_NOENEMY; - else if( strcmpi(split[6],"party")==0 ) skill->dbs->db[idx].unit_target = BCT_PARTY; - else if( strcmpi(split[6],"ally")==0 ) skill->dbs->db[idx].unit_target = BCT_PARTY|BCT_GUILD; - else if( strcmpi(split[6],"guild")==0 ) skill->dbs->db[idx].unit_target = BCT_GUILD; - else if( strcmpi(split[6],"all")==0 ) skill->dbs->db[idx].unit_target = BCT_ALL; - else if( strcmpi(split[6],"enemy")==0 ) skill->dbs->db[idx].unit_target = BCT_ENEMY; - else if( strcmpi(split[6],"self")==0 ) skill->dbs->db[idx].unit_target = BCT_SELF; - else if( strcmpi(split[6],"sameguild")==0 ) skill->dbs->db[idx].unit_target = BCT_GUILD|BCT_SAMEGUILD; - else if( strcmpi(split[6],"noone")==0 ) skill->dbs->db[idx].unit_target = BCT_NOONE; - else skill->dbs->db[idx].unit_target = (int)strtol(split[6],NULL,16); - - skill->dbs->db[idx].unit_flag = (int)strtol(split[7],NULL,16); - - if (skill->dbs->db[idx].unit_flag&UF_DEFNOTENEMY && battle_config.defnotenemy) - skill->dbs->db[idx].unit_target = BCT_NOENEMY; - - //By default, target just characters. - skill->dbs->db[idx].unit_target |= BL_CHAR; - if (skill->dbs->db[idx].unit_flag&UF_NOPC) - skill->dbs->db[idx].unit_target &= ~BL_PC; - if (skill->dbs->db[idx].unit_flag&UF_NOMOB) - skill->dbs->db[idx].unit_target &= ~BL_MOB; - if (skill->dbs->db[idx].unit_flag&UF_SKILL) - skill->dbs->db[idx].unit_target |= BL_SKILL; - - return true; -} - bool skill_parse_row_producedb(char* split[], int columns, int current) { // ProduceItemID,ItemLV,RequireSkill,Requireskill_lv,MaterialID1,MaterialAmount1,...... int x,y; @@ -19180,6 +18967,1276 @@ bool skill_parse_row_changematerialdb(char* split[], int columns, int current) { return true; } +#define skilldb_duplicate_warning(name, setting, skill) (ShowError("skill_read_skilldb: Duplicate entry '%s' in setting '%s' for Skill Id %d in '%s', skipping...\n", name, setting, skill, "db/"DBPATH"skill_db.conf")) +#define skilldb_invalid_error(name, setting, skill) (ShowError("skill_read_skilldb: Invalid entry '%s' in setting '%s' for Skill Id %d in '%s', skipping...\n", name, setting, skill, "db/"DBPATH"skill_db.conf")) + +/** + * Sets Level based configuration for skill groups from skill_db.conf [ Smokexyz/Hercules ] + * @param *conf pointer to config setting. + * @param *arr pointer to array to be set. + */ +void skill_config_set_level(struct config_setting_t *conf, int *arr) +{ + int i=0; + + if (config_setting_is_group(conf)) { + for (i=0; isetting_lookup_int(conf, level, &arr[i]); + } + } else if (config_setting_is_array(conf)) { + for (i=0; isetting_get_int_elem(conf, i); + } + } else { + int val=libconfig->setting_get_int(conf); + for(i=0; isetting_lookup_string(conf, "Hit", &type)) { + if (strcmpi(type, "BDT_SKILL") == 0) { + sk->hit = BDT_SKILL; + } else if (strcmpi(type, "BDT_MULTIHIT") == 0) { + sk->hit = BDT_MULTIHIT; + } else if (strcmpi(type, "BDT_NORMAL") == 0) { + sk->hit = BDT_NORMAL; + } else { + skilldb_invalid_error(type, "Hit", sk->nameid); + return; + } + } +} + +/** + * Validates "SkillType" when reading skill_db.conf + * @param conf struct, pointer to skill configuration + * @param sk struct, pointer to s_skill_db + * @return (void) + */ +void skill_validate_skilltype(struct config_setting_t *conf, struct s_skill_db *sk) +{ + struct config_setting_t *t = NULL, *tt = NULL; + + if((t=libconfig->setting_get_member(conf, "SkillType")) && config_setting_is_group(t)) { + int j=0; + while ((tt = libconfig->setting_get_elem(t, j++))) { + const char *type = config_setting_name(tt); + bool on = libconfig->setting_get_bool_real(tt); + + if (strcmpi(type, "Enemy") == 0) { + if (on) { + sk->inf |= INF_ATTACK_SKILL; + } else { + sk->inf &= ~INF_ATTACK_SKILL; + } + } else if (strcmpi(type, "Place") == 0) { + if (on) { + sk->inf |= INF_GROUND_SKILL; + } else { + sk->inf &= ~INF_GROUND_SKILL; + } + } else if (strcmpi(type, "Self") == 0) { + if (on) { + sk->inf |= INF_SELF_SKILL; + } else { + sk->inf &= ~INF_SELF_SKILL; + } + } else if (strcmpi(type, "Friend") == 0) { + if (on) { + sk->inf |= INF_SUPPORT_SKILL; + } else { + sk->inf &= ~INF_SUPPORT_SKILL; + } + } else if (strcmpi(type, "Trap") == 0) { + if (on) { + sk->inf |= INF_TARGET_TRAP; + } else { + sk->inf &= ~INF_TARGET_TRAP; + } + } else if (strcmpi(type, "Passive") != 0) { + skilldb_invalid_error(type, config_setting_name(t), sk->nameid); + } + } + } +} + +/** + * Validates "SkillInfo" when reading skill_db.conf + * @param conf struct, pointer to skill configuration + * @param sk struct, pointer to s_skill_db + * @return (void) + */ +void skill_validate_skillinfo(struct config_setting_t *conf, struct s_skill_db *sk) +{ + struct config_setting_t *t = NULL, *tt = NULL; + + if ((t=libconfig->setting_get_member(conf, "SkillInfo")) && config_setting_is_group(t)) { + int j=0; + while ((tt = libconfig->setting_get_elem(t, j++))) { + const char *type = config_setting_name(tt); + bool on = libconfig->setting_get_bool_real(tt); + + if (strcmpi(type, "Quest") == 0) { + if (on) { + sk->inf2 |= INF2_QUEST_SKILL; + } else { + sk->inf2 &= ~INF2_QUEST_SKILL; + } + } else if (strcmpi(type, "NPC") == 0) { + if (on) { + sk->inf2 |= INF2_NPC_SKILL; + } else { + sk->inf2 &= ~INF2_NPC_SKILL; + } + } else if (strcmpi(type, "Wedding") == 0) { + if (on) { + sk->inf2 |= INF2_WEDDING_SKILL; + } else { + sk->inf2 &= ~INF2_WEDDING_SKILL; + } + } else if (strcmpi(type, "Spirit") == 0) { + if (on) { + sk->inf2 |= INF2_SPIRIT_SKILL; + } else { + sk->inf2 &= ~INF2_SPIRIT_SKILL; + } + } else if (strcmpi(type, "Guild") == 0) { + if (on) { + sk->inf2 |= INF2_GUILD_SKILL; + } else { + sk->inf2 &= ~INF2_GUILD_SKILL; + } + } else if (strcmpi(type, "Song") == 0) { + if (on) { + sk->inf2 |= INF2_SONG_DANCE; + } else { + sk->inf2 &= ~INF2_SONG_DANCE; + } + } else if (strcmpi(type, "Ensemble") == 0) { + if (on) { + sk->inf2 |= INF2_ENSEMBLE_SKILL; + } else { + sk->inf2 &= ~INF2_ENSEMBLE_SKILL; + } + } else if (strcmpi(type, "Trap") == 0) { + if (on) { + sk->inf2 |= INF2_TRAP; + } else { + sk->inf2 &= ~INF2_TRAP; + } + } else if (strcmpi(type, "TargetSelf") == 0) { + if (on) { + sk->inf2 |= INF2_TARGET_SELF; + } else { + sk->inf2 &= ~INF2_TARGET_SELF; + } + } else if (strcmpi(type, "NoCastSelf") == 0) { + if (on) { + sk->inf2 |= INF2_NO_TARGET_SELF; + } else { + sk->inf2 &= ~INF2_NO_TARGET_SELF; + } + } else if (strcmpi(type, "PartyOnly") == 0) { + if (on) { + sk->inf2 |= INF2_PARTY_ONLY; + } else { + sk->inf2 &= ~INF2_PARTY_ONLY; + } + } else if (strcmpi(type, "GuildOnly") == 0) { + if (on) { + sk->inf2 |= INF2_GUILD_ONLY; + } else { + sk->inf2 &= ~INF2_GUILD_ONLY; + } + } else if (strcmpi(type, "NoEnemy") == 0) { + if (on) { + sk->inf2 |= INF2_NO_ENEMY; + } else { + sk->inf2 &= ~INF2_NO_ENEMY; + } + } else if (strcmpi(type, "IgnoreLandProtector") == 0) { + if (on) { + sk->inf2 |= INF2_NOLP; + } else { + sk->inf2 &= ~INF2_NOLP; + } + } else if (strcmpi(type, "Chorus") == 0) { + if (on) { + sk->inf2 |= INF2_CHORUS_SKILL; + } else { + sk->inf2 &= ~INF2_CHORUS_SKILL; + } + } else if (strcmpi(type, "None") != 0) { + skilldb_invalid_error(type, config_setting_name(t), sk->nameid); + } + } + } +} + +/** + * Validates "AttackType" when reading skill_db.conf + * @param conf struct, pointer to skill configuration + * @param sk struct, pointer to s_skill_db + * @return (void) + */ +void skill_validate_attacktype(struct config_setting_t *conf, struct s_skill_db *sk) +{ + const char *type = NULL; + + if (libconfig->setting_lookup_string(conf, "AttackType", &type)) { + if (!strcmpi(type, "Weapon")) { + sk->skill_type = BF_WEAPON; + } else if (!strcmpi(type, "Magic")) { + sk->skill_type = BF_MAGIC; + } else if (!strcmpi(type, "Misc")) { + sk->skill_type = BF_MISC; + } else { + skilldb_invalid_error(type, "AttackType", sk->nameid); + return; + } + } +} + +/** + * Validates "Element" when reading skill_db.conf + * @param ele_t struct, pointer to skill configuration + * @param sk struct, pointer to s_skill_db + * @return (void) + */ +void skill_validate_element(struct config_setting_t *conf, struct s_skill_db *sk) +{ + const char *type = NULL; + struct config_setting_t *t = NULL; + + if ((t=libconfig->setting_get_member(conf, "Element")) && config_setting_is_group(t)) { + int j = 0; + char lv[5]; + + for (j=0; j < MAX_SKILL_LEVEL; j++) { + sprintf(lv, "Lv%d",j+1); + if (libconfig->setting_lookup_string(t, lv, &type)) { + if (strcmpi(type,"Ele_Weapon") == 0) + sk->element[j] = -1; + else if (strcmpi(type,"Ele_Endowed") == 0) + sk->element[j] = -2; + else if (strcmpi(type,"Ele_Random") == 0) + sk->element[j] = -3; + else if (!script->get_constant(type,&sk->element[j])) + skilldb_invalid_error(type, config_setting_name(conf), sk->nameid); + } + } + + } else if (libconfig->setting_lookup_string(conf, "Element", &type)) { + int ele = 0; + + if (strcmpi(type,"Ele_Weapon") == 0) + ele = -1; + else if (strcmpi(type,"Ele_Endowed") == 0) + ele = -2; + else if (strcmpi(type,"Ele_Random") == 0) + ele = -3; + else if (!script->get_constant(type, &ele)) { + skilldb_invalid_error(type, config_setting_name(conf), sk->nameid); + return; + } + + skill->level_set_value(sk->element, ele); + } +} + +/** + * Validates "DamageType" when reading skill_db.conf + * @param conf struct, pointer to skill configuration + * @param sk struct, pointer to s_skill_db + * @return (void) + */ +void skill_validate_damagetype(struct config_setting_t *conf, struct s_skill_db *sk) +{ + struct config_setting_t *t = NULL, *tt = NULL; + + if ((t=libconfig->setting_get_member(conf, "DamageType")) && config_setting_is_group(t)) { + int j=0; + while ((tt = libconfig->setting_get_elem(t, j++))) { + const char *type = config_setting_name(tt); + bool on = libconfig->setting_get_bool_real(tt); + + if (strcmpi(type, "NoDamage") == 0) { + if (on) { + sk->nk |= NK_NO_DAMAGE; + } else { + sk->nk &= ~NK_NO_DAMAGE; + } + } else if (strcmpi(type, "SplashArea") == 0) { + if (on) { + sk->nk |= NK_SPLASH; + } else { + sk->nk &= ~NK_SPLASH; + } + } else if (strcmpi(type, "SplitDamage") == 0) { + if (on) { + sk->nk |= NK_SPLASHSPLIT; + } else { + sk->nk &= ~NK_SPLASHSPLIT; + } + } else if (strcmpi(type, "IgnoreCards") == 0) { + if (on) { + sk->nk |= NK_NO_CARDFIX_ATK; + } else { + sk->nk &= ~NK_NO_CARDFIX_ATK; + } + } else if (strcmpi(type, "IgnoreElement") == 0) { + if (on) { + sk->nk |= NK_NO_ELEFIX; + } else { + sk->nk &= ~NK_NO_ELEFIX; + } + } else if (strcmpi(type, "IgnoreDefense") == 0) { + if (on) { + sk->nk |= NK_IGNORE_DEF; + } else { + sk->nk &= ~NK_IGNORE_DEF; + } + } else if (strcmpi(type, "IgnoreFlee") == 0) { + if (on) { + sk->nk |= NK_IGNORE_FLEE; + } else { + sk->nk &= ~NK_IGNORE_FLEE; + } + } else if (strcmpi(type, "IgnoreDefCards") == 0) { + if (on) { + sk->nk |= NK_NO_CARDFIX_DEF; + } else { + sk->nk &= ~NK_NO_CARDFIX_DEF; + } + } else { + skilldb_invalid_error(type, config_setting_name(t), sk->nameid); + } + } + } +} + +/** + * Validates "SkillCast/DelayOptions" when reading skill_db.conf + * @param conf struct, pointer to skill configuration + * @param sk struct, pointer to s_skill_db + * @param delay boolean, switch for cast/delay setting + * @return (void) + */ +void skill_validate_castnodex(struct config_setting_t *conf, struct s_skill_db *sk, bool delay) +{ + struct config_setting_t *t = NULL, *tt = NULL; + + if ((t=libconfig->setting_get_member(conf, delay?"SkillDelayOptions":"CastTimeOptions")) && config_setting_is_group(t)) { + int j = 0, tmpopt = 0; + while ((tt = libconfig->setting_get_elem(t, j++)) && j < 4) { + const char *type = config_setting_name(tt); + bool on = libconfig->setting_get_bool_real(tt); + + if (strcmpi(type, "IgnoreDex") == 0) { + if (on) { + tmpopt |= 1<<0; + } else { + tmpopt &= ~(1<<0); + } + } else if (strcmpi(type, "IgnoreStatusEffect") == 0) { + if (on) { + tmpopt |= 1<<1; + } else { + tmpopt &= ~(1<<1); + } + } else if (strcmpi(type, "IgnoreItemBonus") == 0) { + if (on) { + tmpopt |= 1<<2; + } else { + tmpopt &= ~(1<<2); + } + } else { + skilldb_invalid_error(type, config_setting_name(t), sk->nameid); + return; + } + + } + skill->level_set_value(delay?sk->delaynodex:sk->castnodex, tmpopt); + } +} + +/** + * Validates the "WeaponTypes" flag + * when parsing skill_db.conf + * @param *type const char, weapon type flag + * @param on boolean, switch for the flag + * @param *sk struct, pointer to s_skill_db + * @return void + */ +int skill_validate_weapontype_sub(const char *type, bool on, struct s_skill_db *sk ) +{ + if (strcmpi(type, "NoWeapon") == 0) { + if (on) { + sk->weapon |= 1<weapon &= ~(1<weapon |= 1<weapon &= ~(1<weapon |= 1<weapon &= ~(1<weapon |= 1<weapon &= ~(1<weapon |= 1<weapon &= ~(1<weapon |= 1<weapon &= ~(1<weapon |= 1<weapon &= ~(1<weapon |= 1<weapon &= ~(1<weapon |= 1<weapon &= ~(1<weapon |= 1<weapon &= ~(1<weapon |= 1<weapon &= ~(1<weapon |= 1<weapon &= ~(1<weapon |= 1<weapon &= ~(1<weapon |= 1<weapon &= ~(1<weapon |= 1<weapon &= ~(1<weapon |= 1<weapon &= ~(1<weapon |= 1<weapon &= ~(1<weapon |= 1<weapon &= ~(1<weapon |= 1<weapon &= ~(1<weapon |= 1<weapon &= ~(1<weapon |= 1<weapon &= ~(1<weapon |= 1<weapon &= ~(1<weapon |= 1<weapon &= ~(1<weapon |= 1<weapon &= ~(1<weapon |= 1<weapon &= ~(1<weapon |= 1<weapon &= ~(1<weapon |= 1<weapon &= ~(1<weapon |= 1<weapon &= ~(1<weapon |= 1<weapon &= ~(1<weapon |= 1<weapon &= ~(1<weapon = 0; + } else { + return 1; // invalid type + } + + return 0; +} + +/** + * Validates "WeaponTypes" + * when parsing skill_db.conf + * @param conf struct, pointer to the skill configuration + * @param sk struct, struct, pointer to s_skill_db + * @return (void) + */ +void skill_validate_weapontype(struct config_setting_t *conf, struct s_skill_db *sk) +{ + struct config_setting_t *tt = NULL; + const char *type = NULL; + + if ((tt = libconfig->setting_get_member(conf, "WeaponTypes")) && config_setting_is_group(tt)) { + int j = 0; + struct config_setting_t *wpt = { 0 }; + while ((wpt = libconfig->setting_get_elem(tt, j++)) && j < 30) { + if (skill_validate_weapontype_sub(config_setting_name(wpt), libconfig->setting_get_bool_real(wpt), sk)) + skilldb_invalid_error(config_setting_name(wpt), config_setting_name(tt), sk->nameid); + } + } else if (libconfig->setting_lookup_string(conf, "WeaponTypes", &type)) { + if (skill_validate_weapontype_sub(type, true, sk)) + skilldb_invalid_error(type, "WeaponTypes", sk->nameid); + } +} + +/** + * Validates the "AmmoTypes" flag + * when parsing skill_db.conf + * @param type string, ammo type flag + * @param on boolean, switch for the flag + * @param sk struct, pointer to s_skill_db + * @return void + */ +int skill_validate_ammotype_sub(const char *type, bool on, struct s_skill_db *sk) +{ + if (strcmpi(type, "A_ARROW") == 0) { + if (on) { + sk->ammo |= 1<ammo &= ~(1<ammo |= 1<ammo &= ~(1<ammo |= 1<ammo &= ~(1<ammo |= 1<ammo &= ~(1<ammo |= 1<ammo &= ~(1<ammo |= 1<ammo &= ~(1<ammo |= 1<ammo &= ~(1<ammo |= 1<ammo &= ~(1<ammo |= 1<ammo &= ~(1<ammo = 0xFFFFFFFF; + } else { + sk->ammo = 0; + } + } else { + return 1; // Invalid Entry + } + + return 0; +} + +/** + * Validates the "AmmoTypes" flag + * when parsing skill_db.conf + * @param conf pointer to the skill configuration + * @param sk struct, pointer to s_skill_db + * @return void + */ +void skill_validate_ammotype(struct config_setting_t *conf, struct s_skill_db *sk) +{ + struct config_setting_t *tt = NULL; + const char *tstr = NULL; + + if ((tt = libconfig->setting_get_member(conf, "AmmoTypes")) && config_setting_is_group(tt)) { + int j = 0; + struct config_setting_t *amt = { 0 }; + while ((amt = libconfig->setting_get_elem(tt, j++))) { + if (skill_validate_ammotype_sub(config_setting_name(amt), libconfig->setting_get_bool_real(amt), sk)) + skilldb_invalid_error(config_setting_name(amt), config_setting_name(tt), sk->nameid); + } + } else if( libconfig->setting_lookup_string(conf, "AmmoTypes", &tstr)) { + if (skill_validate_ammotype_sub(tstr, true, sk)) + skilldb_invalid_error(tstr, "AmmoTypes", sk->nameid); + } +} + +/** + * Validates the "State" flag + * when parsing skill_db.conf + * @param conf struct, pointer to the skill configuration + * @param sk struct, pointer to s_skill_db + * @return void + */ +void skill_validate_state(struct config_setting_t *conf, struct s_skill_db *sk) +{ + const char *type = NULL; + + if (libconfig->setting_lookup_string(conf, "State", &type) && strcmpi(type,"None") != ST_NONE) { + if ( strcmpi(type,"Hiding") == 0 ) sk->state = ST_HIDING; + else if (strcmpi(type,"Cloaking") == 0 ) sk->state = ST_CLOAKING; + else if (strcmpi(type,"Hidden") == 0 ) sk->state = ST_HIDDEN; + else if (strcmpi(type,"Riding") == 0 ) sk->state = ST_RIDING; + else if (strcmpi(type,"Falcon") == 0 ) sk->state = ST_FALCON; + else if (strcmpi(type,"Cart") == 0 ) sk->state = ST_CART; + else if (strcmpi(type,"Shield") == 0 ) sk->state = ST_SHIELD; + else if (strcmpi(type,"Sight") == 0 ) sk->state = ST_SIGHT; + else if (strcmpi(type,"ExplosionSpirits") == 0 ) sk->state = ST_EXPLOSIONSPIRITS; + else if (strcmpi(type,"CartBoost") == 0 ) sk->state = ST_CARTBOOST; + else if (strcmpi(type,"NotOverWeight") == 0 ) sk->state = ST_RECOV_WEIGHT_RATE; + else if (strcmpi(type,"Moveable") == 0 ) sk->state = ST_MOVE_ENABLE; + else if (strcmpi(type,"InWater") == 0 ) sk->state = ST_WATER; + else if (strcmpi(type,"Dragon") == 0 ) sk->state = ST_RIDINGDRAGON; + else if (strcmpi(type,"Warg") == 0 ) sk->state = ST_WUG; + else if (strcmpi(type,"RidingWarg") == 0 ) sk->state = ST_RIDINGWUG; + else if (strcmpi(type,"MadoGear") == 0 ) sk->state = ST_MADO; + else if (strcmpi(type,"ElementalSpirit") == 0 ) sk->state = ST_ELEMENTALSPIRIT; + else if (strcmpi(type,"PoisonWeapon") == 0 ) sk->state = ST_POISONINGWEAPON; + else if (strcmpi(type,"RollingCutter") == 0 ) sk->state = ST_ROLLINGCUTTER; + else if (strcmpi(type,"MH_Fighting") == 0 ) sk->state = ST_MH_FIGHTING; + else if (strcmpi(type,"MH_Grappling") == 0 ) sk->state = ST_MH_FIGHTING; + else if (strcmpi(type,"Peco") == 0 ) sk->state = ST_PECO; + else + skilldb_invalid_error(type, "State", sk->nameid); + } +} + +/** + * Validates the "Items" flag + * when parsing skill_db.conf + * @param conf struct, pointer to the skill configuration + * @param sk struct, pointer to s_skill_db + * @return void + */ +void skill_validate_item_requirements(struct config_setting_t *conf, struct s_skill_db *sk) +{ + struct config_setting_t *tt = NULL; + + if ((tt=libconfig->setting_get_member(conf, "Items")) && config_setting_is_group(conf)) { + int itx=-1; + struct config_setting_t *it; + + while((it=libconfig->setting_get_elem(tt, itx++)) && itx < MAX_SKILL_ITEM_REQUIRE) { + const char *type = config_setting_name(it); + + if( type[0] == 'I' && type[1] == 'D' && itemdb->exists(atoi(type+2)) ) + sk->itemid[itx] = atoi(type+2); + else if(!script->get_constant(type, &sk->itemid[itx])) { + ShowWarning("skill_read_skilldb: Invalid required Item '%s' given for skill Id %d in '%s', skipping...\n",type, sk->nameid, DBPATH"skill_db.conf"); + continue; + } + + sk->amount[itx] = libconfig->setting_get_int(it); + } + } +} + +/** + * Validates the "Unit > Target" flag + * when parsing skill_db.conf + * @param conf struct, pointer to the skill configuration + * @param sk struct, pointer to s_skill_db + * @return void + */ +void skill_validate_unit_target(struct config_setting_t *conf, struct s_skill_db *sk) +{ + const char *type = NULL; + + if(libconfig->setting_lookup_string(conf, "Target", &type)) { + + if(!strcmpi(type,"NotEnemy")) sk->unit_target = BCT_NOENEMY; + else if(!strcmpi(type,"NotParty")) sk->unit_target = BCT_NOPARTY; + else if (!strcmpi(type,"NotGuild")) sk->unit_target = BCT_NOGUILD; + else if(!strcmpi(type,"Friend")) sk->unit_target = BCT_NOENEMY; + else if(!strcmpi(type,"Party")) sk->unit_target = BCT_PARTY; + else if(!strcmpi(type,"Ally")) sk->unit_target = BCT_PARTY|BCT_GUILD; + else if(!strcmpi(type,"Guild")) sk->unit_target = BCT_GUILD; + else if(!strcmpi(type,"All")) sk->unit_target = BCT_ALL; + else if(!strcmpi(type,"Enemy")) sk->unit_target = BCT_ENEMY; + else if(!strcmpi(type,"Self")) sk->unit_target = BCT_SELF; + else if(!strcmpi(type,"SameGuild")) sk->unit_target = BCT_GUILD|BCT_SAMEGUILD; + + if (sk->unit_flag&UF_DEFNOTENEMY && battle_config.defnotenemy) + sk->unit_target = BCT_NOENEMY; + + //By default, target just characters. + sk->unit_target |= BL_CHAR; + + if (sk->unit_flag&UF_NOPC) + sk->unit_target &= ~BL_PC; + if (sk->unit_flag&UF_NOMOB) + sk->unit_target &= ~BL_MOB; + if (sk->unit_flag&UF_SKILL) + sk->unit_target |= BL_SKILL; + } +} + +/** + * Validates the "Unit > Flag" setting + * when parsing skill_db.conf + * @param type const char, name of the flag being parsed. + * @param on boolean, switch for flag setting + * @param sk struct, pointer to s_skill_db. + * @return (void) + */ +int skill_validate_unit_flag_sub(const char *type, bool on, struct s_skill_db *sk) +{ + if (strcmpi(type, "UF_DEFNOTENEMY") == 0) { + if (on) { + sk->unit_flag |= UF_DEFNOTENEMY; + } else { + sk->unit_flag &= ~UF_DEFNOTENEMY; + } + } else if (strcmpi(type, "UF_NOREITERATION") == 0) { + if (on) { + sk->unit_flag |= UF_NOREITERATION; + } else { + sk->unit_flag &= ~UF_NOREITERATION; + } + } else if (strcmpi(type, "UF_NOFOOTSET") == 0) { + if (on) { + sk->unit_flag |= UF_NOFOOTSET; + } else { + sk->unit_flag &= ~UF_NOFOOTSET; + } + } else if (strcmpi(type, "UF_NOOVERLAP") == 0) { + if (on) { + sk->unit_flag |= UF_NOOVERLAP; + } else { + sk->unit_flag &= ~UF_NOOVERLAP; + } + } else if (strcmpi(type, "UF_PATHCHECK") == 0) { + if (on) { + sk->unit_flag |= UF_PATHCHECK; + } else { + sk->unit_flag &= ~UF_PATHCHECK; + } + } else if (strcmpi(type, "UF_NOPC") == 0) { + if (on) { + sk->unit_flag |= UF_NOPC; + } else { + sk->unit_flag &= ~UF_NOPC; + } + } else if (strcmpi(type, "UF_NOMOB") == 0) { + if (on) { + sk->unit_flag |= UF_NOMOB; + } else { + sk->unit_flag &= ~UF_NOMOB; + } + } else if (strcmpi(type, "UF_SKILL") == 0) { + if (on) { + sk->unit_flag |= UF_SKILL; + } else { + sk->unit_flag &= ~UF_SKILL; + } + } else if (strcmpi(type, "UF_DANCE") == 0) { + if (on) { + sk->unit_flag |= UF_DANCE; + } else { + sk->unit_flag &= ~UF_DANCE; + } + } else if (strcmpi(type, "UF_ENSEMBLE") == 0) { + if (on) { + sk->unit_flag |= UF_ENSEMBLE; + } else { + sk->unit_flag &= ~UF_ENSEMBLE; + } + } else if (strcmpi(type, "UF_SONG") == 0) { + if (on) { + sk->unit_flag |= UF_SONG; + } else { + sk->unit_flag &= ~UF_SONG; + } + } else if (strcmpi(type, "UF_DUALMODE") == 0) { + if (on) { + sk->unit_flag |= UF_DUALMODE; + } else { + sk->unit_flag &= ~UF_DUALMODE; + } + } else if (strcmpi(type, "UF_RANGEDSINGLEUNIT") == 0) { + if (on) { + sk->unit_flag |= UF_RANGEDSINGLEUNIT; + } else { + sk->unit_flag &= ~UF_RANGEDSINGLEUNIT; + } + } else { + return 1; // Invalid Type + } + + return 0; +} + +/** + * Validate "Unit > Flag" setting + * when parsing skill_db.conf + * @param conf struct, pointer to the skill configuration + * @param sk struct, struct, pointer to s_skill_db + * @return (void) + */ +void skill_validate_unit_flag(struct config_setting_t *conf, struct s_skill_db *sk) +{ + struct config_setting_t *t = NULL; + + if ((t=libconfig->setting_get_member(conf, "Flag")) && config_setting_is_group(t)) { + int j=0; + struct config_setting_t *tt = NULL; + while ((tt = libconfig->setting_get_elem(t, j++))) { + const char *name = config_setting_name(tt); + + if (skill_validate_unit_flag_sub(name, libconfig->setting_get_bool_real(tt), sk)) + skilldb_invalid_error(name, config_setting_name(t), sk->nameid); + } + } +} +/** + * Validate additional field settings via plugins + * when parsing skill_db.conf + * @param conf struct, pointer to the skill configuration + * @param sk struct, struct, pointer to s_skill_db + * @return (void) + */ +void skill_validate_additional_fields(struct config_setting_t *conf, struct s_skill_db *sk) +{ + // Does nothing like a boss. *cough* plugins *cough* +} + +/** + * Validates a skill entry and adds it to the database. [ Smokexyz/Hercules ] + * @param sk contains skill data to be checked. + * @param *source filepath constant. + * @return boolean true on success. + */ +bool skill_validate_skilldb(struct s_skill_db *sk, const char *source) +{ + int idx = skill->get_index(sk->nameid); + + if (idx == 0) { + ShowWarning("skill_validate_skilldb: Invalid skill Id %d provided in '%s'! ... skipping\n", sk->nameid, source); + ShowInfo("It is possible that the skill Id is 0 or unavailable (interferes with guild/homun/mercenary skill mapping).\n"); + return false; + } else if (sk->max > MAX_SKILL_LEVEL || sk->max <= 0) { + ShowError("skill_validate_skilldb: Invalid Max Level %d specified for skill Id %d in '%s', skipping...\n", sk->max, sk->nameid, source); + return false; + } + + /* Direct assignment of temporary skill storage to skill db */ + skill->dbs->db[idx] = *sk; + /* Put skill name in name2id DB */ + strdb_iput(skill->name2id_db, skill->dbs->db[idx].name, skill->dbs->db[idx].nameid); + /* Set Name to Id script constants */ + script->set_constant2(skill->dbs->db[idx].name, (int)skill->dbs->db[idx].nameid, false, false); + + return true; +} + +/** + * Reads skill_db.conf from relative filepath and processes [ Smokexyz/Hercules ] + * entries into the skill database. + * @param filename contains the file path and name. + * @return boolean true on success + */ +bool skill_read_skilldb(const char *filename) +{ + struct config_t skilldb; + struct config_setting_t *sk, *conf; + char filepath[256]; + int count=0, index=0; + bool duplicate[MAX_SKILL] = {0}; + + nullpo_retr(false, filename); + + sprintf(filepath,"db/%s",filename); + + if (!libconfig->load_file(&skilldb, filepath)) { + return false; // Libconfig error report. + } + + // Possible Syntax error. + if ((sk=libconfig->setting_get_member(skilldb.root, "skill_db")) == NULL) { + ShowError("skill_read_skilldb: Skill DB could not be loaded, please check '%s'.\n", filepath); + libconfig->destroy(&skilldb); + return false; + } + + while ((conf = libconfig->setting_get_elem(sk,index++))) { + int idx=0, skill_id=0, temp=0; + struct config_setting_t *t = NULL, *tt = NULL; + struct s_skill_db tmp_db = { 0 }; + + /* Skill ID */ + if (!libconfig->setting_lookup_int(conf, "Id", &skill_id)) { + ShowError("skill_read_skilldb: Skill Id not specified for entry %d in '%s', skipping...\n", index, filepath ); + continue; + } + + tmp_db.nameid = skill_id; + + if((idx = skill->get_index(skill_id)) == 0) { + ShowError("skill_read_skilldb: Skill Id %d is out of range, or within a reserved range (for guild, homunculus, mercenary or elemental skills). skipping...\n", idx); + continue; + } + + if (duplicate[idx]) { + ShowWarning("skill_read_skilldb: Duplicate Skill Id %d in entry %d in '%s', skipping...\n", skill_id, index, filepath); + continue; + } + + /* Skill Name Constant */ + if (!libconfig->setting_lookup_mutable_string(conf, "Name", tmp_db.name, sizeof(tmp_db.name))) { + ShowError("skill_read_skilldb: Name not specified for skill Id %d in '%s', skipping...\n", skill_id, filepath); + continue; + } + + /* Skill Description */ + libconfig->setting_lookup_mutable_string(conf, "Description", tmp_db.desc, sizeof(tmp_db.desc)); + + /* Max Level */ + if (!libconfig->setting_lookup_int(conf, "MaxLevel", &temp)) { + ShowError("skill_read_skilldb: MaxLevel not specified for skill Id %d in '%s', skipping...\n", skill_id, filepath); + continue; + } else { + tmp_db.max = temp; + } + + /* Range */ + if ((t=libconfig->setting_get_member(conf, "Range"))) + skill->config_set_level(t, tmp_db.range); + + /* Hit Type */ + skill->validate_hittype(conf, &tmp_db); + + /* Skill Type */ + skill->validate_skilltype(conf, &tmp_db); + + /* Skill Info */ + skill->validate_skillinfo(conf, &tmp_db); + + /* Skill Attack Type */ + skill->validate_attacktype(conf, &tmp_db); + + /* Skill Element */ + skill->validate_element(conf, &tmp_db); + + /* Damage Type */ + skill->validate_damagetype(conf, &tmp_db); + + /* Splash Range */ + if ((t = libconfig->setting_get_member(conf, "SplashRange"))) + skill->config_set_level(t, tmp_db.splash); + + /* Number of Hits */ + if ((t = libconfig->setting_get_member(conf, "NumberOfHits")) && config_setting_is_group(t)) + skill->config_set_level(t, tmp_db.num); + else if ((libconfig->setting_lookup_int(conf, "NumberOfHits", &temp))) + skill->level_set_value(tmp_db.num, temp); + else + skill->level_set_value(tmp_db.num, 1); // Default 1 + + /* Interrupt Cast */ + if (libconfig->setting_lookup_bool(conf, "InterruptCast", &tmp_db.castcancel) == 0) + tmp_db.castcancel = 1; + + /* Cast Defense Rate */ + libconfig->setting_lookup_int(conf, "CastDefRate", &tmp_db.cast_def_rate); + + /* Skill Instances */ + if ((t = libconfig->setting_get_member(conf, "SkillInstances"))) + skill->config_set_level(t, tmp_db.maxcount); + + /* Knock-Back Tiles */ + if ((t = libconfig->setting_get_member(conf, "KnockBackTiles"))) + skill->config_set_level(t, tmp_db.blewcount); + /** + * Skill Cast / Delay data handling + */ + /* Cast Time */ + if ((t=libconfig->setting_get_member(conf, "CastTime"))) + skill->config_set_level(t, tmp_db.cast); + + /* After Cast Act Delay */ + if ((t=libconfig->setting_get_member(conf, "AfterCastActDelay"))) + skill->config_set_level(t, tmp_db.delay); + + /* After Cast Walk Delay */ + if ((t=libconfig->setting_get_member(conf, "AfterCastWalkDelay"))) + skill->config_set_level(t, tmp_db.walkdelay); + + /* Skill Data/Duration */ + if ((t=libconfig->setting_get_member(conf, "SkillData1"))) + skill->config_set_level(t, tmp_db.upkeep_time); + + /* Skill Data/Duration 2 */ + if ((t=libconfig->setting_get_member(conf, "SkillData2"))) + skill->config_set_level(t, tmp_db.upkeep_time2); + + /* Skill Cool Down */ + if ((t=libconfig->setting_get_member(conf, "CoolDown"))) + skill->config_set_level(t, tmp_db.cooldown); + +#ifdef RENEWAL_CAST + /* Fixed Casting Time */ + if ((t=libconfig->setting_get_member(conf, "FixedCastTime"))) + skill->config_set_level(t, tmp_db.fixed_cast); +#endif + /* Cast Time Options */ + skill->validate_castnodex(conf, &tmp_db, false); + skill->validate_castnodex(conf, &tmp_db, true); + + /** + * Skill Requirements data handling + */ + if ((t=libconfig->setting_get_member(conf, "Requirements")) && config_setting_is_group(t)) { + + /* HP Costs */ + if ((tt = libconfig->setting_get_member(t, "HPCost"))) + skill->config_set_level(tt, tmp_db.hp); + + /* Max HP Trigger */ + if ((tt = libconfig->setting_get_member(t, "MaxHPTrigger"))) + skill->config_set_level(tt, tmp_db.mhp); + + /* SP Cost */ + if ((tt = libconfig->setting_get_member(t, "SPCost"))) + skill->config_set_level(tt, tmp_db.sp); + + /* HP Rate */ + if ((tt = libconfig->setting_get_member(t, "HPRate"))) + skill->config_set_level(tt, tmp_db.hp_rate); + + /* SP Rate */ + if ((tt = libconfig->setting_get_member(t, "SPRate"))) + skill->config_set_level(tt, tmp_db.sp_rate); + + /* Zeny Cost */ + if ((tt = libconfig->setting_get_member(t, "ZenyCost"))) + skill->config_set_level(tt, tmp_db.zeny); + + /* Spirit Sphere Cost */ + if ((tt = libconfig->setting_get_member(t, "SpiritSphereCost"))) + skill->config_set_level(tt, tmp_db.spiritball); + + /* Weapon Types */ + skill->validate_weapontype(conf, &tmp_db); + + /* Ammunition Types */ + skill->validate_ammotype(conf, &tmp_db); + + /* Ammunition Amount */ + if ((tt = libconfig->setting_get_member(t, "AmmoAmount"))) + skill->config_set_level(tt, tmp_db.ammo_qty); + + /* State */ + skill->validate_state(t, &tmp_db); + + /* Spirit Sphere Cost */ + if ((tt = libconfig->setting_get_member(t, "SpiritSphereCost"))) + skill->config_set_level(tt, tmp_db.spiritball); + + /* Item Requirements and Amounts */ + skill->validate_item_requirements(t, &tmp_db); + } + + /** + * Skill Unit data handling + */ + if ((t=libconfig->setting_get_member(conf, "Unit")) && config_setting_is_group(t)) { + + /* Unit IDs [1,2] */ + if ((tt=libconfig->setting_get_member(t, "Id")) && config_setting_is_array(tt)) { + tmp_db.unit_id[0] = libconfig->setting_get_int_elem(tt, 0); + tmp_db.unit_id[1] = libconfig->setting_get_int_elem(tt, 1); + } else { + libconfig->setting_lookup_int(t, "Id", &tmp_db.unit_id[0]); + } + + /* Layout */ + if((tt=libconfig->setting_get_member(t, "Layout"))) + skill->config_set_level(tt, tmp_db.unit_layout_type); + + /* Range */ + if((tt=libconfig->setting_get_member(t, "Range"))) + skill->config_set_level(tt, tmp_db.unit_range); + + /* Interval */ + if(libconfig->setting_lookup_int(t, "Interval", &temp)) + tmp_db.unit_interval = temp; + + /* Flag */ + skill->validate_unit_flag(t, &tmp_db); + + /* Target */ + skill->validate_unit_target(t, &tmp_db); + } + + /* Additional Fields for Plugins */ + skill->validate_additional_fields(conf, &tmp_db); + + // Validate the skill entry, add it to the duplicate array and increment count on success. + if ((duplicate[idx] = skill->validate_skilldb(&tmp_db, filepath))) + count++; + } + + libconfig->destroy(&skilldb); + + ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", count, filepath); + + return true; +} + +#undef skilldb_duplicate_warning +#undef skilldb_invalid_error + /*=============================== * DB reading. * skill_db.txt @@ -19205,26 +20262,20 @@ void skill_readdb(bool minimal) { safestrncpy(skill->dbs->db[0].name, "UNKNOWN_SKILL", sizeof(skill->dbs->db[0].name)); safestrncpy(skill->dbs->db[0].desc, "Unknown Skill", sizeof(skill->dbs->db[0].desc)); + itemdb->name_constants(); // refresh ItemDB constants before loading of skills + #ifdef ENABLE_CASE_CHECK - script->parser_current_file = DBPATH"skill_db.txt"; + script->parser_current_file = DBPATH"skill_db.conf"; #endif // ENABLE_CASE_CHECK - sv->readdb(map->db_path, DBPATH"skill_db.txt", ',', 17, 17, MAX_SKILL_DB, skill->parse_row_skilldb); + //sv->readdb(map->db_path, DBPATH"skill_db.txt", ',', 17, 17, MAX_SKILL_DB, skill->parse_row_skilldb); + skill->read_skilldb(DBPATH"skill_db.conf"); #ifdef ENABLE_CASE_CHECK script->parser_current_file = NULL; #endif // ENABLE_CASE_CHECK if (minimal) return; - - sv->readdb(map->db_path, DBPATH"skill_require_db.txt", ',', 32, 32, MAX_SKILL_DB, skill->parse_row_requiredb); -#ifdef RENEWAL_CAST - sv->readdb(map->db_path, "re/skill_cast_db.txt", ',', 8, 8, MAX_SKILL_DB, skill->parse_row_castdb); -#else - sv->readdb(map->db_path, "pre-re/skill_cast_db.txt", ',', 7, 7, MAX_SKILL_DB, skill->parse_row_castdb); -#endif - sv->readdb(map->db_path, DBPATH"skill_castnodex_db.txt", ',', 2, 3, MAX_SKILL_DB, skill->parse_row_castnodexdb); - sv->readdb(map->db_path, DBPATH"skill_unit_db.txt", ',', 8, 8, MAX_SKILL_DB, skill->parse_row_unitdb); - + skill->init_unit_layout(); sv->readdb(map->db_path, "produce_db.txt", ',', 4, 4+2*MAX_PRODUCE_RESOURCE, MAX_SKILL_PRODUCE_DB, skill->parse_row_producedb); sv->readdb(map->db_path, "create_arrow_db.txt", ',', 1+2, 1+2*MAX_ARROW_RESOURCE, MAX_SKILL_ARROW_DB, skill->parse_row_createarrowdb); @@ -19507,11 +20558,26 @@ void skill_defaults(void) { skill->unit_timer = skill_unit_timer; skill->unit_timer_sub = skill_unit_timer_sub; skill->init_unit_layout = skill_init_unit_layout; - skill->parse_row_skilldb = skill_parse_row_skilldb; - skill->parse_row_requiredb = skill_parse_row_requiredb; - skill->parse_row_castdb = skill_parse_row_castdb; - skill->parse_row_castnodexdb = skill_parse_row_castnodexdb; - skill->parse_row_unitdb = skill_parse_row_unitdb; + /* Skill DB Libconfig */ + skill->validate_hittype = skill_validate_hittype; + skill->validate_attacktype = skill_validate_attacktype; + skill->validate_element = skill_validate_element; + skill->validate_skilltype = skill_validate_skilltype; + skill->validate_skillinfo = skill_validate_skillinfo; + skill->validate_damagetype = skill_validate_damagetype; + skill->validate_castnodex = skill_validate_castnodex; + skill->validate_weapontype = skill_validate_weapontype; + skill->validate_ammotype = skill_validate_ammotype; + skill->validate_state = skill_validate_state; + skill->validate_item_requirements = skill_validate_item_requirements; + skill->validate_unit_target = skill_validate_unit_target; + skill->validate_unit_flag = skill_validate_unit_flag; + skill->validate_additional_fields = skill_validate_additional_fields; + skill->validate_skilldb = skill_validate_skilldb; + skill->read_skilldb = skill_read_skilldb; + skill->config_set_level = skill_config_set_level; + skill->level_set_value = skill_level_set_value; + /* */ skill->parse_row_producedb = skill_parse_row_producedb; skill->parse_row_createarrowdb = skill_parse_row_createarrowdb; skill->parse_row_abradb = skill_parse_row_abradb; diff --git a/src/map/skill.h b/src/map/skill.h index 35fddafa4..9bb3eae13 100644 --- a/src/map/skill.h +++ b/src/map/skill.h @@ -58,7 +58,7 @@ struct status_change_entry; #define MAX_SKILLUNITGROUP 25 #define MAX_SKILL_ITEM_REQUIRE 10 #define MAX_SKILLUNITGROUPTICKSET 25 -#define MAX_SKILL_NAME_LENGTH 30 +#define MAX_SKILL_NAME_LENGTH 32 // (Epoque:) To-do: replace this macro with some sort of skill tree check (rather than hard-coded skill names) #define skill_ischangesex(id) ( \ @@ -2033,11 +2033,24 @@ struct skill_interface { int (*unit_timer) (int tid, int64 tick, int id, intptr_t data); int (*unit_timer_sub) (union DBKey key, struct DBData *data, va_list ap); void (*init_unit_layout) (void); - bool (*parse_row_skilldb) (char* split[], int columns, int current); - bool (*parse_row_requiredb) (char* split[], int columns, int current); - bool (*parse_row_castdb) (char* split[], int columns, int current); - bool (*parse_row_castnodexdb) (char* split[], int columns, int current); - bool (*parse_row_unitdb) (char* split[], int columns, int current); + void (*validate_hittype) (struct config_setting_t *conf, struct s_skill_db *skill); + void (*validate_skilltype) (struct config_setting_t *conf, struct s_skill_db *skill); + void (*validate_attacktype) (struct config_setting_t *conf, struct s_skill_db *skill); + void (*validate_element) (struct config_setting_t *conf, struct s_skill_db *skill); + void (*validate_skillinfo) (struct config_setting_t *conf, struct s_skill_db *skill); + void (*validate_damagetype) (struct config_setting_t *conf, struct s_skill_db *skill); + void (*validate_castnodex) (struct config_setting_t *conf, struct s_skill_db *skill, bool delay); + void (*validate_weapontype) (struct config_setting_t *conf, struct s_skill_db *skill); + void (*validate_ammotype) (struct config_setting_t *conf, struct s_skill_db *skill); + void (*validate_state) (struct config_setting_t *conf, struct s_skill_db *skill); + void (*validate_item_requirements) (struct config_setting_t *conf, struct s_skill_db *skill); + void (*validate_unit_target) (struct config_setting_t *conf, struct s_skill_db *skill); + void (*validate_unit_flag) (struct config_setting_t *conf, struct s_skill_db *skill); + void (*validate_additional_fields) (struct config_setting_t *conf, struct s_skill_db *skill); + bool (*validate_skilldb) (struct s_skill_db *skt, const char *source); + bool (*read_skilldb) (const char *filename); + void (*config_set_level) (struct config_setting_t *conf, int *arr); + void (*level_set_value) (int *arr, int value); bool (*parse_row_producedb) (char* split[], int columns, int current); bool (*parse_row_createarrowdb) (char* split[], int columns, int current); bool (*parse_row_abradb) (char* split[], int columns, int current); diff --git a/tools/skilldbconverter.php b/tools/skilldbconverter.php new file mode 100644 index 000000000..169ea9162 --- /dev/null +++ b/tools/skilldbconverter.php @@ -0,0 +1,1141 @@ +. +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* Credits : Smokexyz */ + +// Credits before anything else. +printcredits(); + +if(!isset($argv[1])) + gethelp(); + +function issetarg($arg) { + global $argv; + for($i=1; $i10)?10:(($max<1)?1:$max); + $list_num = $arr[8]; + $castcancel = $arr[9]; + $cast_defence_rate = $arr[10]; + $inf2 = $arr[11]; + $maxcount = $arr[12]; + $skill_type = $arr[13]; + $blow_count = $arr[14]; + $name = $arr[15]; + if(strlen(substr($arr[16], 0, strpos($arr[16], "//")))) + $description = substr($arr[16], 0, strpos($arr[16], "//")); + else + $description = $arr[16]; + + $putsk .= "{\n". + "\tId: ".$id."\n". + "\tName: \"".trim($name)."\"\n". + "\tDescription: \"".trim($description)."\"\n". + "\tMaxLevel: ".$max."\n"; + if($range) $putsk .= "\tRange: ".leveled($range, $max, $id)."\n"; + if($hit==8) $putsk .= "\tHit: \"BDT_MULTIHIT\"\n"; + else if($hit==6) $putsk .= "\tHit: \"BDT_SKILL\"\n"; + if($inf) $putsk .= "\tSkillType: ".getinf($inf)."\n"; + if($inf2) $putsk .= "\tSkillInfo: ".getinf2($inf2)."\n"; + if($skill_type != "none" && $inf) $putsk .= "\tAttackType: \"".ucfirst($skill_type)."\"\n"; + if($element) $putsk .= "\tElement: ".leveled_ele($element, $max, $id)."\n"; + if($nk && $nk != "0x0") $putsk .= "\tDamageType: ".getnk($nk)."\n"; + if($splash) $putsk .= "\tSplashRange: ".leveled($splash, $max, $id)."\n"; + if($list_num != "1") $putsk .= "\tNumberOfHits: ".leveled($list_num, $max, $id)."\n"; + if($castcancel && $inf) $putsk .= "\tInterruptCast: true\n"; + if($cast_defence_rate) $putsk .= "\tCastDefRate: ".$cast_defence_rate."\n"; + if($maxcount) $putsk .= "\tSkillInstances: ".leveled($maxcount, $max, $id)."\n"; + if($blow_count) $putsk .= "\tKnockBackTiles: ".leveled($blow_count, $max, $id)."\n"; + // Cast Db + $key = array_search($id, $skcast['ID']); + if($key) { + if($skcast['casttime'][$key]) $putsk .= "\tCastTime: ".leveled($skcast['casttime'][$key], $max, $id)."\n"; + if($skcast['actdelay'][$key]) $putsk .= "\tAfterCastActDelay: ".leveled($skcast['actdelay'][$key], $max, $id)."\n"; + if($skcast['walkdelay'][$key] != 0) $putsk .= "\tAfterCastWalkDelay: ".leveled($skcast['walkdelay'][$key], $max, $id)."\n"; + if($skcast['data1'][$key] != 0) $putsk .= "\tSkillData1: ".leveled($skcast['data1'][$key], $max, $id)."\n"; + if($skcast['data2'][$key] != 0) $putsk .= "\tSkillData2: ".leveled($skcast['data2'][$key], $max, $id)."\n"; + if($skcast['cooldown'][$key] != 0) $putsk .= "\tCoolDown: ".leveled($skcast['cooldown'][$key], $max, $id)."\n"; + if(defined('RENEWAL') && strlen($skcast['fixedcast'][$key])>1 && $skcast['fixedcast'][$key] != 0) + $putsk .= "\tFixedCastTime: ".leveled($skcast['fixedcast'][$key], $max, $id)."\n"; + } + // Cast NoDex + unset($key); + $key = array_search($id, $sknodex['ID']); + if($key) { + if (isset($sknodex["cast"][$key]) && $sknodex["cast"][$key] != 0) $putsk .= "\tCastTimeOptions: ".getnocast($sknodex["cast"][$key], $id)."\n"; + if (isset($sknodex["delay"][$key]) && $sknodex["delay"][$key] != 0) $putsk .= "\tSkillDelayOptions: ".getnocast($sknodex["delay"][$key], $id)."\n"; + unset($sknodex["ID"][$key]); + unset($sknodex["cast"][$key]); + unset($sknodex["delay"][$key]); + } + + // require DB + unset($key); + $key = array_search($id, $skreq['ID']); + if($key) { + $putsk .= "\tRequirements: {\n"; + if ($skreq['HPCost'][$key]) $putsk .= "\t\tHPCost: ".leveled($skreq['HPCost'][$key], $max, $id, 1)."\n"; + if ($skreq['SPCost'][$key]) $putsk .= "\t\tSPCost: ".leveled($skreq['SPCost'][$key], $max, $id, 1)."\n"; + if ($skreq['HPRateCost'][$key]) $putsk .= "\t\tHPRateCost: ".leveled($skreq['HPRateCost'][$key], $max, $id, 1)."\n"; + if ($skreq['SPRateCost'][$key]) $putsk .= "\t\tSPRateCost: ".leveled($skreq['SPRateCost'][$key], $max, $id, 1)."\n"; + if ($skreq['ZenyCost'][$key]) $putsk .= "\t\tZenyCost: ".leveled($skreq['ZenyCost'][$key], $max, $id, 1)."\n"; + if ($skreq['Weapons'][$key] != 99) $putsk .= "\t\tWeaponTypes: ".getweapontypes($skreq['Weapons'][$key], $id)."\n"; + if ($skreq['AmmoTypes'][$key] == 99) $putsk .= "\t\tAmmoTypes: \"All\"\n"; + else if ($skreq['AmmoTypes'][$key]) $putsk .= "\t\tAmmoTypes: ".getammotypes($skreq['AmmoTypes'][$key], $id)."\n"; + if ($skreq['AmmoAmount'][$key]) $putsk .= "\t\tAmmoAmount: ".leveled($skreq['AmmoAmount'][$key], $max, $id, 1)."\n"; + if ($skreq['State'][$key] != "none" && $skreq['State'][$key]) $putsk .= "\t\tState: \"".getstate($skreq['State'][$key],$id)."\"\n"; + if ($skreq['SpiritSphere'][$key]) $putsk .= "\t\tSpiritSphereCost: ".leveled($skreq['SpiritSphere'][$key], $max, $id, 1)."\n"; + if ($skreqit['ItemId'][$key][0] > 0) { + $putsk .= "\t\tItems: {\n"; + for ($index=0; $index 0 + && $skunit['Flag'][$key] != "") + $putsk .= "\t\tFlag: ".getunitflag($skunit['Flag'][$key], $id)."\n"; + $putsk .= "\t}\n"; + } + // close skill + $putsk .= "},\n"; + // Display progress bar + show_status($i++, $linecount); +} +show_status($linecount, $linecount); +/** + * Print final messages and exit the script, conversion has completed. + */ +print "\n"; +print "The skill database has been \033[1;32msuccessfully\033[0m converted to Hercules' libconfig\n"; +print "format and has been saved as '".DIRPATH."skill_db.conf'.\n"; +print "The following files are now deprecated and can be deleted -\n"; +print DIRPATH."skill_db.txt\n"; +print DIRPATH."skill_cast_db.txt\n"; +print DIRPATH."skill_castnodex_db.txt\n"; +print DIRPATH."skill_require_db.txt\n"; +print DIRPATH."skill_unit_db.txt\n"; +$putsk .= ")"; +$skconf = "skill_db.conf"; +file_put_contents(DIRPATH.$skconf, $putsk); +if($debug) { + print "\033[0;34m[Debug]\033[0;0m Memory after converting: ".print_mem()."\n"; + print "\033[0;34m[Debug]\033[0;0m Execution Time : ".(microtime_float()-$t_init)."s\n"; +} +fclose($skmain); + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +/* Functions +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +function microtime_float() +{ + list($usec, $sec) = explode(" ", microtime()); + return ((float)$usec + (float)$sec); +} + +function show_status($done, $total) { + $perc = floor(($done / $total) * 100); + $perc = floor($perc/2); + $left = 50-$perc; + $finalperc = $perc * 2; + $write = sprintf("\033[0G\033[2K[%'={$perc}s>%-{$left}s] - $finalperc%% - $done/$total", "", ""); + fwrite(STDERR, $write); +} +function get_element($ele,$id) +{ + switch($ele) + { + case -1: return "Ele_Weapon"; + case -2: return "Ele_Endowed"; + case -3: return "Ele_Random"; + case 0: return "Ele_Neutral"; + case 1: return "Ele_Water"; + case 2: return "Ele_Earth"; + case 3: return "Ele_Fire"; + case 4: return "Ele_Wind"; + case 5: return "Ele_Poison"; + case 6: return "Ele_Holy"; + case 7: return "Ele_Dark"; + case 8: return "Ele_Ghost"; + case 9: return "Ele_Undead"; + default: print "\r\033[0;31mWarning\033[0;0mUnknown Element ".$ele." provided for skill Id ".$id."\n"; + } + + return NULL; +} +function leveled_ele($str, $max, $skill_id) +{ + if(strpos($str, ':') == true) + { + $lvs = explode(":", $str); + $retval = "{\n"; + for($i=0; $i<$max && isset($lvs[$i]); $i++) { + if (isset($lvs[$i])) { + $retval .= "\t\tLv".($i+1).": \"".get_element($lvs[$i],$skill_id)."\"\n"; + } else { + print "\r\033[0;31mWarning\033[0;0m - Invalid Level ".($i+1)." provided in Element Level for Skill Id ".$skill_id."\n"; + continue; + } + } + $retval .= "\t}"; + } else { + $retval = "\"".get_element($str,$skill_id)."\""; + } + + return $retval; +} + +function leveled($str, $max, $id, $tab=0) +{ + switch($tab) { + case 1: + $ittab = "\t\t\t"; + $endtab = "\t\t"; + break; + case 2: + $ittab = "\t\t\t\t"; + $endtab = "\t\t\t"; + break; + default: + $ittab = "\t\t"; + $endtab = "\t"; + break; + } + + $retval = ""; + if(strpos($str, ':') == true) { + $lvs = explode(":", trim($str)); + $retval = "{\n"; + for($i=0; $i<$max && isset($lvs[$i]); $i++) { + if(isset($lvs[$i])) { + $retval .= $ittab."Lv".($i+1).": ".$lvs[$i]."\n"; + } else { + print "\r\033[0;31mWarning\033[0;0m - Invalid Level index ".($i+1)." provided in skill Id ".$id."\n"; + } + } + $retval .= $endtab."}"; + } else { + $retval = intval($str); + } + + return $retval; +} + +function getstate($state,$id) +{ + if( strcmp($state,"hiding") == 0 ) return "Hiding"; + else if( strcmp($state,"cloaking") == 0 ) return "Cloaking"; + else if( strcmp($state,"hidden") == 0 ) return "Hidden"; + else if( strcmp($state,"riding") == 0 ) return "Riding"; + else if( strcmp($state,"falcon") == 0 ) return "Falcon"; + else if( strcmp($state,"cart") == 0 ) return "Cart"; + else if( strcmp($state,"shield") == 0 ) return "Shield"; + else if( strcmp($state,"sight") == 0 ) return "Sight"; + else if( strcmp($state,"explosionspirits") == 0 ) return "ExplosionSpirits"; + else if( strcmp($state,"cartboost") == 0 ) return "CartBoost"; + else if( strcmp($state,"recover_weight_rate") == 0 ) return "NotOverWeight"; + else if( strcmp($state,"move_enable") == 0 ) return "Moveable"; + else if( strcmp($state,"water") == 0 ) return "InWater"; + else if( strcmp($state,"dragon") == 0 ) return "Dragon"; + else if( strcmp($state,"warg") == 0 ) return "Warg"; + else if( strcmp($state,"ridingwarg") == 0 ) return "RidingWarg"; + else if( strcmp($state,"mado") == 0 ) return "MadoGear"; + else if( strcmp($state,"elementalspirit") == 0 ) return "ElementalSpirit"; + else if( strcmp($state,"poisonweapon") == 0 ) return "PoisonWeapon"; + else if( strcmp($state,"rollingcutter") == 0 ) return "RollingCutter"; + else if( strcmp($state,"mh_fighting") == 0 ) return "MH_Fighting"; + else if( strcmp($state,"mh_grappling") == 0 ) return "MH_Grappling"; + else if( strcmp($state,"peco") == 0 ) return "Peco"; + else print "\r\033[0;31mWarning\033[0;0m - Invalid State ".$state." provided for Skill ID ".$id.", please correct this manually.\n"; +} + +function getinf($inf) +{ + $bitmask = array( + "Passive" => 0, + "Enemy" => 1, + "Place" => 2, + "Self" => 4, + "Friend" => 16, + "Trap" => 32, + ); + + $retval = "{\n"; + foreach ($bitmask as $key => $val) + { + if($inf&$val) + $retval .= "\t\t".$key.": true\n"; + } + $retval .= "\t}"; + + return $retval; +} + +function getinf2($inf2=0x0000) +{ + $bitmask = array( + "Quest" => intval(0x0001), // = quest skill + "NPC" => intval(0x0002), // = npc skill + "Wedding" => intval(0x0004), // = wedding skill + "Spirit" => intval(0x0008), // = spirit skill + "Guild" => intval(0x0010), // = guild skill + "Song" => intval(0x0020), // = song/dance + "Ensemble" => intval(0x0040), // = ensemble skill + "Trap" => intval(0x0080), // = trap + "TargetSelf" => intval(0x0100), // = skill that damages/targets yourself + "NoCastSelf" => intval(0x0200), // = cannot be casted on self (if inf = 4, auto-select target skill) + "PartyOnly" => intval(0x0400), // = usable only on party-members (and enemies if skill is offensive) + "GuildOnly" => intval(0x0800), // = usable only on guild-mates (and enemies if skill is offensive) + "NoEnemy" => intval(0x1000), // = disable usage on enemies (for non-offensive skills). + "IgnoreLandProtector" => intval(0x2000), // = skill ignores land protector (e.g. arrow shower) + "Chorus" => intval(0x4000) // = chorus skill + ); + + $inf2 = intval(substr($inf2, 2),16); + + $retval = "{\n "; + foreach($bitmask as $key => $val) { + if($inf2&$val) { + $retval .= "\t\t".$key.": true\n"; + } + } + $retval .= "\t}"; + + return $retval; +} + +function getnk($nk) +{ + $bitmask = array( + "NoDamage" => intval(0x01), //- No damage skill + "SplashArea" => intval(0x02)+intval(0x04), //- Has splash area (requires source modification) + "SplitDamage" => intval(0x04), //- Damage should be split among targets (requires 0x02 in order to work) + "IgnoreCards" => intval(0x08), //- Skill ignores caster's % damage cards (misc type always ignores) + "IgnoreElement" => intval(0x10), //- Skill ignores elemental adjustments + "IgnoreDefense" => intval(0x20), //- Skill ignores target's defense (misc type always ignores) + "IgnoreFlee" => intval(0x40), //- Skill ignores target's flee (magic type always ignores) + "IgnoreDefCards" => intval(0x80) //- Skill ignores target's def cards + ); + $nk = intval($nk,16); + $retval = "{\n"; + foreach($bitmask as $key => $val) { + if($key == "SplitDamage") { + if($nk&$bitmask["SplashArea"]) + continue; + else { + $retval .= "\t\tSplashArea: true\n"; + } + } else if($nk&$val) $retval .= "\t\t".$key.": true\n"; + } + $retval .= "\t}"; + + return $retval; +} + +function getnocast($opt, $id) +{ + $bitmask = array( + 'Default' => 0, //- everything affects the skill's cast time + 'IgnoreDex' => 1, //- skill's cast time is not affected by dex + 'IgnoreStatusEffect' => 2, //- skill's cast time is not affected by statuses (Suffragium, etc) + 'IgnoreItemBonus' => 4 //- skill's cast time is not affected by item bonuses (equip, cards) + ); + + if($opt > array_sum($bitmask) || $opt < 0) + print "\r\033[0;31mWarning\033[0;0m - a bitmask for CastNoDex entry for skill ID ".$id." is higher than total of masks or lower than 0."; + + $retval = "{\n"; + foreach($bitmask as $key => $val) { + if($opt&$val) { + $retval .= "\t\t".$key.": true\n"; + } + } + $retval .= "\t}"; + + return $retval; +} + +function getweapontypes($list, $id) +{ + $bitmask = array( + 0 => "NoWeapon", + 1 => "Daggers", + 2 => "1HSwords", + 3 => "2HSwords", + 4 => "1HSpears", + 5 => "2HSpears", + 6 => "1HAxes", + 7 => "2HAxes", + 8 => "Maces", + 9 => "2HMaces", + 10 => "Staves", + 11 => "Bows", + 12 => "Knuckles", + 13 => "Instruments", + 14 => "Whips", + 15 => "Books", + 16 => "Katars", + 17 => "Revolvers", + 18 => "Rifles", + 19 => "GatlingGuns", + 20 => "Shotguns", + 21 => "GrenadeLaunchers", + 22 => "FuumaShurikens", + 23 => "2HStaves", + 24 => "MaxSingleWeaponType", + 25 => "DWDaggers", + 26 => "DWSwords", + 27 => "DWAxes", + 28 => "DWDaggerSword", + 29 => "DWDaggerAxe", + 30 => "DWSwordAxe", + ); + if(strpos($list, ':') == true) { + $type = explode(":", $list); + $wmask = 0; + for($i=0; $i 30 || $type[$i] < 0) + print "\r\033[0;31mWarning\033[0;0m - Invalid weapon type ".$i." for skill ID ".$id."\n"; + } + $retval = "{\n"; + for($j=0; $j "A_ARROW", + 2 => "A_DAGGER", + 3 => "A_BULLET", + 4 => "A_SHELL", + 5 => "A_GRENADE", + 6 => "A_SHURIKEN", + 7 => "A_KUNAI", + 8 => "A_CANNONBALL", + 9 => "A_THROWWEAPON" + ); + + if(strpos($list, ':') == true) { + $type = explode(":", $list); + $wmask = 0; + for($i=0; $i 9 || $type[$i] < 1) { + print "\r\033[0;31mWarning\033[0;0m - Invalid weapon type ".$i." for skill ID ".$id."\n"; + } + } + $retval = "{\n "; + for($j=0; $j intval(0x001), //0x001(UF_DEFNOTENEMY)If 'defunit_not_enemy' is set, the target is changed to 'friend' + 'UF_NOREITERATION' => intval(0x002), //0x002(UF_NOREITERATION)Spell cannot be stacked + 'UF_NOFOOTSET' => intval(0x004), //0x004(UF_NOFOOTSET)Spell cannot be cast near/on targets + 'UF_NOOVERLAP' => intval(0x008), //0x008(UF_NOOVERLAP)Spell effects do not overlap + 'UF_PATHCHECK' => intval(0x010), //0x010(UF_PATHCHECK)Only cells with a shootable path will be placed + 'UF_NOPC' => intval(0x020), //0x020(UF_NOPC)Spell cannot affect players. + 'UF_NOMOB' => intval(0x040), //0x040(UF_NOMOB)Spell cannot affect mobs. + 'UF_SKILL' => intval(0x080), //0x080(UF_SKILL)Spell CAN affect skills. + 'UF_DANCE' => intval(0x100), //0x100(UF_DANCE)Dance skill + 'UF_ENSEMBLE' => intval(0x200), //0x200(UF_ENSEMBLE)Ensemble skill + 'UF_SONG' => intval(0x400), //0x400(UF_SONG)Song skill + 'UF_DUALMODE' => intval(0x800), //0x800(UF_DUALMODE)Spell has effects both at an interval and when you step in/out + 'UF_RANGEDSINGLEUNIT' => intval(0x2000) //0x2000(UF_RANGEDSINGLEUNIT)Layout hack, use layout range propriety but only display center. + ); + + $count = 0; $flag = 0; + $flag = intval($flag,16); + if($flag <= 0) return 0; + + $ret = "{\n "; + foreach($bitmask as $key => $val) { + if($flag&$val) { + $ret .= "\t\t\t".$key.": true\n"; + } + } + + if($flag > array_sum($bitmask)) + print "\r\033[0;31mWarning\033[0;0m - Invalid Unit Flag ".$flag." provided for skill Id ".$id."\n"; + + $ret .= "\t\t}"; + + return $ret; +} + +function print_mem() +{ + return convert(memory_get_usage(true)); +} + +function convert($size) +{ + $unit=array('b','kb','mb','gb','tb','pb'); + return @round($size/pow(1024,($i=floor(log($size,1024)))),2).' '.$unit[$i]; +} + +function gethelp() +{ + print "Usage: php skilldbconverter.php [option]\n"; + print "Options:\n"; + print "\t-re [--renewal] for renewal skill database conversion.\n"; + print "\t-pre-re [--pre-renewal] for pre-renewal skill database conversion.\n"; + print "\t-itid [--use-itemid] to use item IDs instead of constants.\n"; + print "\t-dir [--directory] provide a custom directory.\n"; + print "\t (Must include the correct -pre-re/-re option)\n"; + print "\t-dbg [--with-debug] print debug information.\n"; + print "\t-h [--help] to display this help text.\n\n"; + print "----------------------- Additional Notes ----------------------\n"; + print "\033[0;31mImportant!\033[0;0m\n"; + print "* Please be advised that either and only one of the arguments -re/-pre-re\n"; + print " must be specified on execution.\n"; + print "* When using the -dir option, -re/-pre-re options must be specified. \n"; + print "* This tool isn't designed to convert renewal data to pre-renewal.\n"; + print "* This tool should ideally be used from the 'tools/' folder, which can be found\n"; + print " in the root of your Hercules installation. This tool will not delete any files\n"; + print " from any of the directories that it reads from or prints to.\n\n"; + print "* Prior to using this tool, please ensure at least 30MB of free RAM.\n"; + print "----------------------- Usage Example -------------------------\n"; + print "- \033[0;32mRenewal Conversion\033[0;0m: php skilldbconverter.php --renewal\n"; + print "- \033[0;32mPre-renewal Conversion\033[0;0m: php skilldbconverter.php --pre-renewal\n"; + print "----------------------------------------------------------------\n"; + exit; +} + +function printcredits() +{ + print " _ _ _ \n"; + print " | | | | | | \n"; + print " | |_| | ___ _ __ ___ _ _| | ___ ___ \n"; + print " | _ |/ _ \ '__/ __| | | | |/ _ \/ __|\n"; + print " | | | | __/ | | (__| |_| | | __/\__ \ \n"; + print " \_| |_/\___|_| \___|\__,_|_|\___||___/\n"; + print "\033[0;36mHercules Skill Database TXT to Libconfig Converter by Smokexyz\033[0m\n"; + print "Copyright (C) 2016 \033[0;32mHercules\033[0m\n"; + print "-----------------------------------------------\n\n"; +} + +function getcomments($re) +{ + return + "//================= Hercules Database ========================================== + //= _ _ _ + //= | | | | | | + //= | |_| | ___ _ __ ___ _ _| | ___ ___ + //= | _ |/ _ \ '__/ __| | | | |/ _ \/ __| + //= | | | | __/ | | (__| |_| | | __/\__ \ + //= \_| |_/\___|_| \___|\__,_|_|\___||___/ + //================= License ==================================================== + //= This file is part of Hercules. + //= http://herc.ws - http://github.com/HerculesWS/Hercules + //= + //= Copyright (C) 2014-2016 Hercules Dev Team + //= + //= Hercules is free software: you can redistribute it and/or modify + //= it under the terms of the GNU General Public License as published by + //= the Free Software Foundation, either version 3 of the License, or + //= (at your option) any later version. + //= + //= This program is distributed in the hope that it will be useful, + //= but WITHOUT ANY WARRANTY; without even the implied warranty of + //= MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + //= GNU General Public License for more details. + //= + //= You should have received a copy of the GNU General Public License + //= along with this program. If not, see . + //============================================================================== + //= ".($re?"Renewal":"Pre-Renewal")." Skill Database [Hercules] + //============================================================================== + //= @Format Notes: + //= - All string entries are case-sensitive and must be quoted. + //= - All setting names are case-sensitive and must be keyed accurately. + /****************************************************************************** + ********************************* Entry structure ***************************** + ******************************************************************************* + + { + ------------------------------ Mandatory Fields ---------------------------- + Id: ID (int) (Required) + Name: \"Skill Name\" (string) (Required) + MaxLevel: Skill Level (int) (Required) + ------------------------------ Optional Fields ----------------------------- + Description: \"Skill Description\" (string) (optional but recommended) + Range: Skill Range (int) (optional, defaults to 0) (can be grouped by Levels) + Note: Range < 5 is considered Melee range. + Hit: Hit Type (int) (optional, default \"BDT_NORMAL\") + Types - \"BDT_SKILL\", \"BDT_MULTIHIT\" or \"BDT_NORMAL\" ] + SkillType: { (bool, defaults to \"Passive\") + Passive: true/false (boolean, defaults to false) + Enemy: true/false (boolean, defaults to false) + Place: true/false (boolean, defaults to false) + Self: true/false (boolean, defaults to false) + Friend: true/false (boolean, defaults to false) + Trap: true/false (boolean, defaults to false) + } + SkillInfo: { (bool, defaults to \"None\") + Quest: true/false (boolean, defaults to false) + NPC: true/false (boolean, defaults to false) + Wedding: true/false (boolean, defaults to false) + Spirit: true/false (boolean, defaults to false) + Guild: true/false (boolean, defaults to false) + Song: true/false (boolean, defaults to false) + Ensemble: true/false (boolean, defaults to false) + Trap: true/false (boolean, defaults to false) + TargetSelf: true/false (boolean, defaults to false) + NoCastSelf: true/false (boolean, defaults to false) + PartyOnly: true/false (boolean, defaults to false) + GuildOnly: true/false (boolean, defaults to false) + NoEnemy: true/false (boolean, defaults to false) + IgnoreLandProtector: true/false (boolean, defaults to false) + Chorus: true/false (boolean, defaults to false) + } + AttackType: \"Attack Type\" (string, defaults to \"None\") + Types: \"None\", \"Weapon\", \"Magic\" or \"Misc\" + Element: \"Element Type\" (string) (Optional field - Default \"Ele_Neutral\") + (can be grouped by Levels) + Types: \"Ele_Neutral\", \"Ele_Water\", \"Ele_Earth\", \"Ele_Fire\", \"Ele_Wind\" + \"Ele_Poison\", \"Ele_Holy\", \"Ele_Dark\", \"Ele_Ghost\", \"Ele_Undead\" + \"Ele_Weapon\" - Uses weapon's element. + \"Ele_Endowed\" - Uses Endowed element. + \"Ele_Random\" - Uses random element. + DamageType: { (bool, default to \"NoDamage\") + NoDamage: true/false No damage skill + SplashArea: true/false Has splash area (requires source modification) + SplitDamage: true/false Damage should be split among targets (requires 'SplashArea' in order to work) + IgnoreCards: true/false Skill ignores caster's % damage cards (misc type always ignores) + IgnoreElement: true/false Skill ignores elemental adjustments + IgnoreDefense: true/false Skill ignores target's defense (misc type always ignores) + IgnoreFlee: true/false Skill ignores target's flee (magic type always ignores) + IgnoreDefCards: true/false Skill ignores target's def cards + } + SplashRange: Damage Splash Area (int, defaults to 0) (can be grouped by Levels) + Note: -1 for screen-wide. + NumberOfHits: Number of Hits (int, defaults to 1) (can be grouped by Levels) + Note: when positive, damage is increased by hits, + negative values just show number of hits without + increasing total damage. + InterruptCast: Cast Interruption (bool, defaults to true) + CastDefRate: Cast Defense Reduction (int, defaults to 0) + SkillInstances: Skill instances (int, defaults to 0) (can be grouped by Levels) + Notes: max amount of skill instances to place on the ground when + player_land_skill_limit/monster_land_skill_limit is enabled. For skills + that attack using a path, this is the path length to be used. + KnockBackTiles: Knock-back by 'n' Tiles (int, defaults to 0) (can be grouped by Levels) + CastTime: Skill cast Time (in ms) (int, defaults to 0) (can be grouped by Levels) + AfterCastActDelay: Skill Delay (in ms) (int, defaults to 0) (can be grouped by Levels) + AfterCastWalkDelay: Walk Delay (in ms) (int, defaults to 0) (can be grouped by Levels) + SkillData1: Skill Data/Duration (in ms) (int, defaults to 0) (can be grouped by Levels) + SkillData2: Skill Data/Duration (in ms) (int, defaults to 0) (can be grouped by Levels) + CoolDown: Skill Cooldown (in ms) (int, defaults to 0) (can be grouped by Levels) + ".($re? + "FixedCastTime: Fixed Cast Time (in ms) (int, defaults to 0) (can be grouped by Levels) + Note: when 0, uses 20% of cast time and less than + 0 means no fixed cast time.":"")." + CastTimeOptions: { + IgnoreDex: true/false (boolean, defaults to false) + IgnoreStatusEffect: true/false (boolean, defaults to false) + IgnoreItemBonus: true/false (boolean, defaults to false) + Note: Delay setting 'IgnoreDex' only makes sense when + delay_dependon_dex is enabled. + } + SkillDelayOptions: { + IgnoreDex: true/false (boolean, defaults to false) + IgnoreStatusEffect: true/false (boolean, defaults to false) + IgnoreItemBonus: true/false (boolean, defaults to false) + Note: Delay setting 'IgnoreDex' only makes sense when + delay_dependon_dex is enabled. + } + Requirements: { + HPCost: HP Cost (int, defaults to 0) (can be grouped by Levels) + SPCost: SP Cost (int, defaults to 0) (can be grouped by Levels) + HPRateCost: HP % Cost (int, defaults to 0) (can be grouped by Levels) + Note: If positive, it is a percent of your current hp, + otherwise it is a percent of your max hp. + SPRateCost: SP % Cost (int, defaults to 0) (can be grouped by Levels) + Note: If positive, it is a percent of your current sp, + otherwise it is a percent of your max sp. + ZenyCost: Zeny Cost (int, defaults to 0) (can be grouped by Levels) + WeaponTypes: { (bool or string, defaults to \"All\") + NoWeapon: true/false (boolean, defaults to false) + Daggers: true/false (boolean, defaults to false) + 1HSwords: true/false (boolean, defaults to false) + 2HSwords: true/false (boolean, defaults to false) + 1HSpears: true/false (boolean, defaults to false) + 2HSpears: true/false (boolean, defaults to false) + 1HAxes: true/false (boolean, defaults to false) + 2HAxes: true/false (boolean, defaults to false) + Maces: true/false (boolean, defaults to false) + 2HMaces: true/false (boolean, defaults to false) + Staves: true/false (boolean, defaults to false) + Bows: true/false (boolean, defaults to false) + Knuckles: true/false (boolean, defaults to false) + Instruments: true/false (boolean, defaults to false) + Whips: true/false (boolean, defaults to false) + Books: true/false (boolean, defaults to false) + Katars: true/false (boolean, defaults to false) + Revolvers: true/false (boolean, defaults to false) + Rifles: true/false (boolean, defaults to false) + GatlingGuns: true/false (boolean, defaults to false) + Shotguns: true/false (boolean, defaults to false) + GrenadeLaunchers: true/false (boolean, defaults to false) + FuumaShurikens: true/false (boolean, defaults to false) + 2HStaves: true/false (boolean, defaults to false) + MaxSingleWeaponType: true/false (boolean, defaults to false) + DWDaggers: true/false (boolean, defaults to false) + DWSwords: true/false (boolean, defaults to false) + DWAxes: true/false (boolean, defaults to false) + DWDaggerSword: true/false (boolean, defaults to false) + DWDaggerAxe: true/false (boolean, defaults to false) + DWSwordAxe: true/false (boolean, defaults to false) + } + AmmoTypes: { (for all types use string \"All\") + A_ARROW: true/false (boolean, defaults to false) + A_DAGGER: true/false (boolean, defaults to false) + A_BULLET: true/false (boolean, defaults to false) + A_SHELL: true/false (boolean, defaults to false) + A_GRENADE: true/false (boolean, defaults to false) + A_SHURIKEN: true/false (boolean, defaults to false) + A_KUNAI: true/false (boolean, defaults to false) + A_CANNONBALL: true/false (boolean, defaults to false) + A_THROWWEAPON: true/false (boolean, defaults to false) + } + AmmoAmount: Ammunition Amount (int, defaults to 0) (can be grouped by Levels) + State: \"Required State\" (string, defaults to \"None\") (can be grouped by Levels) + Types : 'None' = Nothing special + 'Moveable' = Requires to be able to move + 'NotOverWeight' = Requires to be less than 50% weight + 'InWater' = Requires to be standing on a water cell + 'Cart' = Requires a Pushcart + 'Riding' = Requires to ride either a peco or a dragon + 'Falcon' = Requires a Falcon + 'Sight' = Requires Sight skill activated + 'Hiding' = Requires Hiding skill activated + 'Cloaking' = Requires Cloaking skill activated + 'ExplosionSpirits' = Requires Fury skill activated + 'CartBoost' = Requires a Pushcart and Cart Boost skill activated + 'Shield' = Requires a 0,shield equipped + 'Warg' = Requires a Warg + 'Dragon' = Requires to ride a Dragon + 'RidingWarg' = Requires to ride a Warg + 'Mado' = Requires to have an active mado + 'PoisonWeapon' = Requires to be under Poisoning Weapon. + 'RollingCutter' = Requires at least one Rotation Counter from Rolling Cutter. + 'ElementalSpirit' = Requires to have an Elemental Spirit summoned. + 'MH_Fighting' = Requires Eleanor fighthing mode + 'MH_Grappling' = Requires Eleanor grappling mode + 'Peco' = Requires riding a peco + SpiritSphereCost: Spirit Sphere Cost (int, defaults to 0) (can be grouped by Levels) + Items: { + ItemID or Aegis_Name : Amount (int, defaults to 0) (can be grouped by Levels) + Item example: \"ID717\" or \"Blue_Gemstone\". + Notes: Items with amount 0 will not be consumed. + Amount can also be grouped by levels. + } + } + Unit: { + Id: [ UnitID, UnitID2 ] (int, defaults to 0) (can be grouped by Levels) + Layout: Unit Layout (int, defaults to 0) (can be grouped by Levels) + Range: Unit Range (int, defaults to 0) (can be grouped by Levels) + Interval: Unit Interval (int, defaults to 0) (can be grouped by Levels) + Target: \"Unit Target\" (string, defaults to \"None\") + Types: + All - affects everyone + NotEnemy - affects anyone who isn't an enemy + Friend - affects party, guildmates and neutral players + Party - affects party only + Guild - affects guild only + Ally - affects party and guildmates only + Sameguild - affects guild but not allies + Enemy - affects enemies only + None - affects nobody + Flag: { + UF_DEFNOTENEMY: true/false (boolean, defaults to false) + UF_NOREITERATION: true/false (boolean, defaults to false) + UF_NOFOOTSET: true/false (boolean, defaults to false) + UF_NOOVERLAP: true/false (boolean, defaults to false) + UF_PATHCHECK: true/false (boolean, defaults to false) + UF_NOPC: true/false (boolean, defaults to false) + UF_NOMOB: true/false (boolean, defaults to false) + UF_SKILL: true/false (boolean, defaults to false) + UF_DANCE: true/false (boolean, defaults to false) + UF_ENSEMBLE: true/false (boolean, defaults to false) + UF_SONG: true/false (boolean, defaults to false) + UF_DUALMODE: true/false (boolean, defaults to false) + UF_RANGEDSINGLEUNI: true/false (boolean, defaults to false) + } + } + } + * This file has been generated by Smokexyz's skilldbconverter.php tool. + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */\n\n"; +} -- cgit v1.2.3-60-g2f50