summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSmokexyz <sagunxp@gmail.com>2016-04-23 01:21:26 +0800
committerSmokexyz <sagunxp@gmail.com>2016-07-09 21:36:45 +0800
commitd2a04ea3158792e2fa30b69a903ea6cf2d349289 (patch)
treec54f32bbd7ff3550bc2b47cc8ab0abe60c4c7ac5
parente35bdc6974663ad11317892ba757dcb35b34be6d (diff)
downloadhercules-d2a04ea3158792e2fa30b69a903ea6cf2d349289.tar.gz
hercules-d2a04ea3158792e2fa30b69a903ea6cf2d349289.tar.bz2
hercules-d2a04ea3158792e2fa30b69a903ea6cf2d349289.tar.xz
hercules-d2a04ea3158792e2fa30b69a903ea6cf2d349289.zip
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.
-rw-r--r--src/map/skill.c1528
-rw-r--r--src/map/skill.h25
-rw-r--r--tools/skilldbconverter.php1141
3 files changed, 2457 insertions, 237 deletions
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 <math.h>
#include <stdio.h>
@@ -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<<l;
- p = strchr(p,':');
- if(!p)
- break;
- p++;
- }
-
- //FIXME: document this
- p = split[8];
- for( j = 0; j < 32; j++ ) {
- int l = atoi(p);
- if( l == 99 ) { // Any ammo type
- skill->dbs->db[idx].ammo = 0xFFFFFFFF;
- break;
- } else if( l ) // 0 stands for no requirement
- skill->dbs->db[idx].ammo |= 1<<l;
- p = strchr(p,':');
- if( !p )
- break;
- p++;
- }
- skill->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; i<MAX_SKILL_LEVEL; i++) {
+ char level[5];
+ sprintf(level, "Lv%d", i+1);
+ libconfig->setting_lookup_int(conf, level, &arr[i]);
+ }
+ } else if (config_setting_is_array(conf)) {
+ for (i=0; i<config_setting_length(conf) && i < MAX_SKILL_LEVEL; i++) {
+ arr[i] = libconfig->setting_get_int_elem(conf, i);
+ }
+ } else {
+ int val=libconfig->setting_get_int(conf);
+ for(i=0; i<MAX_SKILL_LEVEL; i++) {
+ arr[i] = val;
+ }
+ }
+}
+
+/**
+ * Sets all values in a skill level array to a specified value [ Smokexyz/Hercules ]
+ * @param *arr pointer to array being parsed.
+ * @param value value being set for the array.
+ * @return (void)
+ */
+void skill_level_set_value(int *arr, int value)
+{
+ int i=0;
+
+ for(i=0; i<MAX_SKILL_LEVEL; i++) {
+ arr[i] = value;
+ }
+}
+
+void skill_validate_hittype(struct config_setting_t *conf, struct s_skill_db *sk)
+{
+ const char *type = NULL;
+
+ if (libconfig->setting_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<<W_FIST;
+ } else {
+ sk->weapon &= ~(1<<W_FIST);
+ }
+ } else if (strcmpi(type, "Daggers") == 0) {
+ if (on) {
+ sk->weapon |= 1<<W_DAGGER;
+ } else {
+ sk->weapon &= ~(1<<W_DAGGER);
+ }
+ } else if (strcmpi(type, "1HSwords") == 0) {
+
+ if (on) {
+ sk->weapon |= 1<<W_1HSWORD;
+ } else {
+ sk->weapon &= ~(1<<W_1HSWORD);
+ }
+ } else if (strcmpi(type, "2HSwords") == 0) {
+ if (on) {
+ sk->weapon |= 1<<W_2HSWORD;
+ } else {
+ sk->weapon &= ~(1<<W_2HSWORD);
+ }
+ } else if (strcmpi(type, "1HSpears") == 0) {
+ if (on) {
+ sk->weapon |= 1<<W_1HSPEAR;
+ } else {
+ sk->weapon &= ~(1<<W_1HSPEAR);
+ }
+ } else if (strcmpi(type, "2HSpears") == 0) {
+ if (on) {
+ sk->weapon |= 1<<W_2HSPEAR;
+ } else {
+ sk->weapon &= ~(1<<W_2HSPEAR);
+ }
+ } else if (strcmpi(type, "1HAxes") == 0) {
+ if (on) {
+ sk->weapon |= 1<<W_1HAXE;
+ } else {
+ sk->weapon &= ~(1<<W_1HAXE);
+ }
+ } else if (strcmpi(type, "2HAxes") == 0) {
+ if (on) {
+ sk->weapon |= 1<<W_2HAXE;
+ } else {
+ sk->weapon &= ~(1<<W_2HAXE);
+ }
+ } else if (strcmpi(type, "Maces") == 0) {
+ if (on) {
+ sk->weapon |= 1<<W_MACE;
+ } else {
+ sk->weapon &= ~(1<<W_MACE);
+ }
+ } else if (strcmpi(type, "2HMaces") == 0) {
+ if (on) {
+ sk->weapon |= 1<<W_2HMACE;
+ } else {
+ sk->weapon &= ~(1<<W_2HMACE);
+ }
+ } else if (strcmpi(type, "Staves") == 0) {
+ if (on) {
+ sk->weapon |= 1<<W_STAFF;
+ } else {
+ sk->weapon &= ~(1<<W_STAFF);
+ }
+ } else if (strcmpi(type, "Bows") == 0) {
+ if (on) {
+ sk->weapon |= 1<<W_BOW;
+ } else {
+ sk->weapon &= ~(1<<W_BOW);
+ }
+ } else if (strcmpi(type, "Knuckles") == 0) {
+ if (on) {
+ sk->weapon |= 1<<W_KNUCKLE;
+ } else {
+ sk->weapon &= ~(1<<W_KNUCKLE);
+ }
+ } else if (strcmpi(type, "Instruments") == 0) {
+ if (on) {
+ sk->weapon |= 1<<W_MUSICAL;
+ } else {
+ sk->weapon &= ~(1<<W_MUSICAL);
+ }
+ } else if (strcmpi(type, "Whips") == 0) {
+ if (on) {
+ sk->weapon |= 1<<W_WHIP;
+ } else {
+ sk->weapon &= ~(1<<W_WHIP);
+ }
+ } else if (strcmpi(type, "Books") == 0) {
+ if (on) {
+ sk->weapon |= 1<<W_BOOK;
+ } else {
+ sk->weapon &= ~(1<<W_BOOK);
+ }
+ } else if (strcmpi(type, "Katars") == 0) {
+ if (on) {
+ sk->weapon |= 1<<W_KATAR;
+ } else {
+ sk->weapon &= ~(1<<W_KATAR);
+ }
+ } else if (strcmpi(type, "Revolvers") == 0) {
+ if (on) {
+ sk->weapon |= 1<<W_REVOLVER;
+ } else {
+ sk->weapon &= ~(1<<W_REVOLVER);
+ }
+ } else if (strcmpi(type, "Rifles") == 0) {
+ if (on) {
+ sk->weapon |= 1<<W_RIFLE;
+ } else {
+ sk->weapon &= ~(1<<W_RIFLE);
+ }
+ } else if (strcmpi(type, "GatlingGuns") == 0) {
+ if (on) {
+ sk->weapon |= 1<<W_GATLING;
+ } else {
+ sk->weapon &= ~(1<<W_GATLING);
+ }
+ } else if (strcmpi(type, "Shotguns") == 0) {
+ if (on) {
+ sk->weapon |= 1<<W_SHOTGUN;
+ } else {
+ sk->weapon &= ~(1<<W_SHOTGUN);
+ }
+ } else if (strcmpi(type, "GrenadeLaunchers") == 0) {
+ if (on) {
+ sk->weapon |= 1<<W_GRENADE;
+ } else {
+ sk->weapon &= ~(1<<W_GRENADE);
+ }
+ } else if (strcmpi(type, "FuumaShurikens") == 0) {
+ if (on) {
+ sk->weapon |= 1<<W_HUUMA;
+ } else {
+ sk->weapon &= ~(1<<W_HUUMA);
+ }
+ } else if (strcmpi(type, "2HStaves") == 0) {
+ if (on) {
+ sk->weapon |= 1<<W_2HSTAFF;
+ } else {
+ sk->weapon &= ~(1<<W_2HSTAFF);
+ }
+ }
+ /* MAX_SINGLE_WEAPON_TYPE excluded */
+ else if (strcmpi(type, "DWDaggers") == 0) {
+ if (on) {
+ sk->weapon |= 1<<W_DOUBLE_DD;
+ } else {
+ sk->weapon &= ~(1<<W_DOUBLE_DD);
+ }
+ } else if (strcmpi(type, "DWSwords") == 0) {
+ if (on) {
+ sk->weapon |= 1<<W_DOUBLE_SS;
+ } else {
+ sk->weapon &= ~(1<<W_DOUBLE_SS);
+ }
+ } else if (strcmpi(type, "DWAxes") == 0) {
+ if (on) {
+ sk->weapon |= 1<<W_DOUBLE_AA;
+ } else {
+ sk->weapon &= ~(1<<W_DOUBLE_AA);
+ }
+ } else if (strcmpi(type, "DWDaggerSword") == 0) {
+ if (on) {
+ sk->weapon |= 1<<W_DOUBLE_DA;
+ } else {
+ sk->weapon &= ~(1<<W_DOUBLE_DA);
+ }
+ } else if (strcmpi(type, "DWDaggerAxe") == 0) {
+ if (on) {
+ sk->weapon |= 1<<W_DOUBLE_DA;
+ } else {
+ sk->weapon &= ~(1<<W_DOUBLE_DA);
+ }
+ } else if (strcmpi(type, "DWSwordAxe") == 0) {
+ if (on) {
+ sk->weapon |= 1<<W_DOUBLE_SA;
+ } else {
+ sk->weapon &= ~(1<<W_DOUBLE_SA);
+ }
+ } else if (strcmpi(type, "All") == 0) {
+ sk->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<<A_ARROW;
+ } else {
+ sk->ammo &= ~(1<<A_ARROW);
+ }
+ } else if (strcmpi(type, "A_DAGGER") == 0) {
+ if (on) {
+ sk->ammo |= 1<<A_DAGGER;
+ } else {
+ sk->ammo &= ~(1<<A_DAGGER);
+ }
+ } else if (strcmpi(type, "A_BULLET") == 0) {
+ if (on) {
+ sk->ammo |= 1<<A_BULLET;
+ } else {
+ sk->ammo &= ~(1<<A_BULLET);
+ }
+ } else if (strcmpi(type, "A_SHELL") == 0) {
+ if (on) {
+ sk->ammo |= 1<<A_SHELL;
+ } else {
+ sk->ammo &= ~(1<<A_SHELL);
+ }
+ } else if (strcmpi(type, "A_GRENADE") == 0) {
+ if (on) {
+ sk->ammo |= 1<<A_GRENADE;
+ } else {
+ sk->ammo &= ~(1<<A_GRENADE);
+ }
+ } else if (strcmpi(type, "A_SHURIKEN") == 0) {
+ if (on) {
+ sk->ammo |= 1<<A_SHURIKEN;
+ } else {
+ sk->ammo &= ~(1<<A_SHURIKEN);
+ }
+ } else if (strcmpi(type, "A_KUNAI") == 0) {
+ if (on) {
+ sk->ammo |= 1<<A_KUNAI;
+ } else {
+ sk->ammo &= ~(1<<A_KUNAI);
+ }
+ } else if (strcmpi(type, "A_CANNONBALL") == 0) {
+ if (on) {
+ sk->ammo |= 1<<A_CANNONBALL;
+ } else {
+ sk->ammo &= ~(1<<A_CANNONBALL);
+ }
+ } else if (strcmpi(type, "A_THROWWEAPON") == 0) {
+ if (on) {
+ sk->ammo |= 1<<A_THROWWEAPON;
+ } else {
+ sk->ammo &= ~(1<<A_THROWWEAPON);
+ }
+ } else if (strcmpi(type, "All") == 0) {
+ if (on) {
+ sk->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 @@
+<?php
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+* _ _ _
+* | | | | | |
+* | |_| | ___ _ __ ___ _ _| | ___ ___
+* | _ |/ _ \ '__/ __| | | | |/ _ \/ __|
+* | | | | __/ | | (__| |_| | | __/\__ \
+* \_| |_/\___|_| \___|\__,_|_|\___||___/
+*
+* * * * * * * * * * * * * * License * * * * * * * * * * * * * * * * * * * * * *
+* This file is part of Hercules.
+* http://herc.ws - http://github.com/HerculesWS/Hercules
+*
+* Copyright (C) 2016- Smokexyz/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 <http://www.gnu.org/licenses/>.
+* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+* Credits : Smokexyz */
+
+// Credits before anything else.
+printcredits();
+
+if(!isset($argv[1]))
+ gethelp();
+
+function issetarg($arg) {
+ global $argv;
+ for($i=1; $i<sizeof($argv); $i++) {
+ if(strncmp($argv[$i],$arg,strlen($arg)) == 0)
+ return $i;
+ }
+ return 0;
+}
+
+$renewal = (issetarg("-re") || issetarg("--renewal"));
+$prere = (issetarg("-pre-re") || issetarg("--pre-renewal"));
+$constants = (!issetarg("-itid") && !issetarg("--use-itemid"));
+$help = (issetarg("-h") || issetarg("--help"));
+$directory = function() use($argv) {
+ $arg = issetarg("--directory")?issetarg("--directory"):(issetarg("-dir")?issetarg("-dir"):0);
+ if ($arg) {
+ $part = explode("=", $argv[$arg]);
+ if(!isset($part[1])) {
+ die("A directory path was not provided!\n");
+ } else if(!is_dir($part[1])) {
+ die("The given directory ".$part[1]." doesn't exist.\n");
+ } else {
+ return $part[1];
+ }
+ }
+};
+
+$dir = $directory();
+
+if ($dir) {
+ print "Read/Write Directory has been set to '".$dir."'\n";
+ print "Please ensure all skill_db TXT files are placed in this path.\n";
+ print "Please also provide the correct version of the database (re/pre-re).\n";
+ define('DIRPATH', $dir);
+}
+
+$debug = (issetarg("-dbg") || issetarg("--with-debug"));
+
+if ($debug) {
+ print "\033[0;33mDebug Mode Enabled.\033[0;0m\n";
+ $t_init = microtime_float();
+}
+
+if($help || (!$renewal && !$prere) || ($renewal && $prere)) {
+ gethelp();
+}
+
+if ($renewal) {
+ print "Renewal enabled.\n";
+ print "skill_db.txt and associated files (cast, nocastdex, require & unit) will be converted.\n";
+ if(!defined('DIRPATH'))
+ define('DIRPATH', '../db/re/');
+ define('RENEWAL', true);
+} else if ($prere) {
+ print "Pre-Renewal enabled.\n";
+ print "skill_db.txt and associated files (cast, nocastdex, require & unit) will be converted.\n";
+ if(!defined('DIRPATH'))
+ define('DIRPATH', '../db/pre-re/');
+}
+
+// check for existence of files.
+
+$file_check = [
+ DIRPATH."skill_require_db.txt",
+ DIRPATH."skill_cast_db.txt",
+ DIRPATH."skill_castnodex_db.txt",
+ DIRPATH."skill_unit_db.txt",
+ DIRPATH."skill_db.txt"
+];
+
+if($constants) array_push($file_check, DIRPATH."item_db.conf");
+
+foreach($file_check as $file) {
+ if(file_exists($file))
+ print $file." - \033[0;32mFound\033[0;0m\n";
+ else
+ die($file." - \033[0;31mNot Found!\033[0;0m\n");
+}
+
+if ($constants) {
+ print "Using of Item Constants : enabled\n";
+} else {
+ print "Using of Item Constants : disabled.\n";
+}
+
+/* Begin the Loading of Files */
+
+$i=0;
+$file="skill_require_db.txt";
+$requiredb = fopen(DIRPATH.$file, "r") or die("Unable to open '".DIRPATH.$file."'.\n");
+print "\033[0;32mReading\033[0;0m '".DIRPATH.$file."' ...\n";
+while(!feof($requiredb))
+{
+ $line = fgets($requiredb);
+ if(substr($line, 0, 2) == "//" || strlen($line) < 10) continue;
+ $line = strstr(preg_replace('/\s+/','',$line), "//", true);
+ $arr = explode(",", $line);
+ if(!isset($arr[0])) continue;
+ $skreq['ID'][$i] = $arr[0];
+ $skreq['HPCost'][$i] = $arr[1];
+ $skreq['MaxHPTrigger'][$i] = $arr[2];
+ $skreq['SPCost'][$i] = $arr[3];
+ $skreq['HPRateCost'][$i] = $arr[4];
+ $skreq['SPRateCost'][$i] = $arr[5];
+ $skreq['ZenyCost'][$i] = $arr[6];
+ $skreq['Weapons'][$i] = $arr[7];
+ $skreq['AmmoTypes'][$i] = $arr[8];
+ $skreq['AmmoAmount'][$i] = $arr[9];
+ $skreq['State'][$i] = $arr[10];
+ $skreq['SpiritSphere'][$i] = $arr[11];
+ $k=0;
+ for($j=12; $j<=31; $j+=2) {
+ $skreqit['ItemId'][$i][$k] = isset($arr[$j])?$arr[$j]:0;
+ $skreqit['Amount'][$i][$k] = isset($arr[$j+1])?$arr[$j+1]:0;
+ $k++;
+ }
+ $i++;
+}
+if ($debug) {
+ print "\033[0;34m[Debug]\033[0;0m Read require_db Memory: ".print_mem()."\n";
+}
+fclose($requiredb);
+
+$file="skill_cast_db.txt";
+$skillcastdb = fopen(DIRPATH.$file, "r") or die("Unable to open '".DIRPATH.$file."'.\n");
+print "\033[0;32mReading\033[0;0m '".DIRPATH.$file."' ...\n";
+$i=0;
+while(!feof($skillcastdb))
+{
+ $line = fgets($skillcastdb);
+ if (substr($line, 0, 2) == "//" || strlen($line) < 10) continue;
+ $arr = explode(",",$line);
+ if (!isset($arr[0])) continue;
+ $skcast["ID"][$i] = $arr[0]; // SkillCastDBId
+ $skcast["casttime"][$i] = $arr[1];
+ $skcast["actdelay"][$i] = $arr[2];
+ $skcast["walkdelay"][$i] = $arr[3];
+ $skcast["data1"][$i] = $arr[4];
+ $skcast["data2"][$i] = $arr[5];
+ $skcast["cooldown"][$i] = $arr[6];
+ if(defined('RENEWAL')) $skcast["fixedcast"][$i] = $arr[7];
+
+ $i++;
+}
+if($debug) {
+ print "\033[0;34m[Debug]\033[0;0m Read cast_db Memory: ".print_mem()."\n";
+}
+fclose($skillcastdb);
+
+$file="skill_castnodex_db.txt";
+$castnodex = fopen(DIRPATH.$file, "r") or die("Unable to open '".DIRPATH.$file."'.\n");
+print "\033[0;32mReading\033[0;0m '".DIRPATH.$file."' ...\n";
+$i=0;
+while(!feof($castnodex))
+{
+ $line = fgets($castnodex);
+ if(substr($line, 0, 2) == "//" || strlen($line) <= 2) continue;
+ $line = strstr(preg_replace('/\s+/','',$line), "//", true);
+ $arr = explode(",",$line);
+ $sknodex["ID"][$i] = $arr[0];
+ $sknodex["cast"][$i] = isset($arr[1])?$arr[1]:0;
+ $sknodex["delay"][$i] = isset($arr[2])?$arr[2]:0;
+ $i++;
+}
+if($debug) {
+ print "\033[0;34m[Debug]\033[0;0m Read cast_nodex Memory: ".print_mem()."\n";
+}
+fclose($castnodex);
+
+/***
+* Read item_db.conf to gather aegis item name informations.
+*/
+if ($constants) {
+ $itemdb[] = array();
+
+ $file = "item_db.conf";
+ if(file_exists(DIRPATH.$file)) {
+ $itemconf = fopen(DIRPATH.$file, "r") or die ("Unable to open '".DIRPATH.$file."'.\n");
+ print "\033[0;32mReading\033[0;0m '".DIRPATH.$file."' ...\n";
+
+ $started = false;
+ $i=0;
+ while(!feof($itemconf)) {
+ $line = fgets($itemconf);
+ $line = trim($line);
+ if(strcmp($line,"{\n"))
+ $started = true;
+ else if (strcmp($line,"},\n"))
+ $started = false;
+ //echo str_replace(" ","",$line)."\n";
+
+ if($started == true) {
+ $p = explode(":", $line);
+ if(isset($p[0])) {
+ if($p[0] == "Id") {
+ //echo $p[0]." ".(isset($p[1])?$p[1]:NULL)."\n";
+ $itemdb['ID'][$i] = intval($p[1]);
+ }
+ if($p[0] == "AegisName") {
+ //echo $p[0]." ".(isset($p[1])?str_replace("\"","",$p[1]):NULL)."\n";
+ $itemdb['name'][$i] = str_replace("\"","",$p[1]);
+ $i++;
+ }
+ }
+ }
+ }
+ if($debug) {
+ print "\033[0;34m[Debug]\033[0;0m Read item_db Memory: ".print_mem()."\n";
+ }
+ fclose($itemconf);
+ } else {
+ print "Unable to open '".DIRPATH.$file."'... defaulting to using Item ID's instead of Constants.\n";
+ $constants = false;
+ }
+}
+
+/* * *
+* Bring forth the contents of skill_unit_db.txt and store them.
+*/
+$i=0;
+$file="skill_unit_db.txt";
+$unitdb = fopen(DIRPATH.$file, "r") or die("Unable to open '".DIRPATH.$file."'.\n");
+print "\033[0;32mReading\033[0;0m '".DIRPATH.$file."' ...\n";
+while(!feof($unitdb)) {
+ $line = fgets($unitdb);
+ if(substr($line, 0, 2) == "//" || strlen($line) < 10) continue;
+ $line = strstr(preg_replace('/\s+/','',$line), "//", true);
+ $arr = explode(",",$line);
+ $skunit['ID'][$i] = $arr[0];
+ $skunit['UnitID'][$i] = $arr[1];
+ $skunit['UnitID2'][$i] = $arr[2];
+ $skunit['Layout'][$i] = $arr[3];
+ $skunit['Range'][$i] = $arr[4];
+ $skunit['Interval'][$i] = $arr[5];
+ $skunit['Target'][$i] = $arr[6];
+ $skunit['Flag'][$i] = substr($arr[7],2);
+ $i++;
+}
+if($debug) {
+ print "\033[0;34m[Debug]\033[0;0m Read unit_db Memory: ".print_mem()."\n";
+}
+fclose($unitdb);
+
+$putsk = ""; // initialize variable for file_put_contents.
+// Publish all comments
+$putsk .= getcomments((defined('RENEWAL')?true:false));
+
+$putsk .= "skill_db: (\n";
+// Get Main Skilldb File
+$file="skill_db.txt";
+$skmain = fopen(DIRPATH.$file, "r") or die("Unable to open '".DIRPATH.$file."'.\n");
+print "\033[0;32mReading\033[0;0m '".DIRPATH.$file."' ...\n";
+$linecount = 0;
+
+// Get Number of entries
+while(!feof($skmain)) {
+ $line = fgets($skmain);
+ if(substr($line, 0, 2) == "//" || strlen($line) < 10) continue;
+ $linecount++;
+}
+if($debug) {
+ print "\033[0;34m[Debug]\033[0;0m Read skill_db Memory: ".print_mem()."\n";
+}
+fclose($skmain);
+print $linecount." entries found in skill_db.txt.\n";
+
+$i=0;
+$skmain = fopen(DIRPATH.$file, "r") or die("Unable to open '".DIRPATH.$file."'.\n");
+// Begin converting process.
+while(!feof($skmain)) {
+ $line = fgets($skmain);
+ if(substr($line, 0, 2) == "//" || strlen($line) < 10) continue;
+ $arr = explode(",", $line);
+ // id,range,hit,inf,element,nk,splash,max,list_num,castcancel,cast_defence_rate,inf2,maxcount,skill_type,blow_count,name,description
+ $id = $arr[0];
+ $range = $arr[1];
+ $hit = $arr[2];
+ $inf = $arr[3];
+ $element = $arr[4];
+ $nk = $arr[5];
+ $splash = $arr[6];
+ $max = $arr[7];
+ $max = ($max>10)?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<sizeof($skreqit['ItemId'][$key]); $index++) {
+ $itemID = $skreqit['ItemId'][$key][$index]; // Required Item
+ $itemamt = $skreqit['Amount'][$key][$index]; // Required Amount
+
+ if (strpos($itemID, ':') == true) {
+ $items = explode(":", $itemID);
+ $it = 0;
+ while (isset($items) && isset($items[$it])) {
+ if ($constants && $itemID) {
+ $itkey = array_search($items[$it], $itemdb['ID']);
+ if($itkey == 0) $itemname = "ID".$itemID;
+ else $itemname = $itemdb['name'][$itkey];
+ $putsk .= "\t\t\t".trim($itemname).": ".leveled($itemamt, $max, $id, 2)."\n";
+ } else if (intval($itemID)) {
+ $putsk .= "\t\t\tID".$items[$it].": ".leveled($itemamt, $max, $id, 2)."\n";
+ }
+ $it++;
+ }
+ } else {
+ if ($constants && $itemID) {
+ $itkey = array_search($skreqit['ItemId'][$key][$index], $itemdb['ID']);
+ if($itkey == 0) $itemname = "ID".$itemID;
+ else $itemname = $itemdb['name'][$itkey];
+ $putsk .= "\t\t\t".trim($itemname).": ".leveled($itemamt, $max, $id, 2)."\n";
+ } else if ($itemID) {
+ $putsk .= "\t\t\tID".$itemID.": ".leveled($itemamt, $max, $id, 2)."\n";
+ }
+ }
+ }
+ $putsk .= "\t\t}\n";
+ }
+ $putsk .= "\t}\n";
+ }
+
+ unset($key);
+ $key = array_search($id, $skunit['ID']);
+ if($key) {
+ $putsk .= "\tUnit: {\n";
+ if(isset($skunit['UnitID'][$key])) {
+ if(isset($skunit['UnitID2'][$key]) && strlen($skunit['UnitID2'][$key])) {
+ $putsk .= "\t\tId: [ ".$skunit['UnitID'][$key].", ".$skunit['UnitID2'][$key]." ]\n";
+ } else $putsk .= "\t\tId: ".$skunit['UnitID'][$key]."\n";
+ }
+ if(isset($skunit['Layout'][$key]) && $skunit['Layout'][$key] != 0) $putsk .= "\t\tLayout: ".leveled($skunit['Layout'][$key], $max, $id, 1)."\n";
+ if(isset($skunit['Range'][$key]) && $skunit['Range'][$key] != 0) $putsk .= "\t\tRange: ".leveled($skunit['Range'][$key], $max, $id, 1)."\n";
+ if(isset($skunit['Interval'][$key])) $putsk .= "\t\tInterval: ".leveled($skunit['Interval'][$key], $max, $id, 1)."\n";
+ if(isset($skunit['Target'][$key]) && $skunit['Target'][$key] != "noone") $putsk .= "\t\tTarget: \"".trim(ucfirst($skunit['Target'][$key]))."\"\n";
+ if(isset($skunit['Flag'][$key]) && intval($skunit['Flag'][$key]) > 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<sizeof($type); $i++) {
+ $wmask |= 1<<$type[$i];
+ if($type[$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<sizeof($type); $j++) {
+ if($wmask&1<<$type[$j])
+ $retval .= "\t\t\t".$bitmask[$type[$j]].": true\n";
+ }
+ $retval .= "\t\t}";
+ } else {
+ $retval = "{\n";
+ $retval .= "\t\t\t".$bitmask[$list].": true\n";
+ $retval .= "\t\t}";
+ }
+
+ return $retval;
+}
+
+function getammotypes($list, $id) {
+ $bitmask = array(
+ 1 => "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<sizeof($type); $i++) {
+ $wmask |= 1<<$type[$i];
+ if($type[$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<sizeof($type); $j++) {
+ if($wmask&1<<$type[$j]) {
+ $retval .= "\t\t\t".$bitmask[$type[$j]].": true\n";
+ }
+ }
+ $retval .= "\t\t}";
+ } else {
+ $retval = "{\n";
+ $retval .= "\t\t\t".$bitmask[$list].": true\n";
+ $retval .= "\t\t}";
+ }
+
+ return $retval;
+}
+
+function getunitflag($flag, $id)
+{
+ $bitmask = array(
+ 'UF_DEFNOTENEMY' => 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 <http://www.gnu.org/licenses/>.
+ //==============================================================================
+ //= ".($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";
+}