From bcfd69d16f6ccbca90b410def8fb72d129c4e216 Mon Sep 17 00:00:00 2001 From: Kenpachi Developer Date: Fri, 15 May 2020 05:24:03 +0200 Subject: Add defaults, bounds and validations when reading skill DB --- src/map/skill.c | 2126 ++++++++++++++++---- src/map/skill.h | 47 +- src/plugins/HPMHooking/HPMHooking.Defs.inc | 2 - .../HPMHooking/HPMHooking_map.HPMHooksCore.inc | 4 - .../HPMHooking/HPMHooking_map.HookingPoints.inc | 1 - src/plugins/HPMHooking/HPMHooking_map.Hooks.inc | 27 - 6 files changed, 1739 insertions(+), 468 deletions(-) diff --git a/src/map/skill.c b/src/map/skill.c index 8c83e57a3..d3a7190f4 100644 --- a/src/map/skill.c +++ b/src/map/skill.c @@ -20122,9 +20122,6 @@ static bool skill_parse_row_changematerialdb(char *split[], int columns, int cur 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. @@ -20169,11 +20166,198 @@ static void skill_level_set_value(int *arr, int value) } } +/** + * Validates a skill's ID when reading the skill DB. + * If validating fails, the ID is set to 0. + * + * @param conf The libconfig settings block which contains the skill's data. + * @param sk The s_skill_db struct where the ID should be set it. + * @param conf_index The 1-based index of the currently processed libconfig settings block. + * + **/ +static void skill_validate_id(struct config_setting_t *conf, struct s_skill_db *sk, int conf_index) +{ + nullpo_retv(conf); + nullpo_retv(sk); + + sk->nameid = 0; + + int id; + + if (libconfig->setting_lookup_int(conf, "Id", &id) == CONFIG_FALSE) + ShowError("%s: No skill ID specified in entry %d in %s! Skipping skill...\n", + __func__, conf_index, conf->file); + else if (id <= 0) + ShowError("%s: Invalid skill ID %d specified in entry %d in %s! Skipping skill...\n", + __func__, id, conf_index, conf->file); + else if(skill->get_index(id) == 0) + ShowError("%s: Skill ID %d in entry %d in %s is out of range, or within a reserved range (for guild, homunculus, mercenary or elemental skills)! Skipping skill...\n", + __func__, id, conf_index, conf->file); + else if (*skill->get_name(id) != '\0') + ShowError("%s: Duplicate skill ID %d in entry %d in %s! Skipping skill...\n", + __func__, id, conf_index, conf->file); + else + sk->nameid = id; +} + +/** + * Validates if a skill's name contains invalid characters when reading the skill DB. + * + * @param name The name to validate. + * @return True if the passed name is a NULL pointer or contains at least one invalid character, otherwise false. + * + **/ +static bool skill_name_contains_invalid_character(const char *name) +{ + nullpo_retr(true, name); + + for (int i = 0; i < MAX_SKILL_NAME_LENGTH && name[i] != '\0'; i++) { + if (ISALNUM(name[i]) == 0 && name[i] != '_') + return true; + } + + return false; +} + +/** + * Validates a skill's name when reading the skill DB. + * If validating fails, the name is set to an enpty string. + * + * @param conf The libconfig settings block which contains the skill's data. + * @param sk The s_skill_db struct where the name should be set it. + * + **/ +static void skill_validate_name(struct config_setting_t *conf, struct s_skill_db *sk) +{ + nullpo_retv(conf); + nullpo_retv(sk); + + *sk->name = '\0'; + + const char *name; + + if (libconfig->setting_lookup_string(conf, "Name", &name) == CONFIG_FALSE || *name == '\0') + ShowError("%s: No name specified for skill ID %d in %s! Skipping skill...\n", + __func__, sk->nameid, conf->file); + else if (strlen(name) >= sizeof(sk->name)) + ShowError("%s: Specified name %s for skill ID %d in %s is too long: %lu! Maximum is %lu. Skipping skill...\n", + __func__, name, sk->nameid, conf->file, strlen(name), sizeof(sk->name) - 1); + else if (skill->name_contains_invalid_character(name)) + ShowError("%s: Specified name %s for skill ID %d in %s contains invalid characters! Allowed characters are letters, numbers and underscores. Skipping skill...\n", + __func__, name, sk->nameid, conf->file); + else if (skill->name2id(name) != 0) + ShowError("%s: Duplicate name %s for skill ID %d in %s! Skipping skill...\n", + __func__, name, sk->nameid, conf->file); + else + safestrncpy(sk->name, name, sizeof(sk->name)); +} + +/** + * Validates a skill's maximum level when reading the skill DB. + * If validating fails, the maximum level is set to 0. + * + * @param conf The libconfig settings block which contains the skill's data. + * @param sk The s_skill_db struct where the maximum level should be set it. + * + **/ +static void skill_validate_max_level(struct config_setting_t *conf, struct s_skill_db *sk) +{ + nullpo_retv(conf); + nullpo_retv(sk); + + sk->max = 0; + + int max_level; + + if (libconfig->setting_lookup_int(conf, "MaxLevel", &max_level) == CONFIG_FALSE) + ShowError("%s: No maximum level specified for skill ID %d in %s! Skipping skill...\n", + __func__, sk->nameid, conf->file); + else if (max_level < 1 || max_level > MAX_SKILL_LEVEL) + ShowError("%s: Invalid maximum level %d specified for skill ID %d in %s! Minimum is 1, maximum is %d. Skipping skill...\n", + __func__, max_level, sk->nameid, conf->file, MAX_SKILL_LEVEL); + else + sk->max = max_level; +} + +/** + * Validates a skill's description when reading the skill DB. + * + * @param conf The libconfig settings block which contains the skill's data. + * @param sk The s_skill_db struct where the description should be set it. + * + **/ +static void skill_validate_description(struct config_setting_t *conf, struct s_skill_db *sk) +{ + nullpo_retv(conf); + nullpo_retv(sk); + + *sk->desc = '\0'; + + const char *description; + + if (libconfig->setting_lookup_string(conf, "Description", &description) == CONFIG_TRUE && *description != '\0') { + if (strlen(description) >= sizeof(sk->desc)) + ShowWarning("%s: Specified description '%s' for skill ID %d in %s is too long: %lu! Maximum is %lu. Trimming...\n", + __func__, description, sk->nameid, conf->file, strlen(description), sizeof(sk->desc) - 1); + + safestrncpy(sk->desc, description, sizeof(sk->desc)); + } +} + +/** + * Validates a skill's range when reading the skill DB. + * + * @param conf The libconfig settings block which contains the skill's data. + * @param sk The s_skill_db struct where the range should be set it. + * + **/ +static void skill_validate_range(struct config_setting_t *conf, struct s_skill_db *sk) +{ + nullpo_retv(conf); + nullpo_retv(sk); + + skill->level_set_value(sk->range, 0); + + struct config_setting_t *t = libconfig->setting_get_member(conf, "Range"); + + if (t != NULL && config_setting_is_group(t)) { + for (int i = 0; i < MAX_SKILL_LEVEL; i++) { + char lv[6]; // Big enough to contain "Lv999" in case of custom MAX_SKILL_LEVEL. + safesnprintf(lv, sizeof(lv), "Lv%d", i + 1); + int range; + + if (libconfig->setting_lookup_int(t, lv, &range) == CONFIG_TRUE) { + if (range >= SHRT_MIN && range <= SHRT_MAX) + sk->range[i] = range; + else + ShowWarning("%s: Invalid range %d specified in level %d for skill ID %d in %s! Minimum is %d, maximum is %d. Defaulting to 0...\n", + __func__, range, i + 1, sk->nameid, conf->file, SHRT_MIN, SHRT_MAX); + } + } + + return; + } + + int range; + + if (libconfig->setting_lookup_int(conf, "Range", &range) == CONFIG_TRUE) { + if (range >= SHRT_MIN && range <= SHRT_MAX) + skill->level_set_value(sk->range, range); + else + ShowWarning("%s: Invalid range %d specified for skill ID %d in %s! Minimum is %d, maximum is %d. Defaulting to 0...\n", + __func__, range, sk->nameid, conf->file, SHRT_MIN, SHRT_MAX); + } +} + static void skill_validate_hittype(struct config_setting_t *conf, struct s_skill_db *sk) { + nullpo_retv(conf); + nullpo_retv(sk); + + sk->hit = BDT_NORMAL; + const char *type = NULL; - nullpo_retv(sk); if (libconfig->setting_lookup_string(conf, "Hit", &type)) { if (strcmpi(type, "BDT_SKILL") == 0) { sk->hit = BDT_SKILL; @@ -20182,8 +20366,8 @@ static void skill_validate_hittype(struct config_setting_t *conf, struct s_skill } else if (strcmpi(type, "BDT_NORMAL") == 0) { sk->hit = BDT_NORMAL; } else { - skilldb_invalid_error(type, "Hit", sk->nameid); - return; + ShowWarning("%s: Invalid hit type %s specified for skill ID %d in %s! Defaulting to BDT_NORMAL...\n", + __func__, type, sk->nameid, conf->file); } } } @@ -20196,9 +20380,13 @@ static void skill_validate_hittype(struct config_setting_t *conf, struct s_skill */ static void skill_validate_skilltype(struct config_setting_t *conf, struct s_skill_db *sk) { + nullpo_retv(conf); + nullpo_retv(sk); + + sk->inf = INF_NONE; + struct config_setting_t *t = NULL, *tt = NULL; - nullpo_retv(sk); if((t=libconfig->setting_get_member(conf, "SkillType")) && config_setting_is_group(t)) { int j=0; while ((tt = libconfig->setting_get_elem(t, j++))) { @@ -20236,7 +20424,8 @@ static void skill_validate_skilltype(struct config_setting_t *conf, struct s_ski sk->inf &= ~INF_TARGET_TRAP; } } else if (strcmpi(type, "Passive") != 0) { - skilldb_invalid_error(type, config_setting_name(t), sk->nameid); + ShowWarning("%s: Invalid skill type %s specified for skill ID %d in %s! Skipping type...\n", + __func__, type, sk->nameid, conf->file); } } } @@ -20250,9 +20439,13 @@ static void skill_validate_skilltype(struct config_setting_t *conf, struct s_ski */ static void skill_validate_skillinfo(struct config_setting_t *conf, struct s_skill_db *sk) { + nullpo_retv(conf); + nullpo_retv(sk); + + sk->inf2 = INF2_NONE; + struct config_setting_t *t = NULL, *tt = NULL; - nullpo_retv(sk); if ((t=libconfig->setting_get_member(conf, "SkillInfo")) && config_setting_is_group(t)) { int j=0; while ((tt = libconfig->setting_get_elem(t, j++))) { @@ -20386,7 +20579,8 @@ static void skill_validate_skillinfo(struct config_setting_t *conf, struct s_ski sk->inf2 &= ~INF2_IS_COMBO_SKILL; } } else if (strcmpi(type, "None") != 0) { - skilldb_invalid_error(type, config_setting_name(t), sk->nameid); + ShowWarning("%s: Invalid sub-type %s specified for skill ID %d in %s! Skipping sub-type...\n", + __func__, type, sk->nameid, conf->file); } } } @@ -20400,9 +20594,13 @@ static void skill_validate_skillinfo(struct config_setting_t *conf, struct s_ski */ static void skill_validate_attacktype(struct config_setting_t *conf, struct s_skill_db *sk) { + nullpo_retv(conf); + nullpo_retv(sk); + + sk->skill_type = BF_NONE; + const char *type = NULL; - nullpo_retv(sk); if (libconfig->setting_lookup_string(conf, "AttackType", &type)) { if (!strcmpi(type, "Weapon")) { sk->skill_type = BF_WEAPON; @@ -20410,9 +20608,9 @@ static void skill_validate_attacktype(struct config_setting_t *conf, struct s_sk sk->skill_type = BF_MAGIC; } else if (!strcmpi(type, "Misc")) { sk->skill_type = BF_MISC; - } else { - skilldb_invalid_error(type, "AttackType", sk->nameid); - return; + } else if (strcmpi(type, "None") != 0) { + ShowWarning("%s: Invalid attack type %s specified for skill ID %d in %s! Defaulting to None...\n", + __func__, type, sk->nameid, conf->file); } } } @@ -20425,16 +20623,21 @@ static void skill_validate_attacktype(struct config_setting_t *conf, struct s_sk */ static void skill_validate_element(struct config_setting_t *conf, struct s_skill_db *sk) { + nullpo_retv(conf); + nullpo_retv(sk); + + skill->level_set_value(sk->element, ELE_NEUTRAL); + const char *type = NULL; struct config_setting_t *t = NULL; - nullpo_retv(sk); if ((t=libconfig->setting_get_member(conf, "Element")) && config_setting_is_group(t)) { int j = 0; char lv[6]; // enough to contain "Lv100" in case of custom MAX_SKILL_LEVEL 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; @@ -20443,12 +20646,13 @@ static void skill_validate_element(struct config_setting_t *conf, struct s_skill 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); + ShowWarning("%s: Invalid element %s specified in level %d for skill ID %d in %s! Defaulting to Ele_Neutral...\n", + __func__, type, j + 1, sk->nameid, conf->file); } } } else if (libconfig->setting_lookup_string(conf, "Element", &type)) { - int ele = 0; + int ele = ELE_NEUTRAL; if (strcmpi(type,"Ele_Weapon") == 0) ele = -1; @@ -20457,7 +20661,8 @@ static void skill_validate_element(struct config_setting_t *conf, struct s_skill 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); + ShowWarning("%s: Invalid element %s specified for skill ID %d in %s! Defaulting to Ele_Neutral...\n", + __func__, type, sk->nameid, conf->file); return; } @@ -20473,9 +20678,13 @@ static void skill_validate_element(struct config_setting_t *conf, struct s_skill */ static void skill_validate_damagetype(struct config_setting_t *conf, struct s_skill_db *sk) { + nullpo_retv(conf); + nullpo_retv(sk); + + sk->nk = NK_NONE; + struct config_setting_t *t = NULL, *tt = NULL; - nullpo_retv(sk); if ((t=libconfig->setting_get_member(conf, "DamageType")) && config_setting_is_group(t)) { int j=0; while ((tt = libconfig->setting_get_elem(t, j++))) { @@ -20531,87 +20740,916 @@ static void skill_validate_damagetype(struct config_setting_t *conf, struct s_sk sk->nk &= ~NK_NO_CARDFIX_DEF; } } else { - skilldb_invalid_error(type, config_setting_name(t), sk->nameid); + ShowWarning("%s: Invalid damage type %s specified for skill ID %d in %s! Skipping damage type...\n", + __func__, type, sk->nameid, conf->file); } } } + + if (sk->nk == NK_NONE) + sk->nk = NK_NO_DAMAGE; } /** - * 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) - */ -static void skill_validate_castnodex(struct config_setting_t *conf, struct s_skill_db *sk, bool delay) + * Validates a skill's splash range when reading the skill DB. + * + * @param conf The libconfig settings block which contains the skill's data. + * @param sk The s_skill_db struct where the splash range should be set it. + * + **/ +static void skill_validate_splash_range(struct config_setting_t *conf, struct s_skill_db *sk) { - struct config_setting_t *t = NULL, *tt = NULL; - + nullpo_retv(conf); nullpo_retv(sk); - 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(sk->splash, 0); + + struct config_setting_t *t = libconfig->setting_get_member(conf, "SplashRange"); + if (t != NULL && config_setting_is_group(t)) { + for (int i = 0; i < MAX_SKILL_LEVEL; i++) { + char lv[6]; // Big enough to contain "Lv999" in case of custom MAX_SKILL_LEVEL. + safesnprintf(lv, sizeof(lv), "Lv%d", i + 1); + int splash_range; + + if (libconfig->setting_lookup_int(t, lv, &splash_range) == CONFIG_TRUE) { + if (splash_range >= SHRT_MIN && splash_range <= SHRT_MAX) + sk->splash[i] = splash_range; + else + ShowWarning("%s: Invalid splash range %d specified in level %d for skill ID %d in %s! Minimum is %d, maximum is %d. Defaulting to 0...\n", + __func__, splash_range, i + 1, sk->nameid, conf->file, SHRT_MIN, SHRT_MAX); + } } - skill->level_set_value(delay?sk->delaynodex:sk->castnodex, tmpopt); + + return; + } + + int splash_range; + + if (libconfig->setting_lookup_int(conf, "SplashRange", &splash_range) == CONFIG_TRUE) { + if (splash_range >= SHRT_MIN && splash_range <= SHRT_MAX) + skill->level_set_value(sk->splash, splash_range); + else + ShowWarning("%s: Invalid splash range %d specified for skill ID %d in %s! Minimum is %d, maximum is %d. Defaulting to 0...\n", + __func__, splash_range, sk->nameid, conf->file, SHRT_MIN, SHRT_MAX); } } /** - * 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 - */ -static int skill_validate_weapontype_sub(const char *type, bool on, struct s_skill_db *sk) + * Validates a skill's number of hits when reading the skill DB. + * + * @param conf The libconfig settings block which contains the skill's data. + * @param sk The s_skill_db struct where the number of hits should be set it. + * + **/ +static void skill_validate_number_of_hits(struct config_setting_t *conf, struct s_skill_db *sk) { - nullpo_ret(sk); - if (strcmpi(type, "NoWeapon") == 0) { - if (on) { - sk->weapon |= 1<weapon &= ~(1<level_set_value(sk->num, 1); + + struct config_setting_t *t = libconfig->setting_get_member(conf, "NumberOfHits"); + + if (t != NULL && config_setting_is_group(t)) { + for (int i = 0; i < MAX_SKILL_LEVEL; i++) { + char lv[6]; // Big enough to contain "Lv999" in case of custom MAX_SKILL_LEVEL. + safesnprintf(lv, sizeof(lv), "Lv%d", i + 1); + int number_of_hits; + + if (libconfig->setting_lookup_int(t, lv, &number_of_hits) == CONFIG_TRUE) { + if (number_of_hits >= SHRT_MIN && number_of_hits <= SHRT_MAX) + sk->num[i] = number_of_hits; + else + ShowWarning("%s: Invalid number of hits %d specified in level %d for skill ID %d in %s! Minimum is %d, maximum is %d. Defaulting to 1...\n", + __func__, number_of_hits, i + 1, sk->nameid, conf->file, SHRT_MIN, SHRT_MAX); + } } - } else if (strcmpi(type, "Daggers") == 0) { - if (on) { - sk->weapon |= 1<weapon &= ~(1<setting_lookup_int(conf, "NumberOfHits", &number_of_hits) == CONFIG_TRUE) { + if (number_of_hits >= SHRT_MIN && number_of_hits <= SHRT_MAX) + skill->level_set_value(sk->num, number_of_hits); + else + ShowWarning("%s: Invalid number of hits %d specified for skill ID %d in %s! Minimum is %d, maximum is %d. Defaulting to 1...\n", + __func__, number_of_hits, sk->nameid, conf->file, SHRT_MIN, SHRT_MAX); + } +} + +/** + * Validates a skill's cast interruptibility when reading the skill DB. + * + * @param conf The libconfig settings block which contains the skill's data. + * @param sk The s_skill_db struct where the cast interruptibility should be set it. + * + **/ +static void skill_validate_interrupt_cast(struct config_setting_t *conf, struct s_skill_db *sk) +{ + nullpo_retv(conf); + nullpo_retv(sk); + + sk->castcancel = 0; + + int interrupt_cast; + + if (libconfig->setting_lookup_bool(conf, "InterruptCast", &interrupt_cast) == CONFIG_TRUE) + sk->castcancel = (interrupt_cast != 0) ? 1 : 0; +} + +/** + * Validates a skill's cast defence rate when reading the skill DB. + * + * @param conf The libconfig settings block which contains the skill's data. + * @param sk The s_skill_db struct where the cast defence rate should be set it. + * + **/ +static void skill_validate_cast_def_rate(struct config_setting_t *conf, struct s_skill_db *sk) +{ + nullpo_retv(conf); + nullpo_retv(sk); + + sk->cast_def_rate = 0; + + int cast_def_rate; + + if (libconfig->setting_lookup_int(conf, "CastDefRate", &cast_def_rate) == CONFIG_TRUE) { + if (cast_def_rate >= SHRT_MIN && cast_def_rate <= SHRT_MAX) + sk->cast_def_rate = cast_def_rate; + else + ShowWarning("%s: Invalid cast defence rate %d specified for skill ID %d in %s! Minimum is %d, maximum is %d. Defaulting to 0...\n", + __func__, cast_def_rate, sk->nameid, conf->file, SHRT_MIN, SHRT_MAX); + } +} + +/** + * Validates a skill's number of instances when reading the skill DB. + * + * @param conf The libconfig settings block which contains the skill's data. + * @param sk The s_skill_db struct where the number of instances should be set it. + * + **/ +static void skill_validate_number_of_instances(struct config_setting_t *conf, struct s_skill_db *sk) +{ + nullpo_retv(conf); + nullpo_retv(sk); + + skill->level_set_value(sk->maxcount, 0); + + struct config_setting_t *t = libconfig->setting_get_member(conf, "SkillInstances"); + + if (t != NULL && config_setting_is_group(t)) { + for (int i = 0; i < MAX_SKILL_LEVEL; i++) { + char lv[6]; // Big enough to contain "Lv999" in case of custom MAX_SKILL_LEVEL. + safesnprintf(lv, sizeof(lv), "Lv%d", i + 1); + int number_of_instances; + + if (libconfig->setting_lookup_int(t, lv, &number_of_instances) == CONFIG_TRUE) { + if (number_of_instances >= 0 && number_of_instances <= MAX_SKILLUNITGROUP) + sk->maxcount[i] = number_of_instances; + else + ShowWarning("%s: Invalid number of instances %d specified in level %d for skill ID %d in %s! Minimum is 0, maximum is %d. Defaulting to 0...\n", + __func__, number_of_instances, i + 1, sk->nameid, conf->file, MAX_SKILLUNITGROUP); + } } - } else if (strcmpi(type, "1HSwords") == 0) { - if (on) { - sk->weapon |= 1<weapon &= ~(1<setting_lookup_int(conf, "SkillInstances", &number_of_instances) == CONFIG_TRUE) { + if (number_of_instances >= 0 && number_of_instances <= MAX_SKILLUNITGROUP) + skill->level_set_value(sk->maxcount, number_of_instances); + else + ShowWarning("%s: Invalid number of instances %d specified for skill ID %d in %s! Minimum is 0, maximum is %d. Defaulting to 0...\n", + __func__, number_of_instances, sk->nameid, conf->file, MAX_SKILLUNITGROUP); + } +} + +/** + * Validates a skill's number of knock back tiles when reading the skill DB. + * + * @param conf The libconfig settings block which contains the skill's data. + * @param sk The s_skill_db struct where the number of knock back tiles should be set it. + * + **/ +static void skill_validate_knock_back_tiles(struct config_setting_t *conf, struct s_skill_db *sk) +{ + nullpo_retv(conf); + nullpo_retv(sk); + + skill->level_set_value(sk->blewcount, 0); + + struct config_setting_t *t = libconfig->setting_get_member(conf, "KnockBackTiles"); + + if (t != NULL && config_setting_is_group(t)) { + for (int i = 0; i < MAX_SKILL_LEVEL; i++) { + char lv[6]; // Big enough to contain "Lv999" in case of custom MAX_SKILL_LEVEL. + safesnprintf(lv, sizeof(lv), "Lv%d", i + 1); + int knock_back_tiles; + + if (libconfig->setting_lookup_int(t, lv, &knock_back_tiles) == CONFIG_TRUE) { + if (knock_back_tiles >= 0) + sk->blewcount[i] = knock_back_tiles; + else + ShowWarning("%s: Invalid number of knock back tiles %d specified in level %d for skill ID %d in %s! Must be greater than or equal to 0. Defaulting to 0...\n", + __func__, knock_back_tiles, i + 1, sk->nameid, conf->file); + } + } + + return; + } + + int knock_back_tiles; + + if (libconfig->setting_lookup_int(conf, "KnockBackTiles", &knock_back_tiles) == CONFIG_TRUE) { + if (knock_back_tiles >= 0) + skill->level_set_value(sk->blewcount, knock_back_tiles); + else + ShowWarning("%s: Invalid number of knock back tiles %d specified for skill ID %d in %s! Must be greater than or equal to 0. Defaulting to 0...\n", + __func__, knock_back_tiles, sk->nameid, conf->file); + } +} + +/** + * Validates a skill's cast time when reading the skill DB. + * + * @param conf The libconfig settings block which contains the skill's data. + * @param sk The s_skill_db struct where the cast time should be set it. + * + **/ +static void skill_validate_cast_time(struct config_setting_t *conf, struct s_skill_db *sk) +{ + nullpo_retv(conf); + nullpo_retv(sk); + + skill->level_set_value(sk->cast, 0); + + struct config_setting_t *t = libconfig->setting_get_member(conf, "CastTime"); + + if (t != NULL && config_setting_is_group(t)) { + for (int i = 0; i < MAX_SKILL_LEVEL; i++) { + char lv[6]; // Big enough to contain "Lv999" in case of custom MAX_SKILL_LEVEL. + safesnprintf(lv, sizeof(lv), "Lv%d", i + 1); + int cast_time; + + if (libconfig->setting_lookup_int(t, lv, &cast_time) == CONFIG_TRUE) { + if (cast_time >= 0) + sk->cast[i] = cast_time; + else + ShowWarning("%s: Invalid cast time %d specified in level %d for skill ID %d in %s! Must be greater than or equal to 0. Defaulting to 0...\n", + __func__, cast_time, i + 1, sk->nameid, conf->file); + } + } + + return; + } + + int cast_time; + + if (libconfig->setting_lookup_int(conf, "CastTime", &cast_time) == CONFIG_TRUE) { + if (cast_time >= 0) + skill->level_set_value(sk->cast, cast_time); + else + ShowWarning("%s: Invalid cast time %d specified for skill ID %d in %s! Must be greater than or equal to 0. Defaulting to 0...\n", + __func__, cast_time, sk->nameid, conf->file); + } +} + +/** + * Validates a skill's after cast act delay when reading the skill DB. + * + * @param conf The libconfig settings block which contains the skill's data. + * @param sk The s_skill_db struct where the after cast act delay should be set it. + * + **/ +static void skill_validate_act_delay(struct config_setting_t *conf, struct s_skill_db *sk) +{ + nullpo_retv(conf); + nullpo_retv(sk); + + skill->level_set_value(sk->delay, 0); + + struct config_setting_t *t = libconfig->setting_get_member(conf, "AfterCastActDelay"); + + if (t != NULL && config_setting_is_group(t)) { + for (int i = 0; i < MAX_SKILL_LEVEL; i++) { + char lv[6]; // Big enough to contain "Lv999" in case of custom MAX_SKILL_LEVEL. + safesnprintf(lv, sizeof(lv), "Lv%d", i + 1); + int act_delay; + + if (libconfig->setting_lookup_int(t, lv, &act_delay) == CONFIG_TRUE) { + if (act_delay >= 0) + sk->delay[i] = act_delay; + else + ShowWarning("%s: Invalid after cast act delay %d specified in level %d for skill ID %d in %s! Must be greater than or equal to 0. Defaulting to 0...\n", + __func__, act_delay, i + 1, sk->nameid, conf->file); + } + } + + return; + } + + int act_delay; + + if (libconfig->setting_lookup_int(conf, "AfterCastActDelay", &act_delay) == CONFIG_TRUE) { + if (act_delay >= 0) + skill->level_set_value(sk->delay, act_delay); + else + ShowWarning("%s: Invalid after cast act delay %d specified for skill ID %d in %s! Must be greater than or equal to 0. Defaulting to 0...\n", + __func__, act_delay, sk->nameid, conf->file); + } +} + +/** + * Validates a skill's after cast walk delay when reading the skill DB. + * + * @param conf The libconfig settings block which contains the skill's data. + * @param sk The s_skill_db struct where the after cast walk delay should be set it. + * + **/ +static void skill_validate_walk_delay(struct config_setting_t *conf, struct s_skill_db *sk) +{ + nullpo_retv(conf); + nullpo_retv(sk); + + skill->level_set_value(sk->walkdelay, 0); + + struct config_setting_t *t = libconfig->setting_get_member(conf, "AfterCastWalkDelay"); + + if (t != NULL && config_setting_is_group(t)) { + for (int i = 0; i < MAX_SKILL_LEVEL; i++) { + char lv[6]; // Big enough to contain "Lv999" in case of custom MAX_SKILL_LEVEL. + safesnprintf(lv, sizeof(lv), "Lv%d", i + 1); + int walk_delay; + + if (libconfig->setting_lookup_int(t, lv, &walk_delay) == CONFIG_TRUE) { + if (walk_delay >= 0) + sk->walkdelay[i] = walk_delay; + else + ShowWarning("%s: Invalid after cast walk delay %d specified in level %d for skill ID %d in %s! Must be greater than or equal to 0. Defaulting to 0...\n", + __func__, walk_delay, i + 1, sk->nameid, conf->file); + } + } + + return; + } + + int walk_delay; + + if (libconfig->setting_lookup_int(conf, "AfterCastWalkDelay", &walk_delay) == CONFIG_TRUE) { + if (walk_delay >= 0) + skill->level_set_value(sk->walkdelay, walk_delay); + else + ShowWarning("%s: Invalid after cast walk delay %d specified for skill ID %d in %s! Must be greater than or equal to 0. Defaulting to 0...\n", + __func__, walk_delay, sk->nameid, conf->file); + } +} + +/** + * Validates a skill's stay duration when reading the skill DB. + * + * @param conf The libconfig settings block which contains the skill's data. + * @param sk The s_skill_db struct where the stay duration should be set it. + * + **/ +static void skill_validate_skill_data1(struct config_setting_t *conf, struct s_skill_db *sk) +{ + nullpo_retv(conf); + nullpo_retv(sk); + + skill->level_set_value(sk->upkeep_time, 0); + + struct config_setting_t *t = libconfig->setting_get_member(conf, "SkillData1"); + + if (t != NULL && config_setting_is_group(t)) { + for (int i = 0; i < MAX_SKILL_LEVEL; i++) { + char lv[6]; // Big enough to contain "Lv999" in case of custom MAX_SKILL_LEVEL. + safesnprintf(lv, sizeof(lv), "Lv%d", i + 1); + int skill_data1; + + if (libconfig->setting_lookup_int(t, lv, &skill_data1) == CONFIG_TRUE) { + if (skill_data1 >= INFINITE_DURATION) + sk->upkeep_time[i] = skill_data1; + else + ShowWarning("%s: Invalid stay duration %d specified in level %d for skill ID %d in %s! Must be greater than or equal to %d. Defaulting to 0...\n", + __func__, skill_data1, i + 1, sk->nameid, conf->file, INFINITE_DURATION); + } + } + + return; + } + + int skill_data1; + + if (libconfig->setting_lookup_int(conf, "SkillData1", &skill_data1) == CONFIG_TRUE) { + if (skill_data1 >= INFINITE_DURATION) + skill->level_set_value(sk->upkeep_time, skill_data1); + else + ShowWarning("%s: Invalid stay duration %d specified for skill ID %d in %s! Must be greater than or equal to %d. Defaulting to 0...\n", + __func__, skill_data1, sk->nameid, conf->file, INFINITE_DURATION); + } +} + +/** + * Validates a skill's effect duration when reading the skill DB. + * + * @param conf The libconfig settings block which contains the skill's data. + * @param sk The s_skill_db struct where the effect duration should be set it. + * + **/ +static void skill_validate_skill_data2(struct config_setting_t *conf, struct s_skill_db *sk) +{ + nullpo_retv(conf); + nullpo_retv(sk); + + skill->level_set_value(sk->upkeep_time2, 0); + + struct config_setting_t *t = libconfig->setting_get_member(conf, "SkillData2"); + + if (t != NULL && config_setting_is_group(t)) { + for (int i = 0; i < MAX_SKILL_LEVEL; i++) { + char lv[6]; // Big enough to contain "Lv999" in case of custom MAX_SKILL_LEVEL. + safesnprintf(lv, sizeof(lv), "Lv%d", i + 1); + int skill_data2; + + if (libconfig->setting_lookup_int(t, lv, &skill_data2) == CONFIG_TRUE) { + if (skill_data2 >= INFINITE_DURATION) + sk->upkeep_time2[i] = skill_data2; + else + ShowWarning("%s: Invalid effect duration %d specified in level %d for skill ID %d in %s! Must be greater than or equal to %d. Defaulting to 0...\n", + __func__, skill_data2, i + 1, sk->nameid, conf->file, INFINITE_DURATION); + } + } + + return; + } + + int skill_data2; + + if (libconfig->setting_lookup_int(conf, "SkillData2", &skill_data2) == CONFIG_TRUE) { + if (skill_data2 >= INFINITE_DURATION) + skill->level_set_value(sk->upkeep_time2, skill_data2); + else + ShowWarning("%s: Invalid effect duration %d specified for skill ID %d in %s! Must be greater than or equal to %d. Defaulting to 0...\n", + __func__, skill_data2, sk->nameid, conf->file, INFINITE_DURATION); + } +} + +/** + * Validates a skill's cooldown when reading the skill DB. + * + * @param conf The libconfig settings block which contains the skill's data. + * @param sk The s_skill_db struct where the cooldown should be set it. + * + **/ +static void skill_validate_cooldown(struct config_setting_t *conf, struct s_skill_db *sk) +{ + nullpo_retv(conf); + nullpo_retv(sk); + + skill->level_set_value(sk->cooldown, 0); + + struct config_setting_t *t = libconfig->setting_get_member(conf, "CoolDown"); + + if (t != NULL && config_setting_is_group(t)) { + for (int i = 0; i < MAX_SKILL_LEVEL; i++) { + char lv[6]; // Big enough to contain "Lv999" in case of custom MAX_SKILL_LEVEL. + safesnprintf(lv, sizeof(lv), "Lv%d", i + 1); + int cooldown; + + if (libconfig->setting_lookup_int(t, lv, &cooldown) == CONFIG_TRUE) { + if (cooldown >= 0) + sk->cooldown[i] = cooldown; + else + ShowWarning("%s: Invalid cooldown %d specified in level %d for skill ID %d in %s! Must be greater than or equal to 0. Defaulting to 0...\n", + __func__, cooldown, i + 1, sk->nameid, conf->file); + } + } + + return; + } + + int cooldown; + + if (libconfig->setting_lookup_int(conf, "CoolDown", &cooldown) == CONFIG_TRUE) { + if (cooldown >= 0) + skill->level_set_value(sk->cooldown, cooldown); + else + ShowWarning("%s: Invalid cooldown %d specified for skill ID %d in %s! Must be greater than or equal to 0. Defaulting to 0...\n", + __func__, cooldown, sk->nameid, conf->file); + } +} + +/** + * Validates a skill's fixed cast time when reading the skill DB. + * If RENEWAL_CAST is not defined, nothing is done. + * + * @param conf The libconfig settings block which contains the skill's data. + * @param sk The s_skill_db struct where the fixed cast time should be set it. + * + **/ +static void skill_validate_fixed_cast_time(struct config_setting_t *conf, struct s_skill_db *sk) +{ + nullpo_retv(conf); + nullpo_retv(sk); + +#ifdef RENEWAL_CAST + skill->level_set_value(sk->fixed_cast, 0); + + struct config_setting_t *t = libconfig->setting_get_member(conf, "FixedCastTime"); + + if (t != NULL && config_setting_is_group(t)) { + for (int i = 0; i < MAX_SKILL_LEVEL; i++) { + char lv[6]; // Big enough to contain "Lv999" in case of custom MAX_SKILL_LEVEL. + safesnprintf(lv, sizeof(lv), "Lv%d", i + 1); + int fixed_cast_time; + + if (libconfig->setting_lookup_int(t, lv, &fixed_cast_time) == CONFIG_TRUE) { + if (fixed_cast_time >= INFINITE_DURATION) + sk->fixed_cast[i] = fixed_cast_time; + else + ShowWarning("%s: Invalid fixed cast time %d specified in level %d for skill ID %d in %s! Must be greater than or equal to %d. Defaulting to 0...\n", + __func__, fixed_cast_time, i + 1, sk->nameid, conf->file, INFINITE_DURATION); + } + } + + return; + } + + int fixed_cast_time; + + if (libconfig->setting_lookup_int(conf, "FixedCastTime", &fixed_cast_time) == CONFIG_TRUE) { + if (fixed_cast_time >= INFINITE_DURATION) + skill->level_set_value(sk->fixed_cast, fixed_cast_time); + else + ShowWarning("%s: Invalid fixed cast time %d specified for skill ID %d in %s! Must be greater than or equal to %d. Defaulting to 0...\n", + __func__, fixed_cast_time, sk->nameid, conf->file, INFINITE_DURATION); + } +#else +#ifndef RENEWAL /** Check pre-RE skill DB for FixedCastTime. **/ + if (libconfig->setting_get_member(conf, "FixedCastTime") != NULL) + ShowWarning("%s: Fixed cast time was specified for skill ID %d in %s without RENEWAL_CAST being defined! Skipping...\n", __func__, sk->nameid, conf->file); +#endif /** RENEWAL **/ +#endif /** RENEWAL_CAST **/ +} + +/** + * 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) + */ +static void skill_validate_castnodex(struct config_setting_t *conf, struct s_skill_db *sk, bool delay) +{ + nullpo_retv(conf); + nullpo_retv(sk); + + skill->level_set_value(delay ? sk->delaynodex : sk->castnodex, 0); + + 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 { + const char *option_string = delay ? "skill delay" : "cast time"; + ShowWarning("%s: Invalid %s option %s specified for skill ID %d in %s! Skipping option...\n", + __func__, option_string, type, sk->nameid, conf->file); + } + + } + skill->level_set_value(delay?sk->delaynodex:sk->castnodex, tmpopt); + } +} + +/** + * Validates a skill's HP cost when reading the skill DB. + * + * @param conf The libconfig settings block which contains the skill's data. + * @param sk The s_skill_db struct where the HP cost should be set it. + * + **/ +static void skill_validate_hp_cost(struct config_setting_t *conf, struct s_skill_db *sk) +{ + nullpo_retv(conf); + nullpo_retv(sk); + + skill->level_set_value(sk->hp, 0); + + struct config_setting_t *t = libconfig->setting_get_member(conf, "HPCost"); + + if (t != NULL && config_setting_is_group(t)) { + for (int i = 0; i < MAX_SKILL_LEVEL; i++) { + char lv[6]; // Big enough to contain "Lv999" in case of custom MAX_SKILL_LEVEL. + safesnprintf(lv, sizeof(lv), "Lv%d", i + 1); + int hp_cost; + + if (libconfig->setting_lookup_int(t, lv, &hp_cost) == CONFIG_TRUE) { + if (hp_cost >= 0 && hp_cost <= battle_config.max_hp) + sk->hp[i] = hp_cost; + else + ShowWarning("%s: Invalid HP cost %d specified in level %d for skill ID %d in %s! Minimum is 0, maximum is %d. Defaulting to 0...\n", + __func__, hp_cost, i + 1, sk->nameid, conf->file, battle_config.max_hp); + } + } + + return; + } + + int hp_cost; + + if (libconfig->setting_lookup_int(conf, "HPCost", &hp_cost) == CONFIG_TRUE) { + if (hp_cost >= 0 && hp_cost <= battle_config.max_hp) + skill->level_set_value(sk->hp, hp_cost); + else + ShowWarning("%s: Invalid HP cost %d specified for skill ID %d in %s! Minimum is 0, maximum is %d. Defaulting to 0...\n", + __func__, hp_cost, sk->nameid, conf->file, battle_config.max_hp); + } +} + +/** + * Validates a skill's SP cost when reading the skill DB. + * + * @param conf The libconfig settings block which contains the skill's data. + * @param sk The s_skill_db struct where the SP cost should be set it. + * + **/ +static void skill_validate_sp_cost(struct config_setting_t *conf, struct s_skill_db *sk) +{ + nullpo_retv(conf); + nullpo_retv(sk); + + skill->level_set_value(sk->sp, 0); + + struct config_setting_t *t = libconfig->setting_get_member(conf, "SPCost"); + + if (t != NULL && config_setting_is_group(t)) { + for (int i = 0; i < MAX_SKILL_LEVEL; i++) { + char lv[6]; // Big enough to contain "Lv999" in case of custom MAX_SKILL_LEVEL. + safesnprintf(lv, sizeof(lv), "Lv%d", i + 1); + int sp_cost; + + if (libconfig->setting_lookup_int(t, lv, &sp_cost) == CONFIG_TRUE) { + if (sp_cost >= 0 && sp_cost <= battle_config.max_sp) + sk->sp[i] = sp_cost; + else + ShowWarning("%s: Invalid SP cost %d specified in level %d for skill ID %d in %s! Minimum is 0, maximum is %d. Defaulting to 0...\n", + __func__, sp_cost, i + 1, sk->nameid, conf->file, battle_config.max_sp); + } + } + + return; + } + + int sp_cost; + + if (libconfig->setting_lookup_int(conf, "SPCost", &sp_cost) == CONFIG_TRUE) { + if (sp_cost >= 0 && sp_cost <= battle_config.max_sp) + skill->level_set_value(sk->sp, sp_cost); + else + ShowWarning("%s: Invalid SP cost %d specified for skill ID %d in %s! Minimum is 0, maximum is %d. Defaulting to 0...\n", + __func__, sp_cost, sk->nameid, conf->file, battle_config.max_sp); + } +} + +/** + * Validates a skill's HP rate cost when reading the skill DB. + * + * @param conf The libconfig settings block which contains the skill's data. + * @param sk The s_skill_db struct where the HP rate cost should be set it. + * + **/ +static void skill_validate_hp_rate_cost(struct config_setting_t *conf, struct s_skill_db *sk) +{ + nullpo_retv(conf); + nullpo_retv(sk); + + skill->level_set_value(sk->hp_rate, 0); + + struct config_setting_t *t = libconfig->setting_get_member(conf, "HPRateCost"); + + if (t != NULL && config_setting_is_group(t)) { + for (int i = 0; i < MAX_SKILL_LEVEL; i++) { + char lv[6]; // Big enough to contain "Lv999" in case of custom MAX_SKILL_LEVEL. + safesnprintf(lv, sizeof(lv), "Lv%d", i + 1); + int hp_rate_cost; + + if (libconfig->setting_lookup_int(t, lv, &hp_rate_cost) == CONFIG_TRUE) { + if (hp_rate_cost >= -100 && hp_rate_cost <= 100) + sk->hp_rate[i] = hp_rate_cost; + else + ShowWarning("%s: Invalid HP rate cost %d specified in level %d for skill ID %d in %s! Minimum is -100, maximum is 100. Defaulting to 0...\n", + __func__, hp_rate_cost, i + 1, sk->nameid, conf->file); + } + } + + return; + } + + int hp_rate_cost; + + if (libconfig->setting_lookup_int(conf, "HPRateCost", &hp_rate_cost) == CONFIG_TRUE) { + if (hp_rate_cost >= -100 && hp_rate_cost <= 100) + skill->level_set_value(sk->hp_rate, hp_rate_cost); + else + ShowWarning("%s: Invalid HP rate cost %d specified for skill ID %d in %s! Minimum is -100, maximum is 100. Defaulting to 0...\n", + __func__, hp_rate_cost, sk->nameid, conf->file); + } +} + +/** + * Validates a skill's SP rate cost when reading the skill DB. + * + * @param conf The libconfig settings block which contains the skill's data. + * @param sk The s_skill_db struct where the SP rate cost should be set it. + * + **/ +static void skill_validate_sp_rate_cost(struct config_setting_t *conf, struct s_skill_db *sk) +{ + nullpo_retv(conf); + nullpo_retv(sk); + + skill->level_set_value(sk->sp_rate, 0); + + struct config_setting_t *t = libconfig->setting_get_member(conf, "SPRateCost"); + + if (t != NULL && config_setting_is_group(t)) { + for (int i = 0; i < MAX_SKILL_LEVEL; i++) { + char lv[6]; // Big enough to contain "Lv999" in case of custom MAX_SKILL_LEVEL. + safesnprintf(lv, sizeof(lv), "Lv%d", i + 1); + int sp_rate_cost; + + if (libconfig->setting_lookup_int(t, lv, &sp_rate_cost) == CONFIG_TRUE) { + if (sp_rate_cost >= -100 && sp_rate_cost <= 100) + sk->sp_rate[i] = sp_rate_cost; + else + ShowWarning("%s: Invalid SP rate cost %d specified in level %d for skill ID %d in %s! Minimum is -100, maximum is 100. Defaulting to 0...\n", + __func__, sp_rate_cost, i + 1, sk->nameid, conf->file); + } + } + + return; + } + + int sp_rate_cost; + + if (libconfig->setting_lookup_int(conf, "SPRateCost", &sp_rate_cost) == CONFIG_TRUE) { + if (sp_rate_cost >= -100 && sp_rate_cost <= 100) + skill->level_set_value(sk->sp_rate, sp_rate_cost); + else + ShowWarning("%s: Invalid SP rate cost %d specified for skill ID %d in %s! Minimum is -100, maximum is 100. Defaulting to 0...\n", + __func__, sp_rate_cost, sk->nameid, conf->file); + } +} + +/** + * Validates a skill's maximum HP trigger when reading the skill DB. + * + * @param conf The libconfig settings block which contains the skill's data. + * @param sk The s_skill_db struct where the maximum HP trigger should be set it. + * + **/ +static void skill_validate_max_hp_trigger(struct config_setting_t *conf, struct s_skill_db *sk) +{ + nullpo_retv(conf); + nullpo_retv(sk); + + skill->level_set_value(sk->mhp, 0); + + struct config_setting_t *t = libconfig->setting_get_member(conf, "MaxHPTrigger"); + + if (t != NULL && config_setting_is_group(t)) { + for (int i = 0; i < MAX_SKILL_LEVEL; i++) { + char lv[6]; // Big enough to contain "Lv999" in case of custom MAX_SKILL_LEVEL. + safesnprintf(lv, sizeof(lv), "Lv%d", i + 1); + int max_hp_trigger; + + if (libconfig->setting_lookup_int(t, lv, &max_hp_trigger) == CONFIG_TRUE) { + if (max_hp_trigger >= 0 && max_hp_trigger <= 100) + sk->mhp[i] = max_hp_trigger; + else + ShowWarning("%s: Invalid maximum HP trigger %d specified in level %d for skill ID %d in %s! Minimum is 0, maximum is 100. Defaulting to 0...\n", + __func__, max_hp_trigger, i + 1, sk->nameid, conf->file); + } + } + + return; + } + + int max_hp_trigger; + + if (libconfig->setting_lookup_int(conf, "MaxHPTrigger", &max_hp_trigger) == CONFIG_TRUE) { + if (max_hp_trigger >= 0 && max_hp_trigger <= 100) + skill->level_set_value(sk->mhp, max_hp_trigger); + else + ShowWarning("%s: Invalid maximum HP trigger %d specified for skill ID %d in %s! Minimum is 0, maximum is 100. Defaulting to 0...\n", + __func__, max_hp_trigger, sk->nameid, conf->file); + } +} + +/** + * Validates a skill's Zeny cost when reading the skill DB. + * + * @param conf The libconfig settings block which contains the skill's data. + * @param sk The s_skill_db struct where the Zeny cost should be set it. + * + **/ +static void skill_validate_zeny_cost(struct config_setting_t *conf, struct s_skill_db *sk) +{ + nullpo_retv(conf); + nullpo_retv(sk); + + skill->level_set_value(sk->zeny, 0); + + struct config_setting_t *t = libconfig->setting_get_member(conf, "ZenyCost"); + + if (t != NULL && config_setting_is_group(t)) { + for (int i = 0; i < MAX_SKILL_LEVEL; i++) { + char lv[6]; // Big enough to contain "Lv999" in case of custom MAX_SKILL_LEVEL. + safesnprintf(lv, sizeof(lv), "Lv%d", i + 1); + int zeny_cost; + + if (libconfig->setting_lookup_int(t, lv, &zeny_cost) == CONFIG_TRUE) { + if (zeny_cost >= 0 && zeny_cost <= MAX_ZENY) + sk->zeny[i] = zeny_cost; + else + ShowWarning("%s: Invalid Zeny cost %d specified in level %d for skill ID %d in %s! Minimum is 0, maximum is %d. Defaulting to 0...\n", + __func__, zeny_cost, i + 1, sk->nameid, conf->file, MAX_ZENY); + } + } + + return; + } + + int zeny_cost; + + if (libconfig->setting_lookup_int(conf, "ZenyCost", &zeny_cost) == CONFIG_TRUE) { + if (zeny_cost >= 0 && zeny_cost <= MAX_ZENY) + skill->level_set_value(sk->zeny, zeny_cost); + else + ShowWarning("%s: Invalid Zeny cost %d specified for skill ID %d in %s! Minimum is 0, maximum is %d. Defaulting to 0...\n", + __func__, zeny_cost, sk->nameid, conf->file, MAX_ZENY); + } +} + +/** + * 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 + */ +static int skill_validate_weapontype_sub(const char *type, bool on, struct s_skill_db *sk) +{ + nullpo_retr(1, type); + nullpo_retr(1, sk); + + if (strcmpi(type, "NoWeapon") == 0) { + if (on) { + sk->weapon |= 1<weapon &= ~(1<weapon |= 1<weapon &= ~(1<weapon |= 1<weapon &= ~(1<weapon = 0; } else { - ShowError("Item %d. Unknown weapon type %s\n", sk->nameid, type); return 1; // invalid type } @@ -20796,20 +21833,26 @@ static int skill_validate_weapontype_sub(const char *type, bool on, struct s_ski */ static void skill_validate_weapontype(struct config_setting_t *conf, struct s_skill_db *sk) { + nullpo_retv(conf); + nullpo_retv(sk); + + sk->weapon = 0; + struct config_setting_t *tt = NULL; const char *type = NULL; - nullpo_retv(sk); if ((tt = libconfig->setting_get_member(conf, "WeaponTypes")) && config_setting_is_group(tt)) { int j = 0; struct config_setting_t *wpt = NULL; while ((wpt = libconfig->setting_get_elem(tt, j++)) != NULL) { 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); + ShowWarning("%s: Invalid required weapon type %s specified for skill ID %d in %s! Skipping type...\n", + __func__, config_setting_name(wpt), sk->nameid, conf->file); } } else if (libconfig->setting_lookup_string(conf, "WeaponTypes", &type)) { if (skill->validate_weapontype_sub(type, true, sk)) - skilldb_invalid_error(type, "WeaponTypes", sk->nameid); + ShowWarning("%s: Invalid required weapon type %s specified for skill ID %d in %s! Defaulting to All...\n", + __func__, type, sk->nameid, conf->file); } } @@ -20823,7 +21866,9 @@ static void skill_validate_weapontype(struct config_setting_t *conf, struct s_sk */ static int skill_validate_ammotype_sub(const char *type, bool on, struct s_skill_db *sk) { - nullpo_ret(sk); + nullpo_retr(1, type); + nullpo_retr(1, sk); + if (strcmpi(type, "A_ARROW") == 0) { if (on) { sk->ammo |= 1<ammo = 0; + struct config_setting_t *tt = NULL; const char *tstr = NULL; - nullpo_retv(sk); 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); + ShowWarning("%s: Invalid required ammunition type %s specified for skill ID %d in %s! Skipping type...\n", + __func__, config_setting_name(amt), sk->nameid, conf->file); + } + } else if( libconfig->setting_lookup_string(conf, "AmmoTypes", &tstr)) { + if (skill->validate_ammotype_sub(tstr, true, sk)) + ShowWarning("%s: Invalid required ammunition type %s specified for skill ID %d in %s! Defaulting to None...\n", + __func__, tstr, sk->nameid, conf->file); + } +} + +/** + * Validates a skill's required ammunition amount when reading the skill DB. + * + * @param conf The libconfig settings block which contains the skill's data. + * @param sk The s_skill_db struct where the required ammunition amount should be set it. + * + **/ +static void skill_validate_ammo_amount(struct config_setting_t *conf, struct s_skill_db *sk) +{ + nullpo_retv(conf); + nullpo_retv(sk); + + skill->level_set_value(sk->ammo_qty, 0); + + struct config_setting_t *t = libconfig->setting_get_member(conf, "AmmoAmount"); + + if (t != NULL && config_setting_is_group(t)) { + for (int i = 0; i < MAX_SKILL_LEVEL; i++) { + char lv[6]; // Big enough to contain "Lv999" in case of custom MAX_SKILL_LEVEL. + safesnprintf(lv, sizeof(lv), "Lv%d", i + 1); + int ammo_amount; + + if (libconfig->setting_lookup_int(t, lv, &ammo_amount) == CONFIG_TRUE) { + if (ammo_amount >= 0 && ammo_amount <= MAX_AMOUNT) + sk->ammo_qty[i] = ammo_amount; + else + ShowWarning("%s: Invalid required ammunition amount %d specified in level %d for skill ID %d in %s! Minimum is 0, maximum is %d. Defaulting to 0...\n", + __func__, ammo_amount, i + 1, sk->nameid, conf->file, MAX_AMOUNT); + } + } + + return; + } + + int ammo_amount; + + if (libconfig->setting_lookup_int(conf, "AmmoAmount", &ammo_amount) == CONFIG_TRUE) { + if (ammo_amount >= 0 && ammo_amount <= MAX_AMOUNT) + skill->level_set_value(sk->ammo_qty, ammo_amount); + else + ShowWarning("%s: Invalid required ammunition amount %d specified for skill ID %d in %s! Minimum is 0, maximum is %d. Defaulting to 0...\n", + __func__, ammo_amount, sk->nameid, conf->file, MAX_AMOUNT); + } +} + +/** + * 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 + */ +static void skill_validate_state(struct config_setting_t *conf, struct s_skill_db *sk) +{ + nullpo_retv(conf); + nullpo_retv(sk); + + sk->state = ST_NONE; + + 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_GRAPPLING; + else if (strcmpi(type,"Peco") == 0 ) sk->state = ST_PECO; + else + ShowWarning("%s: Invalid required state %s specified for skill ID %d in %s! Defaulting to None...\n", + __func__, type, sk->nameid, conf->file); + } +} + +/** + * Validates a skill's Spirit Sphere cost when reading the skill DB. + * + * @param conf The libconfig settings block which contains the skill's data. + * @param sk The s_skill_db struct where the Spirit Sphere cost should be set it. + * + **/ +static void skill_validate_spirit_sphere_cost(struct config_setting_t *conf, struct s_skill_db *sk) +{ + nullpo_retv(conf); + nullpo_retv(sk); + + skill->level_set_value(sk->spiritball, 0); + + struct config_setting_t *t = libconfig->setting_get_member(conf, "SpiritSphereCost"); + + if (t != NULL && config_setting_is_group(t)) { + for (int i = 0; i < MAX_SKILL_LEVEL; i++) { + char lv[6]; // Big enough to contain "Lv999" in case of custom MAX_SKILL_LEVEL. + safesnprintf(lv, sizeof(lv), "Lv%d", i + 1); + int spirit_sphere_cost; + + if (libconfig->setting_lookup_int(t, lv, &spirit_sphere_cost) == CONFIG_TRUE) { + if (spirit_sphere_cost >= 0 && spirit_sphere_cost <= MAX_SPIRITBALL) + sk->spiritball[i] = spirit_sphere_cost; + else + ShowWarning("%s: Invalid Spirit Sphere cost %d specified in level %d for skill ID %d in %s! Minimum is 0, maximum is %d. Defaulting to 0...\n", + __func__, spirit_sphere_cost, i + 1, sk->nameid, conf->file, MAX_SPIRITBALL); + } + } + + return; + } + + int spirit_sphere_cost; + + if (libconfig->setting_lookup_int(conf, "SpiritSphereCost", &spirit_sphere_cost) == CONFIG_TRUE) { + if (spirit_sphere_cost >= 0 && spirit_sphere_cost <= MAX_SPIRITBALL) + skill->level_set_value(sk->spiritball, spirit_sphere_cost); + else + ShowWarning("%s: Invalid Spirit Sphere cost %d specified for skill ID %d in %s! Minimum is 0, maximum is %d. Defaulting to 0...\n", + __func__, spirit_sphere_cost, sk->nameid, conf->file, MAX_SPIRITBALL); + } +} + +/** + * 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 + */ +static void skill_validate_item_requirements(struct config_setting_t *conf, struct s_skill_db *sk) +{ + nullpo_retv(conf); + nullpo_retv(sk); + + skill->level_set_value(sk->itemid, 0); + skill->level_set_value(sk->amount, 0); + + 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 (strlen(type) < 2) { + ShowWarning("%s: Invalid required item %s specified for skill ID %d in %s! Skipping item...\n", + __func__, type, sk->nameid, conf->file); + continue; + } + + int item_id = 0; + + if (type[0] == 'I' && type[1] == 'D') { + item_id = atoi(type + 2); + + if (item_id == 0 || itemdb->exists(item_id) == NULL) { + ShowWarning("%s: Invalid required item %s specified for skill ID %d in %s! Skipping item...\n", + __func__, type, sk->nameid, conf->file); + continue; + } + } else if (!script->get_constant(type, &item_id)) { + ShowWarning("%s: Invalid required item %s specified for skill ID %d in %s! Skipping item...\n", + __func__, type, sk->nameid, conf->file); + continue; + } + + int amount = 0; + + if (config_setting_is_group(it)) { + // TODO: Per-level item requirements are not implemented yet! + // We just take the first level for the time being (old txt behavior) + amount = libconfig->setting_get_int_elem(it, 0); + } else { + amount = libconfig->setting_get_int(it); + } + + if (amount < 0 || amount > MAX_AMOUNT) { + ShowWarning("%s: Invalid required item amount %d specified for skill ID %d in %s! Minimum is 0, maximum is %d. Skipping item...\n", + __func__, amount, sk->nameid, conf->file, MAX_AMOUNT); + continue; + } + + sk->itemid[itx] = item_id; + sk->amount[itx] = amount; + } + } +} + +/** + * Validates a skill's requirements when reading the skill DB. + * + * @param conf The libconfig settings block which contains the skill's data. + * @param sk The s_skill_db struct where the requirements should be set it. + * + **/ +static void skill_validate_requirements(struct config_setting_t *conf, struct s_skill_db *sk) +{ + nullpo_retv(conf); + nullpo_retv(sk); + + struct config_setting_t *t = libconfig->setting_get_member(conf, "Requirements"); + + if (t != NULL && config_setting_is_group(t)) { + skill->validate_hp_cost(t, sk); + skill->validate_sp_cost(t, sk); + skill->validate_hp_rate_cost(t, sk); + skill->validate_sp_rate_cost(t, sk); + skill->validate_max_hp_trigger(t, sk); + skill->validate_zeny_cost(t, sk); + skill->validate_weapontype(t, sk); + skill->validate_ammotype(t, sk); + skill->validate_ammo_amount(t, sk); + skill->validate_state(t, sk); + skill->validate_spirit_sphere_cost(t, sk); + skill->validate_item_requirements(t, sk); + } +} + +/** + * Validates a single unit ID when reading the skill DB. + * + * @param unit_id The unit ID to validate. + * @return A number greater than or equal to 0 if the passed unit ID is valid, otherwise -1. + * + **/ +static int skill_validate_unit_id_sub(int unit_id) +{ + if (unit_id == 0 || (unit_id >= UNT_SAFETYWALL && unit_id <= UNT_SV_ROOTTWIST)) + return unit_id; + + return -1; +} + +/** + * Validates a skill's unit IDs when reading the skill DB. + * + * @param conf The libconfig settings block which contains the skill's data. + * @param sk The s_skill_db struct where the unit IDs should be set it. + * + **/ +static void skill_validate_unit_id(struct config_setting_t *conf, struct s_skill_db *sk) +{ + nullpo_retv(conf); + nullpo_retv(sk); + + sk->unit_id[0] = 0; + sk->unit_id[1] = 0; + + struct config_setting_t *t = libconfig->setting_get_member(conf, "Id"); + + if (t != NULL && config_setting_is_array(t)) { + if (libconfig->setting_length(t) == 0) { + ShowWarning("%s: No unit ID(s) specified for skill ID %d in %s! Defaulting to 0...\n", + __func__, sk->nameid, conf->file); + return; + } + + if (libconfig->setting_length(t) > 2) + ShowWarning("%s: Specified more than two unit IDs for skill ID %d in %s! Reading only the first two...\n", + __func__, sk->nameid, conf->file); + + int unit_id1 = libconfig->setting_get_int_elem(t, 0); + + if (skill->validate_unit_id_sub(unit_id1) == -1) { + ShowWarning("%s: Unknown unit ID %d specified for skill ID %d in %s! Defaulting to 0...\n", + __func__, unit_id1, sk->nameid, conf->file); + unit_id1 = 0; + } + + int unit_id2 = 0; + + if (libconfig->setting_length(t) > 1) { + unit_id2 = libconfig->setting_get_int_elem(t, 1); + + if (skill->validate_unit_id_sub(unit_id2) == -1) { + ShowWarning("%s: Unknown unit ID %d specified for skill ID %d in %s! Defaulting to 0...\n", + __func__, unit_id2, sk->nameid, conf->file); + unit_id2 = 0; + } + } + + sk->unit_id[0] = unit_id1; + sk->unit_id[1] = unit_id2; + + return; + } + + int unit_id; + + if (libconfig->setting_lookup_int(conf, "Id", &unit_id) == CONFIG_TRUE) { + if (skill->validate_unit_id_sub(unit_id) == -1) + ShowWarning("%s: Unknown unit ID %d specified for skill ID %d in %s! Defaulting to 0...\n", + __func__, unit_id, sk->nameid, conf->file); + else + sk->unit_id[0] = unit_id; + } +} + +/** + * Validates a skill's unit layout when reading the skill DB. + * + * @param conf The libconfig settings block which contains the skill's data. + * @param sk The s_skill_db struct where the unit layout should be set it. + * + **/ +static void skill_validate_unit_layout(struct config_setting_t *conf, struct s_skill_db *sk) +{ + nullpo_retv(conf); + nullpo_retv(sk); + + skill->level_set_value(sk->unit_layout_type, 0); + + struct config_setting_t *t = libconfig->setting_get_member(conf, "Layout"); + + if (t != NULL && config_setting_is_group(t)) { + for (int i = 0; i < MAX_SKILL_LEVEL; i++) { + char lv[6]; // Big enough to contain "Lv999" in case of custom MAX_SKILL_LEVEL. + safesnprintf(lv, sizeof(lv), "Lv%d", i + 1); + int unit_layout; + + if (libconfig->setting_lookup_int(t, lv, &unit_layout) == CONFIG_TRUE) { + if (unit_layout >= -1 && unit_layout <= MAX_SKILL_UNIT_LAYOUT) + sk->unit_layout_type[i] = unit_layout; + else + ShowWarning("%s: Invalid unit layout %d specified in level %d for skill ID %d in %s! Minimum is -1, maximum is %d. Defaulting to 0...\n", + __func__, unit_layout, i + 1, sk->nameid, conf->file, MAX_SKILL_UNIT_LAYOUT); + } } - } else if( libconfig->setting_lookup_string(conf, "AmmoTypes", &tstr)) { - if (skill->validate_ammotype_sub(tstr, true, sk)) - skilldb_invalid_error(tstr, "AmmoTypes", sk->nameid); + + return; } -} -/** - * 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 - */ -static void skill_validate_state(struct config_setting_t *conf, struct s_skill_db *sk) -{ - const char *type = NULL; + int unit_layout; - nullpo_retv(sk); - 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_GRAPPLING; - else if (strcmpi(type,"Peco") == 0 ) sk->state = ST_PECO; + if (libconfig->setting_lookup_int(conf, "Layout", &unit_layout) == CONFIG_TRUE) { + if (unit_layout >= -1 && unit_layout <= MAX_SKILL_UNIT_LAYOUT) + skill->level_set_value(sk->unit_layout_type, unit_layout); else - skilldb_invalid_error(type, "State", sk->nameid); + ShowWarning("%s: Invalid unit layout %d specified for skill ID %d in %s! Minimum is -1, maximum is %d. Defaulting to 0...\n", + __func__, unit_layout, sk->nameid, conf->file, MAX_SKILL_UNIT_LAYOUT); } } /** - * 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 - */ -static void skill_validate_item_requirements(struct config_setting_t *conf, struct s_skill_db *sk) + * Validates a skill's unit range when reading the skill DB. + * + * @param conf The libconfig settings block which contains the skill's data. + * @param sk The s_skill_db struct where the unit range should be set it. + * + **/ +static void skill_validate_unit_range(struct config_setting_t *conf, struct s_skill_db *sk) { - struct config_setting_t *tt = NULL; - + nullpo_retv(conf); nullpo_retv(sk); - 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); + skill->level_set_value(sk->unit_range, 0); - 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; - } + struct config_setting_t *t = libconfig->setting_get_member(conf, "Range"); - if (config_setting_is_group(it)) { - // TODO: Per-level item requirements are not implemented yet! - // We just take the first level for the time being (old txt behavior) - sk->amount[itx] = libconfig->setting_get_int_elem(it, 0); - } else { - sk->amount[itx] = libconfig->setting_get_int(it); + if (t != NULL && config_setting_is_group(t)) { + for (int i = 0; i < MAX_SKILL_LEVEL; i++) { + char lv[6]; // Big enough to contain "Lv999" in case of custom MAX_SKILL_LEVEL. + safesnprintf(lv, sizeof(lv), "Lv%d", i + 1); + int unit_range; + + if (libconfig->setting_lookup_int(t, lv, &unit_range) == CONFIG_TRUE) { + if (unit_range >= -1 && unit_range <= UCHAR_MAX) + sk->unit_range[i] = unit_range; + else + ShowWarning("%s: Invalid unit range %d specified in level %d for skill ID %d in %s! Minimum is -1, maximum is %d. Defaulting to 0...\n", + __func__, unit_range, i + 1, sk->nameid, conf->file, UCHAR_MAX); } } + + return; + } + + int unit_range; + + if (libconfig->setting_lookup_int(conf, "Range", &unit_range) == CONFIG_TRUE) { + if (unit_range >= -1 && unit_range <= UCHAR_MAX) + skill->level_set_value(sk->unit_range, unit_range); + else + ShowWarning("%s: Invalid unit range %d specified for skill ID %d in %s! Minimum is -1, maximum is %d. Defaulting to 0...\n", + __func__, unit_range, sk->nameid, conf->file, UCHAR_MAX); } } /** - * 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 - */ -static void skill_validate_unit_target(struct config_setting_t *conf, struct s_skill_db *sk) + * Validates a skill's unit interval when reading the skill DB. + * + * @param conf The libconfig settings block which contains the skill's data. + * @param sk The s_skill_db struct where the unit interval should be set it. + * + **/ +static void skill_validate_unit_interval(struct config_setting_t *conf, struct s_skill_db *sk) { - const char *type = NULL; - + nullpo_retv(conf); nullpo_retv(sk); - 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; + sk->unit_interval = 0; - //By default, target just characters. - sk->unit_target |= BL_CHAR; + int unit_interval; - 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; + if (libconfig->setting_lookup_int(conf, "Interval", &unit_interval) == CONFIG_TRUE) { + if (unit_interval >= INFINITE_DURATION) + sk->unit_interval = unit_interval; + else + ShowWarning("%s: Invalid unit interval %d specified for skill ID %d in %s! Must be greater than or equal to %d. Defaulting to 0...\n", + __func__, unit_interval, sk->nameid, conf->file, INFINITE_DURATION); + } } /** @@ -21046,8 +22407,8 @@ static void skill_validate_unit_target(struct config_setting_t *conf, struct s_s */ static int skill_validate_unit_flag_sub(const char *type, bool on, struct s_skill_db *sk) { - nullpo_ret(type); - nullpo_ret(sk); + nullpo_retr(1, type); + nullpo_retr(1, sk); if (strcmpi(type, "UF_DEFNOTENEMY") == 0) { if (on) { sk->unit_flag |= UF_DEFNOTENEMY; @@ -21142,9 +22503,13 @@ static int skill_validate_unit_flag_sub(const char *type, bool on, struct s_skil */ static void skill_validate_unit_flag(struct config_setting_t *conf, struct s_skill_db *sk) { + nullpo_retv(conf); + nullpo_retv(sk); + + sk->unit_flag = 0; + struct config_setting_t *t = NULL; - nullpo_retv(sk); if ((t=libconfig->setting_get_member(conf, "Flag")) && config_setting_is_group(t)) { int j=0; struct config_setting_t *tt = NULL; @@ -21152,51 +22517,94 @@ static void skill_validate_unit_flag(struct config_setting_t *conf, struct s_ski 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); + ShowWarning("%s: Invalid unit flag %s specified for skill ID %d in %s! Skipping flag...\n", + __func__, name, sk->nameid, conf->file); } } } + /** - * Validate additional field settings via plugins + * Validates the "Unit > Target" flag * when parsing skill_db.conf * @param conf struct, pointer to the skill configuration - * @param sk struct, struct, pointer to s_skill_db - * @return (void) + * @param sk struct, pointer to s_skill_db + * @return void */ -static void skill_validate_additional_fields(struct config_setting_t *conf, struct s_skill_db *sk) +static void skill_validate_unit_target(struct config_setting_t *conf, struct s_skill_db *sk) { - // Does nothing like a boss. *cough* plugins *cough* + nullpo_retv(conf); + nullpo_retv(sk); + + sk->unit_target = BCT_NOONE; + + 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_SAMEGUILD; + else if(strcmpi(type, "None") != 0) + ShowWarning("%s: Invalid unit target %s specified for skill ID %d in %s! Defaulting to None...\n", + __func__, type, sk->nameid, conf->file); + } + + 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 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. - */ -static bool skill_validate_skilldb(struct s_skill_db *sk, const char *source) + * Validates a skill's unit data when reading the skill DB. + * + * @param conf The libconfig settings block which contains the skill's data. + * @param sk The s_skill_db struct where the unit data should be set it. + * + **/ +static void skill_validate_unit(struct config_setting_t *conf, struct s_skill_db *sk) { - int idx; + nullpo_retv(conf); + nullpo_retv(sk); - nullpo_retr(false, sk); - 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 <= 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; - } + struct config_setting_t *t = libconfig->setting_get_member(conf, "Unit"); - /* 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); + if (t != NULL && config_setting_is_group(t)) { + skill->validate_unit_id(t, sk); + skill->validate_unit_layout(t, sk); + skill->validate_unit_range(t, sk); + skill->validate_unit_interval(t, sk); + skill->validate_unit_flag(t, sk); + skill->validate_unit_target(t, sk); + } +} - return true; +/** + * 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) + */ +static void skill_validate_additional_fields(struct config_setting_t *conf, struct s_skill_db *sk) +{ + // Does nothing like a boss. *cough* plugins *cough* } /** @@ -21211,7 +22619,6 @@ static bool skill_read_skilldb(const char *filename) struct config_setting_t *sk, *conf; char filepath[256]; int count=0, index=0; - bool duplicate[MAX_SKILL_DB] = {0}; nullpo_retr(false, filename); @@ -21229,221 +22636,56 @@ static bool skill_read_skilldb(const char *filename) } 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); + /** Validate mandatory fields. **/ + skill->validate_id(conf, &tmp_db, index); + if (tmp_db.nameid == 0) 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); + skill->validate_name(conf, &tmp_db); + if (*tmp_db.name == '\0') 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); + skill->validate_max_level(conf, &tmp_db); + if (tmp_db.max == 0) 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 */ + /** Validate optional fields. **/ + skill->validate_description(conf, &tmp_db); + skill->validate_range(conf, &tmp_db); 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) == CONFIG_FALSE) - tmp_db.castcancel = 0; - - /* 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_splash_range(conf, &tmp_db); + skill->validate_number_of_hits(conf, &tmp_db); + skill->validate_interrupt_cast(conf, &tmp_db); + skill->validate_cast_def_rate(conf, &tmp_db); + skill->validate_number_of_instances(conf, &tmp_db); + skill->validate_knock_back_tiles(conf, &tmp_db); + skill->validate_cast_time(conf, &tmp_db); + skill->validate_act_delay(conf, &tmp_db); + skill->validate_walk_delay(conf, &tmp_db); + skill->validate_skill_data1(conf, &tmp_db); + skill->validate_skill_data2(conf, &tmp_db); + skill->validate_cooldown(conf, &tmp_db); + skill->validate_fixed_cast_time(conf, &tmp_db); skill->validate_castnodex(conf, &tmp_db, false); skill->validate_castnodex(conf, &tmp_db, true); + skill->validate_requirements(conf, &tmp_db); + skill->validate_unit(conf, &tmp_db); - /** - * 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, "HPRateCost"))) - skill->config_set_level(tt, tmp_db.hp_rate); - - /* SP Rate */ - if ((tt = libconfig->setting_get_member(t, "SPRateCost"))) - 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(t, &tmp_db); - - /* Ammunition Types */ - skill->validate_ammotype(t, &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 */ + /** Validate 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++; + /** Add the skill. **/ + skill->dbs->db[skill->get_index(tmp_db.nameid)] = tmp_db; + strdb_iput(skill->name2id_db, tmp_db.name, tmp_db.nameid); + script->set_constant2(tmp_db.name, tmp_db.nameid, false, false); + count++; } libconfig->destroy(&skilldb); @@ -21453,9 +22695,6 @@ static bool skill_read_skilldb(const char *filename) return true; } -#undef skilldb_duplicate_warning -#undef skilldb_invalid_error - /*=============================== * DB reading. * produce_db.txt @@ -21785,24 +23024,57 @@ void skill_defaults(void) skill->init_unit_layout = skill_init_unit_layout; skill->init_unit_layout_unknown = skill_init_unit_layout_unknown; /* Skill DB Libconfig */ + skill->validate_id = skill_validate_id; + skill->name_contains_invalid_character = skill_name_contains_invalid_character; + skill->validate_name = skill_validate_name; + skill->validate_max_level = skill_validate_max_level; + skill->validate_description = skill_validate_description; + skill->validate_range = skill_validate_range; 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_attacktype = skill_validate_attacktype; + skill->validate_element = skill_validate_element; skill->validate_damagetype = skill_validate_damagetype; + skill->validate_splash_range = skill_validate_splash_range; + skill->validate_number_of_hits = skill_validate_number_of_hits; + skill->validate_interrupt_cast = skill_validate_interrupt_cast; + skill->validate_cast_def_rate = skill_validate_cast_def_rate; + skill->validate_number_of_instances = skill_validate_number_of_instances; + skill->validate_knock_back_tiles = skill_validate_knock_back_tiles; + skill->validate_cast_time = skill_validate_cast_time; + skill->validate_act_delay = skill_validate_act_delay; + skill->validate_walk_delay = skill_validate_walk_delay; + skill->validate_skill_data1 = skill_validate_skill_data1; + skill->validate_skill_data2 = skill_validate_skill_data2; + skill->validate_cooldown = skill_validate_cooldown; + skill->validate_fixed_cast_time = skill_validate_fixed_cast_time; skill->validate_castnodex = skill_validate_castnodex; + skill->validate_hp_cost = skill_validate_hp_cost; + skill->validate_sp_cost = skill_validate_sp_cost; + skill->validate_hp_rate_cost = skill_validate_hp_rate_cost; + skill->validate_sp_rate_cost = skill_validate_sp_rate_cost; + skill->validate_max_hp_trigger = skill_validate_max_hp_trigger; + skill->validate_zeny_cost = skill_validate_zeny_cost; + skill->validate_weapontype_sub = skill_validate_weapontype_sub; skill->validate_weapontype = skill_validate_weapontype; + skill->validate_ammotype_sub = skill_validate_ammotype_sub; skill->validate_ammotype = skill_validate_ammotype; + skill->validate_ammo_amount = skill_validate_ammo_amount; skill->validate_state = skill_validate_state; + skill->validate_spirit_sphere_cost = skill_validate_spirit_sphere_cost; skill->validate_item_requirements = skill_validate_item_requirements; - skill->validate_unit_target = skill_validate_unit_target; + skill->validate_requirements = skill_validate_requirements; + skill->validate_unit_id_sub = skill_validate_unit_id_sub; + skill->validate_unit_id = skill_validate_unit_id; + skill->validate_unit_layout = skill_validate_unit_layout; + skill->validate_unit_range = skill_validate_unit_range; + skill->validate_unit_interval = skill_validate_unit_interval; + skill->validate_unit_flag_sub = skill_validate_unit_flag_sub; skill->validate_unit_flag = skill_validate_unit_flag; + skill->validate_unit_target = skill_validate_unit_target; + skill->validate_unit = skill_validate_unit; skill->validate_additional_fields = skill_validate_additional_fields; - skill->validate_skilldb = skill_validate_skilldb; - skill->validate_weapontype_sub = skill_validate_weapontype_sub; - skill->validate_ammotype_sub = skill_validate_ammotype_sub; - skill->validate_unit_flag_sub = skill_validate_unit_flag_sub; skill->read_skilldb = skill_read_skilldb; skill->config_set_level = skill_config_set_level; skill->level_set_value = skill_level_set_value; diff --git a/src/map/skill.h b/src/map/skill.h index 651a355cf..66441f9f4 100644 --- a/src/map/skill.h +++ b/src/map/skill.h @@ -1745,7 +1745,7 @@ struct skill_condition { // Database skills struct s_skill_db { int nameid; - char name[MAX_SKILL_NAME_LENGTH]; + char name[MAX_SKILL_NAME_LENGTH + 1]; char desc[40]; int range[MAX_SKILL_LEVEL],hit,inf,element[MAX_SKILL_LEVEL],nk,splash[MAX_SKILL_LEVEL],max; int num[MAX_SKILL_LEVEL]; @@ -2115,24 +2115,57 @@ struct skill_interface { int (*unit_timer_sub) (union DBKey key, struct DBData *data, va_list ap); void (*init_unit_layout) (void); void (*init_unit_layout_unknown) (int skill_idx, int pos); + void (*validate_id) (struct config_setting_t *conf, struct s_skill_db *sk, int conf_index); + bool (*name_contains_invalid_character) (const char *name); + void (*validate_name) (struct config_setting_t *conf, struct s_skill_db *sk); + void (*validate_max_level) (struct config_setting_t *conf, struct s_skill_db *sk); + void (*validate_description) (struct config_setting_t *conf, struct s_skill_db *sk); + void (*validate_range) (struct config_setting_t *conf, struct s_skill_db *sk); void (*validate_hittype) (struct config_setting_t *conf, struct s_skill_db *sk); void (*validate_skilltype) (struct config_setting_t *conf, struct s_skill_db *sk); + void (*validate_skillinfo) (struct config_setting_t *conf, struct s_skill_db *sk); void (*validate_attacktype) (struct config_setting_t *conf, struct s_skill_db *sk); void (*validate_element) (struct config_setting_t *conf, struct s_skill_db *sk); - void (*validate_skillinfo) (struct config_setting_t *conf, struct s_skill_db *sk); void (*validate_damagetype) (struct config_setting_t *conf, struct s_skill_db *sk); + void (*validate_splash_range) (struct config_setting_t *conf, struct s_skill_db *sk); + void (*validate_number_of_hits) (struct config_setting_t *conf, struct s_skill_db *sk); + void (*validate_interrupt_cast) (struct config_setting_t *conf, struct s_skill_db *sk); + void (*validate_cast_def_rate) (struct config_setting_t *conf, struct s_skill_db *sk); + void (*validate_number_of_instances) (struct config_setting_t *conf, struct s_skill_db *sk); + void (*validate_knock_back_tiles) (struct config_setting_t *conf, struct s_skill_db *sk); + void (*validate_cast_time) (struct config_setting_t *conf, struct s_skill_db *sk); + void (*validate_act_delay) (struct config_setting_t *conf, struct s_skill_db *sk); + void (*validate_walk_delay) (struct config_setting_t *conf, struct s_skill_db *sk); + void (*validate_skill_data1) (struct config_setting_t *conf, struct s_skill_db *sk); + void (*validate_skill_data2) (struct config_setting_t *conf, struct s_skill_db *sk); + void (*validate_cooldown) (struct config_setting_t *conf, struct s_skill_db *sk); + void (*validate_fixed_cast_time) (struct config_setting_t *conf, struct s_skill_db *sk); void (*validate_castnodex) (struct config_setting_t *conf, struct s_skill_db *sk, bool delay); + void (*validate_hp_cost) (struct config_setting_t *conf, struct s_skill_db *sk); + void (*validate_sp_cost) (struct config_setting_t *conf, struct s_skill_db *sk); + void (*validate_hp_rate_cost) (struct config_setting_t *conf, struct s_skill_db *sk); + void (*validate_sp_rate_cost) (struct config_setting_t *conf, struct s_skill_db *sk); + void (*validate_max_hp_trigger) (struct config_setting_t *conf, struct s_skill_db *sk); + void (*validate_zeny_cost) (struct config_setting_t *conf, struct s_skill_db *sk); + int (*validate_weapontype_sub) (const char *type, bool on, struct s_skill_db *sk); void (*validate_weapontype) (struct config_setting_t *conf, struct s_skill_db *sk); + int (*validate_ammotype_sub) (const char *type, bool on, struct s_skill_db *sk); void (*validate_ammotype) (struct config_setting_t *conf, struct s_skill_db *sk); + void (*validate_ammo_amount) (struct config_setting_t *conf, struct s_skill_db *sk); void (*validate_state) (struct config_setting_t *conf, struct s_skill_db *sk); + void (*validate_spirit_sphere_cost) (struct config_setting_t *conf, struct s_skill_db *sk); void (*validate_item_requirements) (struct config_setting_t *conf, struct s_skill_db *sk); - void (*validate_unit_target) (struct config_setting_t *conf, struct s_skill_db *sk); + void (*validate_requirements) (struct config_setting_t *conf, struct s_skill_db *sk); + int (*validate_unit_id_sub) (int unit_id); + void (*validate_unit_id) (struct config_setting_t *conf, struct s_skill_db *sk); + void (*validate_unit_layout) (struct config_setting_t *conf, struct s_skill_db *sk); + void (*validate_unit_range) (struct config_setting_t *conf, struct s_skill_db *sk); + void (*validate_unit_interval) (struct config_setting_t *conf, struct s_skill_db *sk); + int (*validate_unit_flag_sub) (const char *type, bool on, struct s_skill_db *sk); void (*validate_unit_flag) (struct config_setting_t *conf, struct s_skill_db *sk); + void (*validate_unit_target) (struct config_setting_t *conf, struct s_skill_db *sk); + void (*validate_unit) (struct config_setting_t *conf, struct s_skill_db *sk); void (*validate_additional_fields) (struct config_setting_t *conf, struct s_skill_db *sk); - bool (*validate_skilldb) (struct s_skill_db *skt, const char *source); - int (*validate_weapontype_sub) (const char *type, bool on, struct s_skill_db *sk); - int (*validate_ammotype_sub) (const char *type, bool on, struct s_skill_db *sk); - int (*validate_unit_flag_sub) (const char *type, bool on, struct s_skill_db *sk); 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); diff --git a/src/plugins/HPMHooking/HPMHooking.Defs.inc b/src/plugins/HPMHooking/HPMHooking.Defs.inc index 50e64e53c..ae47330f6 100644 --- a/src/plugins/HPMHooking/HPMHooking.Defs.inc +++ b/src/plugins/HPMHooking/HPMHooking.Defs.inc @@ -7580,8 +7580,6 @@ typedef void (*HPMHOOK_pre_skill_validate_unit_flag) (struct config_setting_t ** typedef void (*HPMHOOK_post_skill_validate_unit_flag) (struct config_setting_t *conf, struct s_skill_db *sk); typedef void (*HPMHOOK_pre_skill_validate_additional_fields) (struct config_setting_t **conf, struct s_skill_db **sk); typedef void (*HPMHOOK_post_skill_validate_additional_fields) (struct config_setting_t *conf, struct s_skill_db *sk); -typedef bool (*HPMHOOK_pre_skill_validate_skilldb) (struct s_skill_db **skt, const char **source); -typedef bool (*HPMHOOK_post_skill_validate_skilldb) (bool retVal___, struct s_skill_db *skt, const char *source); typedef int (*HPMHOOK_pre_skill_validate_weapontype_sub) (const char **type, bool *on, struct s_skill_db **sk); typedef int (*HPMHOOK_post_skill_validate_weapontype_sub) (int retVal___, const char *type, bool on, struct s_skill_db *sk); typedef int (*HPMHOOK_pre_skill_validate_ammotype_sub) (const char **type, bool *on, struct s_skill_db **sk); diff --git a/src/plugins/HPMHooking/HPMHooking_map.HPMHooksCore.inc b/src/plugins/HPMHooking/HPMHooking_map.HPMHooksCore.inc index 0c245dee3..77f4dc18c 100644 --- a/src/plugins/HPMHooking/HPMHooking_map.HPMHooksCore.inc +++ b/src/plugins/HPMHooking/HPMHooking_map.HPMHooksCore.inc @@ -6104,8 +6104,6 @@ struct { struct HPMHookPoint *HP_skill_validate_unit_flag_post; struct HPMHookPoint *HP_skill_validate_additional_fields_pre; struct HPMHookPoint *HP_skill_validate_additional_fields_post; - struct HPMHookPoint *HP_skill_validate_skilldb_pre; - struct HPMHookPoint *HP_skill_validate_skilldb_post; struct HPMHookPoint *HP_skill_validate_weapontype_sub_pre; struct HPMHookPoint *HP_skill_validate_weapontype_sub_post; struct HPMHookPoint *HP_skill_validate_ammotype_sub_pre; @@ -13029,8 +13027,6 @@ struct { int HP_skill_validate_unit_flag_post; int HP_skill_validate_additional_fields_pre; int HP_skill_validate_additional_fields_post; - int HP_skill_validate_skilldb_pre; - int HP_skill_validate_skilldb_post; int HP_skill_validate_weapontype_sub_pre; int HP_skill_validate_weapontype_sub_post; int HP_skill_validate_ammotype_sub_pre; diff --git a/src/plugins/HPMHooking/HPMHooking_map.HookingPoints.inc b/src/plugins/HPMHooking/HPMHooking_map.HookingPoints.inc index 2d35608f0..138c69de2 100644 --- a/src/plugins/HPMHooking/HPMHooking_map.HookingPoints.inc +++ b/src/plugins/HPMHooking/HPMHooking_map.HookingPoints.inc @@ -3121,7 +3121,6 @@ struct HookingPointData HookingPoints[] = { { HP_POP(skill->validate_unit_target, HP_skill_validate_unit_target) }, { HP_POP(skill->validate_unit_flag, HP_skill_validate_unit_flag) }, { HP_POP(skill->validate_additional_fields, HP_skill_validate_additional_fields) }, - { HP_POP(skill->validate_skilldb, HP_skill_validate_skilldb) }, { HP_POP(skill->validate_weapontype_sub, HP_skill_validate_weapontype_sub) }, { HP_POP(skill->validate_ammotype_sub, HP_skill_validate_ammotype_sub) }, { HP_POP(skill->validate_unit_flag_sub, HP_skill_validate_unit_flag_sub) }, diff --git a/src/plugins/HPMHooking/HPMHooking_map.Hooks.inc b/src/plugins/HPMHooking/HPMHooking_map.Hooks.inc index 08d807f1c..f675caba2 100644 --- a/src/plugins/HPMHooking/HPMHooking_map.Hooks.inc +++ b/src/plugins/HPMHooking/HPMHooking_map.Hooks.inc @@ -81604,33 +81604,6 @@ void HP_skill_validate_additional_fields(struct config_setting_t *conf, struct s } return; } -bool HP_skill_validate_skilldb(struct s_skill_db *skt, const char *source) { - int hIndex = 0; - bool retVal___ = false; - if (HPMHooks.count.HP_skill_validate_skilldb_pre > 0) { - bool (*preHookFunc) (struct s_skill_db **skt, const char **source); - *HPMforce_return = false; - for (hIndex = 0; hIndex < HPMHooks.count.HP_skill_validate_skilldb_pre; hIndex++) { - preHookFunc = HPMHooks.list.HP_skill_validate_skilldb_pre[hIndex].func; - retVal___ = preHookFunc(&skt, &source); - } - if (*HPMforce_return) { - *HPMforce_return = false; - return retVal___; - } - } - { - retVal___ = HPMHooks.source.skill.validate_skilldb(skt, source); - } - if (HPMHooks.count.HP_skill_validate_skilldb_post > 0) { - bool (*postHookFunc) (bool retVal___, struct s_skill_db *skt, const char *source); - for (hIndex = 0; hIndex < HPMHooks.count.HP_skill_validate_skilldb_post; hIndex++) { - postHookFunc = HPMHooks.list.HP_skill_validate_skilldb_post[hIndex].func; - retVal___ = postHookFunc(retVal___, skt, source); - } - } - return retVal___; -} int HP_skill_validate_weapontype_sub(const char *type, bool on, struct s_skill_db *sk) { int hIndex = 0; int retVal___ = 0; -- cgit v1.2.3-60-g2f50 From 17ef88dded9602e23ebbc64510c8f9546896b4a6 Mon Sep 17 00:00:00 2001 From: Kenpachi Developer Date: Sat, 16 May 2020 12:15:28 +0200 Subject: Change maximum length of skill descriptions to 50 --- src/map/skill.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/map/skill.h b/src/map/skill.h index 66441f9f4..946a3583c 100644 --- a/src/map/skill.h +++ b/src/map/skill.h @@ -60,6 +60,10 @@ struct status_change_entry; #define MAX_SKILLUNITGROUPTICKSET 25 #define MAX_SKILL_NAME_LENGTH 32 +#ifndef MAX_SKILL_DESC_LENGTH + #define MAX_SKILL_DESC_LENGTH 50 +#endif + // Custom Skill Ranges is used in skill_get_index, to allocate indexes based on ID and gaps between 2 SkillID #ifndef CUSTOM_SKILL_RANGES #define CUSTOM_SKILL_RANGES @@ -1746,7 +1750,7 @@ struct skill_condition { struct s_skill_db { int nameid; char name[MAX_SKILL_NAME_LENGTH + 1]; - char desc[40]; + char desc[MAX_SKILL_DESC_LENGTH + 1]; int range[MAX_SKILL_LEVEL],hit,inf,element[MAX_SKILL_LEVEL],nk,splash[MAX_SKILL_LEVEL],max; int num[MAX_SKILL_LEVEL]; int cast[MAX_SKILL_LEVEL],walkdelay[MAX_SKILL_LEVEL],delay[MAX_SKILL_LEVEL]; -- cgit v1.2.3-60-g2f50 From 59e49872b4738eb4ae19ec3f4cce574441345c4a Mon Sep 17 00:00:00 2001 From: Kenpachi Developer Date: Sat, 16 May 2020 12:16:01 +0200 Subject: Change maximum skill level to 20 --- src/map/skill.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/map/skill.h b/src/map/skill.h index 946a3583c..30b70d037 100644 --- a/src/map/skill.h +++ b/src/map/skill.h @@ -50,7 +50,7 @@ struct status_change_entry; #define MAX_ARROW_RESOURCE 5 #define MAX_SKILL_ABRA_DB 210 #define MAX_SKILL_IMPROVISE_DB 30 -#define MAX_SKILL_LEVEL 10 +#define MAX_SKILL_LEVEL 20 #define MAX_SKILL_UNIT_LAYOUT 45 #define MAX_SQUARE_LAYOUT 5 // 11*11 Placement of a maximum unit #define MAX_SKILL_UNIT_COUNT ((MAX_SQUARE_LAYOUT*2+1)*(MAX_SQUARE_LAYOUT*2+1)) -- cgit v1.2.3-60-g2f50 From 14399badde8d4a041640b27efe1b1ee8dda372b1 Mon Sep 17 00:00:00 2001 From: Kenpachi Developer Date: Sat, 16 May 2020 15:54:25 +0200 Subject: Remove FixedCastTime settings from pre-RE skill DB --- db/pre-re/skill_db.conf | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/db/pre-re/skill_db.conf b/db/pre-re/skill_db.conf index 1222aa1a0..7d319ba69 100644 --- a/db/pre-re/skill_db.conf +++ b/db/pre-re/skill_db.conf @@ -32602,7 +32602,6 @@ skill_db: ( InterruptCast: true CastTime: 1000 AfterCastActDelay: 1000 - FixedCastTime: 0 Requirements: { SPCost: 10 } @@ -32623,7 +32622,6 @@ skill_db: ( AfterCastActDelay: 1000 SkillData1: -1 CoolDown: 15000 - FixedCastTime: 0 Requirements: { SPCost: 30 } @@ -32659,7 +32657,6 @@ skill_db: ( Lv9: 1 Lv10: 1 } - FixedCastTime: 0 Requirements: { SPCost: { Lv1: 20 @@ -32691,7 +32688,6 @@ skill_db: ( AfterCastActDelay: 1000 SkillData1: 6000 CoolDown: 15000 - FixedCastTime: 0 Requirements: { SPCost: 10 } @@ -32735,7 +32731,6 @@ skill_db: ( Lv9: 18000 Lv10: 20000 } - FixedCastTime: 0 Requirements: { SPCost: { Lv1: 30 @@ -32787,7 +32782,6 @@ skill_db: ( CastTime: 2500 AfterCastActDelay: 1000 SkillData1: 10000 - FixedCastTime: 0 Requirements: { SPCost: { Lv1: 40 @@ -32835,7 +32829,6 @@ skill_db: ( Lv4: 6000 Lv5: 7000 } - FixedCastTime: 0 Requirements: { SPCost: { Lv1: 40 @@ -32909,7 +32902,6 @@ skill_db: ( Lv5: 6000 } - FixedCastTime: 0 Requirements: { SPCost: { Lv1: 20 @@ -32961,7 +32953,6 @@ skill_db: ( Lv10: 25000 } CoolDown: 3000 - FixedCastTime: 0 Requirements: { SPCost: { Lv1: 10 @@ -33002,7 +32993,6 @@ skill_db: ( } SkillInstances: 1 AfterCastActDelay: 1000 - FixedCastTime: 0 Requirements: { SPCost: 1 } @@ -33031,7 +33021,6 @@ skill_db: ( AfterCastActDelay: 1000 SkillData1: 9000 SkillData2: 1000 - FixedCastTime: 0 Requirements: { SPCost: { Lv1: 10 @@ -33063,7 +33052,6 @@ skill_db: ( InterruptCast: true CastTime: 2500 AfterCastActDelay: 1000 - FixedCastTime: 0 Requirements: { SPCost: { Lv1: 10 @@ -33093,7 +33081,6 @@ skill_db: ( NumberOfHits: -5 InterruptCast: true AfterCastActDelay: 1000 - FixedCastTime: 0 Requirements: { SPCost: 1 } @@ -33126,7 +33113,6 @@ skill_db: ( Lv10: 150000 } CoolDown: 10000 - FixedCastTime: 0 Requirements: { SPCost: { Lv1: 12 @@ -33186,7 +33172,6 @@ skill_db: ( AfterCastActDelay: 1000 SkillData1: 1000 CoolDown: 8000 - FixedCastTime: 0 Requirements: { SPCost: { Lv1: 15 @@ -33237,7 +33222,6 @@ skill_db: ( Lv9: 24000 Lv10: 26000 } - FixedCastTime: 0 Requirements: { SPCost: { Lv1: 20 @@ -33270,7 +33254,6 @@ skill_db: ( AfterCastActDelay: 1000 SkillData1: 30000 CoolDown: 20000 - FixedCastTime: 0 Requirements: { SPCost: { Lv1: 20 @@ -33315,7 +33298,6 @@ skill_db: ( Lv10: 330000 } CoolDown: 10000 - FixedCastTime: 0 Requirements: { SPCost: { Lv1: 44 @@ -33353,7 +33335,6 @@ skill_db: ( AfterCastActDelay: 1000 SkillData1: 120000 CoolDown: 7000 - FixedCastTime: 0 Requirements: { SPCost: { Lv1: 22 -- cgit v1.2.3-60-g2f50 From 1ba5bcab0ee34471b0b7d1ff3fc4f04a2ea79f85 Mon Sep 17 00:00:00 2001 From: Kenpachi Developer Date: Sat, 16 May 2020 19:46:36 +0200 Subject: Cap SkillInstances to 25 (MAX_SKILLUNITGROUP) in (pre-)RE skill DB --- db/pre-re/skill_db.conf | 4 ++-- db/re/skill_db.conf | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/db/pre-re/skill_db.conf b/db/pre-re/skill_db.conf index 7d319ba69..f65e35357 100644 --- a/db/pre-re/skill_db.conf +++ b/db/pre-re/skill_db.conf @@ -27587,7 +27587,7 @@ skill_db: ( Lv7: 20 Lv8: 22 Lv9: 24 - Lv10: 26 + Lv10: 25 } CastTime: 1000 AfterCastActDelay: 1000 @@ -27642,7 +27642,7 @@ skill_db: ( Lv7: 20 Lv8: 22 Lv9: 24 - Lv10: 26 + Lv10: 25 } CastTime: 1000 AfterCastActDelay: 1000 diff --git a/db/re/skill_db.conf b/db/re/skill_db.conf index 8cb55d918..4bbdd19f3 100644 --- a/db/re/skill_db.conf +++ b/db/re/skill_db.conf @@ -28298,7 +28298,7 @@ skill_db: ( Lv7: 20 Lv8: 22 Lv9: 24 - Lv10: 26 + Lv10: 25 } CastTime: 1000 AfterCastActDelay: 1000 @@ -28353,7 +28353,7 @@ skill_db: ( Lv7: 20 Lv8: 22 Lv9: 24 - Lv10: 26 + Lv10: 25 } CastTime: 1000 AfterCastActDelay: 1000 -- cgit v1.2.3-60-g2f50 From b7651c05a0beffd3953341dec67f85f75e216e66 Mon Sep 17 00:00:00 2001 From: Kenpachi Developer Date: Sat, 16 May 2020 15:34:13 +0200 Subject: Apply code style to several functions related to skill DB reading --- src/map/skill.c | 1323 +++++++++++++++++++++++++++---------------------------- 1 file changed, 655 insertions(+), 668 deletions(-) diff --git a/src/map/skill.c b/src/map/skill.c index d3a7190f4..78efaa348 100644 --- a/src/map/skill.c +++ b/src/map/skill.c @@ -20349,6 +20349,13 @@ static void skill_validate_range(struct config_setting_t *conf, struct s_skill_d } } +/** + * Validates a skill's hit type when reading the skill DB. + * + * @param conf The libconfig settings block which contains the skill's data. + * @param sk The s_skill_db struct where the hit type should be set it. + * + **/ static void skill_validate_hittype(struct config_setting_t *conf, struct s_skill_db *sk) { nullpo_retv(conf); @@ -20356,28 +20363,26 @@ static void skill_validate_hittype(struct config_setting_t *conf, struct s_skill sk->hit = BDT_NORMAL; - const char *type = NULL; + const char *hit_type; - if (libconfig->setting_lookup_string(conf, "Hit", &type)) { - if (strcmpi(type, "BDT_SKILL") == 0) { + if (libconfig->setting_lookup_string(conf, "Hit", &hit_type) == CONFIG_TRUE) { + if (strcmpi(hit_type, "BDT_SKILL") == 0) sk->hit = BDT_SKILL; - } else if (strcmpi(type, "BDT_MULTIHIT") == 0) { + else if (strcmpi(hit_type, "BDT_MULTIHIT") == 0) sk->hit = BDT_MULTIHIT; - } else if (strcmpi(type, "BDT_NORMAL") == 0) { - sk->hit = BDT_NORMAL; - } else { + else if (strcmpi(hit_type, "BDT_NORMAL") != 0) ShowWarning("%s: Invalid hit type %s specified for skill ID %d in %s! Defaulting to BDT_NORMAL...\n", - __func__, type, sk->nameid, conf->file); - } + __func__, hit_type, sk->nameid, conf->file); } } /** - * Validates "SkillType" when reading skill_db.conf - * @param conf struct, pointer to skill configuration - * @param sk struct, pointer to s_skill_db - * @return (void) - */ + * Validates a skill's types when reading the skill DB. + * + * @param conf The libconfig settings block which contains the skill's data. + * @param sk The s_skill_db struct where the types should be set it. + * + **/ static void skill_validate_skilltype(struct config_setting_t *conf, struct s_skill_db *sk) { nullpo_retv(conf); @@ -20385,58 +20390,56 @@ static void skill_validate_skilltype(struct config_setting_t *conf, struct s_ski sk->inf = INF_NONE; - struct config_setting_t *t = NULL, *tt = NULL; + struct config_setting_t *t = libconfig->setting_get_member(conf, "SkillType"); - 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); + if (t != NULL && config_setting_is_group(t)) { + struct config_setting_t *tt; + int i = 0; + + while ((tt = libconfig->setting_get_elem(t, i++)) != NULL) { + const char *skill_type = config_setting_name(tt); bool on = libconfig->setting_get_bool_real(tt); - if (strcmpi(type, "Enemy") == 0) { - if (on) { + if (strcmpi(skill_type, "Enemy") == 0) { + if (on) sk->inf |= INF_ATTACK_SKILL; - } else { + else sk->inf &= ~INF_ATTACK_SKILL; - } - } else if (strcmpi(type, "Place") == 0) { - if (on) { + } else if (strcmpi(skill_type, "Place") == 0) { + if (on) sk->inf |= INF_GROUND_SKILL; - } else { + else sk->inf &= ~INF_GROUND_SKILL; - } - } else if (strcmpi(type, "Self") == 0) { - if (on) { + } else if (strcmpi(skill_type, "Self") == 0) { + if (on) sk->inf |= INF_SELF_SKILL; - } else { + else sk->inf &= ~INF_SELF_SKILL; - } - } else if (strcmpi(type, "Friend") == 0) { - if (on) { + } else if (strcmpi(skill_type, "Friend") == 0) { + if (on) sk->inf |= INF_SUPPORT_SKILL; - } else { + else sk->inf &= ~INF_SUPPORT_SKILL; - } - } else if (strcmpi(type, "Trap") == 0) { - if (on) { + } else if (strcmpi(skill_type, "Trap") == 0) { + if (on) sk->inf |= INF_TARGET_TRAP; - } else { + else sk->inf &= ~INF_TARGET_TRAP; - } - } else if (strcmpi(type, "Passive") != 0) { + } else if (strcmpi(skill_type, "Passive") != 0) { ShowWarning("%s: Invalid skill type %s specified for skill ID %d in %s! Skipping type...\n", - __func__, type, sk->nameid, conf->file); + __func__, skill_type, sk->nameid, conf->file); } } } } /** - * Validates "SkillInfo" when reading skill_db.conf - * @param conf struct, pointer to skill configuration - * @param sk struct, pointer to s_skill_db - * @return (void) - */ + * Validates a skill's sub-types when reading the skill DB. + * + * @param conf The libconfig settings block which contains the skill's data. + * @param sk The s_skill_db struct where the sub-types should be set it. + * + **/ static void skill_validate_skillinfo(struct config_setting_t *conf, struct s_skill_db *sk) { nullpo_retv(conf); @@ -20444,154 +20447,136 @@ static void skill_validate_skillinfo(struct config_setting_t *conf, struct s_ski sk->inf2 = INF2_NONE; - struct config_setting_t *t = NULL, *tt = NULL; + struct config_setting_t *t = libconfig->setting_get_member(conf, "SkillInfo"); - 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); + if (t != NULL && config_setting_is_group(t)) { + struct config_setting_t *tt; + int i = 0; + + while ((tt = libconfig->setting_get_elem(t, i++)) != NULL) { + const char *skill_info = config_setting_name(tt); bool on = libconfig->setting_get_bool_real(tt); - if (strcmpi(type, "Quest") == 0) { - if (on) { + if (strcmpi(skill_info, "Quest") == 0) { + if (on) sk->inf2 |= INF2_QUEST_SKILL; - } else { + else sk->inf2 &= ~INF2_QUEST_SKILL; - } - } else if (strcmpi(type, "NPC") == 0) { - if (on) { + } else if (strcmpi(skill_info, "NPC") == 0) { + if (on) sk->inf2 |= INF2_NPC_SKILL; - } else { + else sk->inf2 &= ~INF2_NPC_SKILL; - } - } else if (strcmpi(type, "Wedding") == 0) { - if (on) { + } else if (strcmpi(skill_info, "Wedding") == 0) { + if (on) sk->inf2 |= INF2_WEDDING_SKILL; - } else { + else sk->inf2 &= ~INF2_WEDDING_SKILL; - } - } else if (strcmpi(type, "Spirit") == 0) { - if (on) { + } else if (strcmpi(skill_info, "Spirit") == 0) { + if (on) sk->inf2 |= INF2_SPIRIT_SKILL; - } else { + else sk->inf2 &= ~INF2_SPIRIT_SKILL; - } - } else if (strcmpi(type, "Guild") == 0) { - if (on) { + } else if (strcmpi(skill_info, "Guild") == 0) { + if (on) sk->inf2 |= INF2_GUILD_SKILL; - } else { + else sk->inf2 &= ~INF2_GUILD_SKILL; - } - } else if (strcmpi(type, "Song") == 0) { - if (on) { + } else if (strcmpi(skill_info, "Song") == 0) { + if (on) sk->inf2 |= INF2_SONG_DANCE; - } else { + else sk->inf2 &= ~INF2_SONG_DANCE; - } - } else if (strcmpi(type, "Ensemble") == 0) { - if (on) { + } else if (strcmpi(skill_info, "Ensemble") == 0) { + if (on) sk->inf2 |= INF2_ENSEMBLE_SKILL; - } else { + else sk->inf2 &= ~INF2_ENSEMBLE_SKILL; - } - } else if (strcmpi(type, "Trap") == 0) { - if (on) { + } else if (strcmpi(skill_info, "Trap") == 0) { + if (on) sk->inf2 |= INF2_TRAP; - } else { + else sk->inf2 &= ~INF2_TRAP; - } - } else if (strcmpi(type, "TargetSelf") == 0) { - if (on) { + } else if (strcmpi(skill_info, "TargetSelf") == 0) { + if (on) sk->inf2 |= INF2_TARGET_SELF; - } else { + else sk->inf2 &= ~INF2_TARGET_SELF; - } - } else if (strcmpi(type, "NoCastSelf") == 0) { - if (on) { + } else if (strcmpi(skill_info, "NoCastSelf") == 0) { + if (on) sk->inf2 |= INF2_NO_TARGET_SELF; - } else { + else sk->inf2 &= ~INF2_NO_TARGET_SELF; - } - } else if (strcmpi(type, "PartyOnly") == 0) { - if (on) { + } else if (strcmpi(skill_info, "PartyOnly") == 0) { + if (on) sk->inf2 |= INF2_PARTY_ONLY; - } else { + else sk->inf2 &= ~INF2_PARTY_ONLY; - } - } else if (strcmpi(type, "GuildOnly") == 0) { - if (on) { + } else if (strcmpi(skill_info, "GuildOnly") == 0) { + if (on) sk->inf2 |= INF2_GUILD_ONLY; - } else { + else sk->inf2 &= ~INF2_GUILD_ONLY; - } - } else if (strcmpi(type, "NoEnemy") == 0) { - if (on) { + } else if (strcmpi(skill_info, "NoEnemy") == 0) { + if (on) sk->inf2 |= INF2_NO_ENEMY; - } else { + else sk->inf2 &= ~INF2_NO_ENEMY; - } - } else if (strcmpi(type, "IgnoreLandProtector") == 0) { - if (on) { + } else if (strcmpi(skill_info, "IgnoreLandProtector") == 0) { + if (on) sk->inf2 |= INF2_NOLP; - } else { + else sk->inf2 &= ~INF2_NOLP; - } - } else if (strcmpi(type, "Chorus") == 0) { - if (on) { + } else if (strcmpi(skill_info, "Chorus") == 0) { + if (on) sk->inf2 |= INF2_CHORUS_SKILL; - } else { + else sk->inf2 &= ~INF2_CHORUS_SKILL; - } - } else if (strcmpi(type, "FreeCastNormal") == 0) { - if (on) { + } else if (strcmpi(skill_info, "FreeCastNormal") == 0) { + if (on) sk->inf2 |= INF2_FREE_CAST_NORMAL; - } else { + else sk->inf2 &= ~INF2_FREE_CAST_NORMAL; - } - } else if (strcmpi(type, "FreeCastReduced") == 0) { - if (on) { + } else if (strcmpi(skill_info, "FreeCastReduced") == 0) { + if (on) sk->inf2 |= INF2_FREE_CAST_REDUCED; - } else { + else sk->inf2 &= ~INF2_FREE_CAST_REDUCED; - } - } else if (strcmpi(type, "ShowSkillScale") == 0) { - if (on) { + } else if (strcmpi(skill_info, "ShowSkillScale") == 0) { + if (on) sk->inf2 |= INF2_SHOW_SKILL_SCALE; - } else { + else sk->inf2 &= ~INF2_SHOW_SKILL_SCALE; - } - } else if (strcmpi(type, "AllowReproduce") == 0) { - if (on) { + } else if (strcmpi(skill_info, "AllowReproduce") == 0) { + if (on) sk->inf2 |= INF2_ALLOW_REPRODUCE; - } else { + else sk->inf2 &= ~INF2_ALLOW_REPRODUCE; - } - } else if (strcmpi(type, "HiddenTrap") == 0) { - if (on) { + } else if (strcmpi(skill_info, "HiddenTrap") == 0) { + if (on) sk->inf2 |= INF2_HIDDEN_TRAP; - } else { + else sk->inf2 &= ~INF2_HIDDEN_TRAP; - } - } else if (strcmpi(type, "IsCombo") == 0) { - if (on) { + } else if (strcmpi(skill_info, "IsCombo") == 0) { + if (on) sk->inf2 |= INF2_IS_COMBO_SKILL; - } else { + else sk->inf2 &= ~INF2_IS_COMBO_SKILL; - } - } else if (strcmpi(type, "None") != 0) { + } else if (strcmpi(skill_info, "None") != 0) { ShowWarning("%s: Invalid sub-type %s specified for skill ID %d in %s! Skipping sub-type...\n", - __func__, type, sk->nameid, conf->file); + __func__, skill_info, sk->nameid, conf->file); } } } } /** - * Validates "AttackType" when reading skill_db.conf - * @param conf struct, pointer to skill configuration - * @param sk struct, pointer to s_skill_db - * @return (void) - */ + * Validates a skill's attack type when reading the skill DB. + * + * @param conf The libconfig settings block which contains the skill's data. + * @param sk The s_skill_db struct where the attack type should be set it. + * + **/ static void skill_validate_attacktype(struct config_setting_t *conf, struct s_skill_db *sk) { nullpo_retv(conf); @@ -20599,28 +20584,28 @@ static void skill_validate_attacktype(struct config_setting_t *conf, struct s_sk sk->skill_type = BF_NONE; - const char *type = NULL; + const char *attack_type; - if (libconfig->setting_lookup_string(conf, "AttackType", &type)) { - if (!strcmpi(type, "Weapon")) { + if (libconfig->setting_lookup_string(conf, "AttackType", &attack_type) == CONFIG_TRUE) { + if (strcmpi(attack_type, "Weapon") == 0) sk->skill_type = BF_WEAPON; - } else if (!strcmpi(type, "Magic")) { + else if (strcmpi(attack_type, "Magic") == 0) sk->skill_type = BF_MAGIC; - } else if (!strcmpi(type, "Misc")) { + else if (strcmpi(attack_type, "Misc") == 0) sk->skill_type = BF_MISC; - } else if (strcmpi(type, "None") != 0) { + else if (strcmpi(attack_type, "None") != 0) ShowWarning("%s: Invalid attack type %s specified for skill ID %d in %s! Defaulting to None...\n", - __func__, type, sk->nameid, conf->file); - } + __func__, attack_type, sk->nameid, conf->file); } } /** - * 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) - */ + * Validates a skill's element when reading the skill DB. + * + * @param conf The libconfig settings block which contains the skill's data. + * @param sk The s_skill_db struct where the element should be set it. + * + **/ static void skill_validate_element(struct config_setting_t *conf, struct s_skill_db *sk) { nullpo_retv(conf); @@ -20628,41 +20613,44 @@ static void skill_validate_element(struct config_setting_t *conf, struct s_skill skill->level_set_value(sk->element, ELE_NEUTRAL); - 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[6]; // enough to contain "Lv100" in case of custom MAX_SKILL_LEVEL - - for (j=0; j < MAX_SKILL_LEVEL; j++) { - sprintf(lv, "Lv%d",j+1); + struct config_setting_t *t = libconfig->setting_get_member(conf, "Element"); - 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])) + if (t != NULL && config_setting_is_group(t)) { + for (int i = 0; i < MAX_SKILL_LEVEL; i++) { + char lv[6]; // Big enough to contain "Lv999" in case of custom MAX_SKILL_LEVEL. + safesnprintf(lv, sizeof(lv), "Lv%d", i + 1); + const char *element; + + if (libconfig->setting_lookup_string(t, lv, &element) == CONFIG_TRUE) { + if (strcmpi(element, "Ele_Weapon") == 0) + sk->element[i] = -1; + else if (strcmpi(element, "Ele_Endowed") == 0) + sk->element[i] = -2; + else if (strcmpi(element, "Ele_Random") == 0) + sk->element[i] = -3; + else if (!script->get_constant(element, &sk->element[i])) ShowWarning("%s: Invalid element %s specified in level %d for skill ID %d in %s! Defaulting to Ele_Neutral...\n", - __func__, type, j + 1, sk->nameid, conf->file); + __func__, element, i + 1, sk->nameid, conf->file); } } - } else if (libconfig->setting_lookup_string(conf, "Element", &type)) { + return; + } + + const char *element; + + if (libconfig->setting_lookup_string(conf, "Element", &element) == CONFIG_TRUE) { int ele = ELE_NEUTRAL; - if (strcmpi(type,"Ele_Weapon") == 0) + if (strcmpi(element, "Ele_Weapon") == 0) { ele = -1; - else if (strcmpi(type,"Ele_Endowed") == 0) + } else if (strcmpi(element, "Ele_Endowed") == 0) { ele = -2; - else if (strcmpi(type,"Ele_Random") == 0) + } else if (strcmpi(element, "Ele_Random") == 0) { ele = -3; - else if (!script->get_constant(type, &ele)) { + } else if (!script->get_constant(element, &ele)) { ShowWarning("%s: Invalid element %s specified for skill ID %d in %s! Defaulting to Ele_Neutral...\n", - __func__, type, sk->nameid, conf->file); + __func__, element, sk->nameid, conf->file); return; } @@ -20671,11 +20659,12 @@ static void skill_validate_element(struct config_setting_t *conf, struct s_skill } /** - * Validates "DamageType" when reading skill_db.conf - * @param conf struct, pointer to skill configuration - * @param sk struct, pointer to s_skill_db - * @return (void) - */ + * Validates a skill's damage types when reading the skill DB. + * + * @param conf The libconfig settings block which contains the skill's data. + * @param sk The s_skill_db struct where the damage types should be set it. + * + **/ static void skill_validate_damagetype(struct config_setting_t *conf, struct s_skill_db *sk) { nullpo_retv(conf); @@ -20683,65 +20672,59 @@ static void skill_validate_damagetype(struct config_setting_t *conf, struct s_sk sk->nk = NK_NONE; - struct config_setting_t *t = NULL, *tt = NULL; + struct config_setting_t *t = libconfig->setting_get_member(conf, "DamageType"); - 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); + if (t != NULL && config_setting_is_group(t)) { + struct config_setting_t *tt; + int i = 0; + + while ((tt = libconfig->setting_get_elem(t, i++)) != NULL) { + const char *damage_type = config_setting_name(tt); bool on = libconfig->setting_get_bool_real(tt); - if (strcmpi(type, "NoDamage") == 0) { - if (on) { + if (strcmpi(damage_type, "NoDamage") == 0) { + if (on) sk->nk |= NK_NO_DAMAGE; - } else { + else sk->nk &= ~NK_NO_DAMAGE; - } - } else if (strcmpi(type, "SplashArea") == 0) { - if (on) { + } else if (strcmpi(damage_type, "SplashArea") == 0) { + if (on) sk->nk |= NK_SPLASH_ONLY; - } else { + else sk->nk &= ~NK_SPLASH_ONLY; - } - } else if (strcmpi(type, "SplitDamage") == 0) { - if (on) { + } else if (strcmpi(damage_type, "SplitDamage") == 0) { + if (on) sk->nk |= NK_SPLASHSPLIT; - } else { + else sk->nk &= ~NK_SPLASHSPLIT; - } - } else if (strcmpi(type, "IgnoreCards") == 0) { - if (on) { + } else if (strcmpi(damage_type, "IgnoreCards") == 0) { + if (on) sk->nk |= NK_NO_CARDFIX_ATK; - } else { + else sk->nk &= ~NK_NO_CARDFIX_ATK; - } - } else if (strcmpi(type, "IgnoreElement") == 0) { - if (on) { + } else if (strcmpi(damage_type, "IgnoreElement") == 0) { + if (on) sk->nk |= NK_NO_ELEFIX; - } else { + else sk->nk &= ~NK_NO_ELEFIX; - } - } else if (strcmpi(type, "IgnoreDefense") == 0) { - if (on) { + } else if (strcmpi(damage_type, "IgnoreDefense") == 0) { + if (on) sk->nk |= NK_IGNORE_DEF; - } else { + else sk->nk &= ~NK_IGNORE_DEF; - } - } else if (strcmpi(type, "IgnoreFlee") == 0) { - if (on) { + } else if (strcmpi(damage_type, "IgnoreFlee") == 0) { + if (on) sk->nk |= NK_IGNORE_FLEE; - } else { + else sk->nk &= ~NK_IGNORE_FLEE; - } - } else if (strcmpi(type, "IgnoreDefCards") == 0) { - if (on) { + } else if (strcmpi(damage_type, "IgnoreDefCards") == 0) { + if (on) sk->nk |= NK_NO_CARDFIX_DEF; - } else { + else sk->nk &= ~NK_NO_CARDFIX_DEF; - } } else { ShowWarning("%s: Invalid damage type %s specified for skill ID %d in %s! Skipping damage type...\n", - __func__, type, sk->nameid, conf->file); + __func__, damage_type, sk->nameid, conf->file); } } } @@ -21299,12 +21282,13 @@ static void skill_validate_fixed_cast_time(struct config_setting_t *conf, struct } /** - * 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) - */ + * Validates a skill's cast time or delay options when reading the skill DB. + * + * @param conf The libconfig settings block which contains the skill's data. + * @param sk The s_skill_db struct where the cast time or delay options should be set it. + * @param delay If true, the skill's delay options are validated, otherwise its cast time options. + * + **/ static void skill_validate_castnodex(struct config_setting_t *conf, struct s_skill_db *sk, bool delay) { nullpo_retv(conf); @@ -21312,40 +21296,40 @@ static void skill_validate_castnodex(struct config_setting_t *conf, struct s_ski skill->level_set_value(delay ? sk->delaynodex : sk->castnodex, 0); - struct config_setting_t *t = NULL, *tt = NULL; + struct config_setting_t *t = libconfig->setting_get_member(conf, delay ? "SkillDelayOptions" : "CastTimeOptions"); - 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); + if (t != NULL && config_setting_is_group(t)) { + struct config_setting_t *tt; + int i = 0; + int options = 0; + + while ((tt = libconfig->setting_get_elem(t, i++)) != NULL) { + const char *value = 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); - } + if (strcmpi(value, "IgnoreDex") == 0) { + if (on) + options |= 1; + else + options &= ~1; + } else if (strcmpi(value, "IgnoreStatusEffect") == 0) { + if (on) + options |= 2; + else + options &= ~2; + } else if (strcmpi(value, "IgnoreItemBonus") == 0) { + if (on) + options |= 4; + else + options &= ~4; } else { const char *option_string = delay ? "skill delay" : "cast time"; ShowWarning("%s: Invalid %s option %s specified for skill ID %d in %s! Skipping option...\n", - __func__, option_string, type, sk->nameid, conf->file); + __func__, option_string, value, sk->nameid, conf->file); } - } - skill->level_set_value(delay?sk->delaynodex:sk->castnodex, tmpopt); + + skill->level_set_value(delay ? sk->delaynodex : sk->castnodex, options); } } @@ -21620,217 +21604,186 @@ static void skill_validate_zeny_cost(struct config_setting_t *conf, struct s_ski } /** - * 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 - */ + * Validates a single weapon type when reading the skill DB. + * + * @param type The weapon type to validate. + * @param on Whether the weapon type is required for the skill. + * @param sk The s_skill_db struct where the weapon type should be set it. + * @return 0 if the passed weapon type is valid, otherwise 1. + * + **/ static int skill_validate_weapontype_sub(const char *type, bool on, struct s_skill_db *sk) { nullpo_retr(1, type); nullpo_retr(1, sk); if (strcmpi(type, "NoWeapon") == 0) { - if (on) { - sk->weapon |= 1<weapon &= ~(1<weapon |= (1 << W_FIST); + else + sk->weapon &= ~(1 << W_FIST); } else if (strcmpi(type, "Daggers") == 0) { - if (on) { - sk->weapon |= 1<weapon &= ~(1<weapon |= (1 << W_DAGGER); + else + sk->weapon &= ~(1 << W_DAGGER); } else if (strcmpi(type, "1HSwords") == 0) { - if (on) { - sk->weapon |= 1<weapon &= ~(1<weapon |= (1 << W_1HSWORD); + else + sk->weapon &= ~(1 << W_1HSWORD); } else if (strcmpi(type, "2HSwords") == 0) { - if (on) { - sk->weapon |= 1<weapon &= ~(1<weapon |= (1 << W_2HSWORD); + else + sk->weapon &= ~(1 << W_2HSWORD); } else if (strcmpi(type, "1HSpears") == 0) { - if (on) { - sk->weapon |= 1<weapon &= ~(1<weapon |= (1 << W_1HSPEAR); + else + sk->weapon &= ~(1 << W_1HSPEAR); } else if (strcmpi(type, "2HSpears") == 0) { - if (on) { - sk->weapon |= 1<weapon &= ~(1<weapon |= (1 << W_2HSPEAR); + else + sk->weapon &= ~(1 << W_2HSPEAR); } else if (strcmpi(type, "1HAxes") == 0) { - if (on) { - sk->weapon |= 1<weapon &= ~(1<weapon |= (1 << W_1HAXE); + else + sk->weapon &= ~(1 << W_1HAXE); } else if (strcmpi(type, "2HAxes") == 0) { - if (on) { - sk->weapon |= 1<weapon &= ~(1<weapon |= (1 << W_2HAXE); + else + sk->weapon &= ~(1 << W_2HAXE); } else if (strcmpi(type, "Maces") == 0) { - if (on) { - sk->weapon |= 1<weapon &= ~(1<weapon |= (1 << W_MACE); + else + sk->weapon &= ~(1 << W_MACE); } else if (strcmpi(type, "2HMaces") == 0) { - if (on) { - sk->weapon |= 1<weapon &= ~(1<weapon |= (1 << W_2HMACE); + else + sk->weapon &= ~(1 << W_2HMACE); } else if (strcmpi(type, "Staves") == 0) { - if (on) { - sk->weapon |= 1<weapon &= ~(1<weapon |= (1 << W_STAFF); + else + sk->weapon &= ~(1 << W_STAFF); } else if (strcmpi(type, "Bows") == 0) { - if (on) { - sk->weapon |= 1<weapon &= ~(1<weapon |= (1 << W_BOW); + else + sk->weapon &= ~(1 << W_BOW); } else if (strcmpi(type, "Knuckles") == 0) { - if (on) { - sk->weapon |= 1<weapon &= ~(1<weapon |= (1 << W_KNUCKLE); + else + sk->weapon &= ~(1 << W_KNUCKLE); } else if (strcmpi(type, "Instruments") == 0) { - if (on) { - sk->weapon |= 1<weapon &= ~(1<weapon |= (1 << W_MUSICAL); + else + sk->weapon &= ~(1 << W_MUSICAL); } else if (strcmpi(type, "Whips") == 0) { - if (on) { - sk->weapon |= 1<weapon &= ~(1<weapon |= (1 << W_WHIP); + else + sk->weapon &= ~(1 << W_WHIP); } else if (strcmpi(type, "Books") == 0) { - if (on) { - sk->weapon |= 1<weapon &= ~(1<weapon |= (1 << W_BOOK); + else + sk->weapon &= ~(1 << W_BOOK); } else if (strcmpi(type, "Katars") == 0) { - if (on) { - sk->weapon |= 1<weapon &= ~(1<weapon |= (1 << W_KATAR); + else + sk->weapon &= ~(1 << W_KATAR); } else if (strcmpi(type, "Revolvers") == 0) { - if (on) { - sk->weapon |= 1<weapon &= ~(1<weapon |= (1 << W_REVOLVER); + else + sk->weapon &= ~(1 << W_REVOLVER); } else if (strcmpi(type, "Rifles") == 0) { - if (on) { - sk->weapon |= 1<weapon &= ~(1<weapon |= (1 << W_RIFLE); + else + sk->weapon &= ~(1 << W_RIFLE); } else if (strcmpi(type, "GatlingGuns") == 0) { - if (on) { - sk->weapon |= 1<weapon &= ~(1<weapon |= (1 << W_GATLING); + else + sk->weapon &= ~(1 << W_GATLING); } else if (strcmpi(type, "Shotguns") == 0) { - if (on) { - sk->weapon |= 1<weapon &= ~(1<weapon |= (1 << W_SHOTGUN); + else + sk->weapon &= ~(1 << W_SHOTGUN); } else if (strcmpi(type, "GrenadeLaunchers") == 0) { - if (on) { - sk->weapon |= 1<weapon &= ~(1<weapon |= (1 << W_GRENADE); + else + sk->weapon &= ~(1 << W_GRENADE); } else if (strcmpi(type, "FuumaShurikens") == 0) { - if (on) { - sk->weapon |= 1<weapon &= ~(1<weapon |= (1 << W_HUUMA); + else + sk->weapon &= ~(1 << W_HUUMA); } else if (strcmpi(type, "2HStaves") == 0) { - if (on) { - sk->weapon |= 1<weapon &= ~(1<weapon |= 1<weapon &= ~(1<weapon |= (1 << W_2HSTAFF); + else + sk->weapon &= ~(1 << W_2HSTAFF); + } 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<weapon &= ~(1<weapon |= (1 << W_DOUBLE_SS); + else + sk->weapon &= ~(1 << W_DOUBLE_SS); } else if (strcmpi(type, "DWAxes") == 0) { - if (on) { - sk->weapon |= 1<weapon &= ~(1<weapon |= (1 << W_DOUBLE_AA); + else + sk->weapon &= ~(1 << W_DOUBLE_AA); } else if (strcmpi(type, "DWDaggerSword") == 0) { - if (on) { - sk->weapon |= 1<weapon &= ~(1<weapon |= (1 << W_DOUBLE_DS); + else + sk->weapon &= ~(1 << W_DOUBLE_DS); } else if (strcmpi(type, "DWDaggerAxe") == 0) { - if (on) { - sk->weapon |= 1<weapon &= ~(1<weapon |= (1 << W_DOUBLE_DA); + else + sk->weapon &= ~(1 << W_DOUBLE_DA); } else if (strcmpi(type, "DWSwordAxe") == 0) { - if (on) { - sk->weapon |= 1<weapon &= ~(1<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 1; } 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) - */ + * Validates a skill's required weapon types when reading the skill DB. + * + * @param conf The libconfig settings block which contains the skill's data. + * @param sk The s_skill_db struct where the required weapon types should be set it. + * + **/ static void skill_validate_weapontype(struct config_setting_t *conf, struct s_skill_db *sk) { nullpo_retv(conf); @@ -21838,111 +21791,110 @@ static void skill_validate_weapontype(struct config_setting_t *conf, struct s_sk sk->weapon = 0; - struct config_setting_t *tt = NULL; - const char *type = NULL; + struct config_setting_t *t = libconfig->setting_get_member(conf, "WeaponTypes"); + + if (t != NULL && config_setting_is_group(t)) { + struct config_setting_t *tt; + int i = 0; - if ((tt = libconfig->setting_get_member(conf, "WeaponTypes")) && config_setting_is_group(tt)) { - int j = 0; - struct config_setting_t *wpt = NULL; - while ((wpt = libconfig->setting_get_elem(tt, j++)) != NULL) { - if (skill->validate_weapontype_sub(config_setting_name(wpt), libconfig->setting_get_bool_real(wpt), sk)) + while ((tt = libconfig->setting_get_elem(t, i++)) != NULL) { + bool on = libconfig->setting_get_bool_real(tt); + + if (skill->validate_weapontype_sub(config_setting_name(tt), on, sk) != 0) ShowWarning("%s: Invalid required weapon type %s specified for skill ID %d in %s! Skipping type...\n", - __func__, config_setting_name(wpt), sk->nameid, conf->file); + __func__, config_setting_name(tt), sk->nameid, conf->file); } - } else if (libconfig->setting_lookup_string(conf, "WeaponTypes", &type)) { - if (skill->validate_weapontype_sub(type, true, sk)) + + return; + } + + const char *weapon_type; + + if (libconfig->setting_lookup_string(conf, "WeaponTypes", &weapon_type) == CONFIG_TRUE) { + if (skill->validate_weapontype_sub(weapon_type, true, sk) != 0) ShowWarning("%s: Invalid required weapon type %s specified for skill ID %d in %s! Defaulting to All...\n", - __func__, type, sk->nameid, conf->file); + __func__, weapon_type, sk->nameid, conf->file); } } /** - * 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 - */ + * Validates a single ammunition type when reading the skill DB. + * + * @param type The ammunition type to validate. + * @param on Whether the ammunition type is required for the skill. + * @param sk The s_skill_db struct where the ammunition type should be set it. + * @return 0 if the passed ammunition type is valid, otherwise 1. + * + **/ static int skill_validate_ammotype_sub(const char *type, bool on, struct s_skill_db *sk) { nullpo_retr(1, type); nullpo_retr(1, sk); if (strcmpi(type, "A_ARROW") == 0) { - if (on) { - sk->ammo |= 1<ammo &= ~(1<ammo |= (1 << A_ARROW); + else + sk->ammo &= ~(1 << A_ARROW); } else if (strcmpi(type, "A_DAGGER") == 0) { - if (on) { - sk->ammo |= 1<ammo &= ~(1<ammo |= (1 << A_DAGGER); + else + sk->ammo &= ~(1 << A_DAGGER); } else if (strcmpi(type, "A_BULLET") == 0) { - if (on) { - sk->ammo |= 1<ammo &= ~(1<ammo |= (1 << A_BULLET); + else + sk->ammo &= ~(1 << A_BULLET); } else if (strcmpi(type, "A_SHELL") == 0) { - if (on) { - sk->ammo |= 1<ammo &= ~(1<ammo |= (1 << A_SHELL); + else + sk->ammo &= ~(1 << A_SHELL); } else if (strcmpi(type, "A_GRENADE") == 0) { - if (on) { - sk->ammo |= 1<ammo &= ~(1<ammo |= (1 << A_GRENADE); + else + sk->ammo &= ~(1 << A_GRENADE); } else if (strcmpi(type, "A_SHURIKEN") == 0) { - if (on) { - sk->ammo |= 1<ammo &= ~(1<ammo |= (1 << A_SHURIKEN); + else + sk->ammo &= ~(1 << A_SHURIKEN); } else if (strcmpi(type, "A_KUNAI") == 0) { - if (on) { - sk->ammo |= 1<ammo &= ~(1<ammo |= (1 << A_KUNAI); + else + sk->ammo &= ~(1 << A_KUNAI); } else if (strcmpi(type, "A_CANNONBALL") == 0) { - if (on) { - sk->ammo |= 1<ammo &= ~(1<ammo |= (1 << A_CANNONBALL); + else + sk->ammo &= ~(1 << A_CANNONBALL); } else if (strcmpi(type, "A_THROWWEAPON") == 0) { - if (on) { - sk->ammo |= 1<ammo &= ~(1<ammo |= (1 << A_THROWWEAPON); + else + sk->ammo &= ~(1 << A_THROWWEAPON); } else if (strcmpi(type, "All") == 0) { - if (on) { + if (on) sk->ammo = 0xFFFFFFFF; - } else { + else sk->ammo = 0; - } } else { - return 1; // Invalid Entry + return 1; } 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 - */ + * Validates a skill's required ammunition types when reading the skill DB. + * + * @param conf The libconfig settings block which contains the skill's data. + * @param sk The s_skill_db struct where the required ammunition types should be set it. + * + **/ static void skill_validate_ammotype(struct config_setting_t *conf, struct s_skill_db *sk) { nullpo_retv(conf); @@ -21950,21 +21902,27 @@ static void skill_validate_ammotype(struct config_setting_t *conf, struct s_skil sk->ammo = 0; - struct config_setting_t *tt = NULL; - const char *tstr = NULL; + struct config_setting_t *t = libconfig->setting_get_member(conf, "AmmoTypes"); - 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)) + if (t != NULL && config_setting_is_group(t)) { + struct config_setting_t *tt; + int i = 0; + + while ((tt = libconfig->setting_get_elem(t, i++)) != NULL) { + bool on = libconfig->setting_get_bool_real(tt); + + if (skill->validate_ammotype_sub(config_setting_name(tt), on, sk) != 0) ShowWarning("%s: Invalid required ammunition type %s specified for skill ID %d in %s! Skipping type...\n", - __func__, config_setting_name(amt), sk->nameid, conf->file); + __func__, config_setting_name(tt), sk->nameid, conf->file); } - } else if( libconfig->setting_lookup_string(conf, "AmmoTypes", &tstr)) { - if (skill->validate_ammotype_sub(tstr, true, sk)) + } + + const char *ammo_type; + + if (libconfig->setting_lookup_string(conf, "AmmoTypes", &ammo_type) == CONFIG_TRUE) { + if (skill->validate_ammotype_sub(ammo_type, true, sk) != 0) ShowWarning("%s: Invalid required ammunition type %s specified for skill ID %d in %s! Defaulting to None...\n", - __func__, tstr, sk->nameid, conf->file); + __func__, ammo_type, sk->nameid, conf->file); } } @@ -22014,12 +21972,12 @@ static void skill_validate_ammo_amount(struct config_setting_t *conf, struct s_s } /** - * 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 - */ + * Validates a skill's required states when reading the skill DB. + * + * @param conf The libconfig settings block which contains the skill's data. + * @param sk The s_skill_db struct where the required states should be set it. + * + **/ static void skill_validate_state(struct config_setting_t *conf, struct s_skill_db *sk) { nullpo_retv(conf); @@ -22027,35 +21985,58 @@ static void skill_validate_state(struct config_setting_t *conf, struct s_skill_d sk->state = ST_NONE; - 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_GRAPPLING; - else if (strcmpi(type,"Peco") == 0 ) sk->state = ST_PECO; - else + const char *state; + + if (libconfig->setting_lookup_string(conf, "State", &state) == CONFIG_TRUE) { + if (strcmpi(state, "Hiding") == 0) + sk->state = ST_HIDING; + else if (strcmpi(state, "Cloaking") == 0) + sk->state = ST_CLOAKING; + else if (strcmpi(state, "Hidden") == 0) + sk->state = ST_HIDDEN; + else if (strcmpi(state, "Riding") == 0) + sk->state = ST_RIDING; + else if (strcmpi(state, "Falcon") == 0) + sk->state = ST_FALCON; + else if (strcmpi(state, "Cart") == 0) + sk->state = ST_CART; + else if (strcmpi(state, "Shield") == 0) + sk->state = ST_SHIELD; + else if (strcmpi(state, "Sight") == 0) + sk->state = ST_SIGHT; + else if (strcmpi(state, "ExplosionSpirits") == 0) + sk->state = ST_EXPLOSIONSPIRITS; + else if (strcmpi(state, "CartBoost") == 0) + sk->state = ST_CARTBOOST; + else if (strcmpi(state, "NotOverWeight") == 0) + sk->state = ST_RECOV_WEIGHT_RATE; + else if (strcmpi(state, "Moveable") == 0) + sk->state = ST_MOVE_ENABLE; + else if (strcmpi(state, "InWater") == 0) + sk->state = ST_WATER; + else if (strcmpi(state, "Dragon") == 0) + sk->state = ST_RIDINGDRAGON; + else if (strcmpi(state, "Warg") == 0) + sk->state = ST_WUG; + else if (strcmpi(state, "RidingWarg") == 0) + sk->state = ST_RIDINGWUG; + else if (strcmpi(state, "MadoGear") == 0) + sk->state = ST_MADO; + else if (strcmpi(state, "ElementalSpirit") == 0) + sk->state = ST_ELEMENTALSPIRIT; + else if (strcmpi(state, "PoisonWeapon") == 0) + sk->state = ST_POISONINGWEAPON; + else if (strcmpi(state, "RollingCutter") == 0) + sk->state = ST_ROLLINGCUTTER; + else if (strcmpi(state, "MH_Fighting") == 0) + sk->state = ST_MH_FIGHTING; + else if (strcmpi(state, "MH_Grappling") == 0) + sk->state = ST_MH_GRAPPLING; + else if (strcmpi(state, "Peco") == 0) + sk->state = ST_PECO; + else if (strcmpi(state, "None") != 0) ShowWarning("%s: Invalid required state %s specified for skill ID %d in %s! Defaulting to None...\n", - __func__, type, sk->nameid, conf->file); + __func__, state, sk->nameid, conf->file); } } @@ -22105,12 +22086,12 @@ static void skill_validate_spirit_sphere_cost(struct config_setting_t *conf, str } /** - * 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 - */ + * Validates a skill's required items when reading the skill DB. + * + * @param conf The libconfig settings block which contains the skill's data. + * @param sk The s_skill_db struct where the required items should be set it. + * + **/ static void skill_validate_item_requirements(struct config_setting_t *conf, struct s_skill_db *sk) { nullpo_retv(conf); @@ -22119,14 +22100,14 @@ static void skill_validate_item_requirements(struct config_setting_t *conf, stru skill->level_set_value(sk->itemid, 0); skill->level_set_value(sk->amount, 0); - struct config_setting_t *tt = NULL; + struct config_setting_t *t = libconfig->setting_get_member(conf, "Items"); - if ((tt=libconfig->setting_get_member(conf, "Items")) && config_setting_is_group(conf)) { - int itx=-1; - struct config_setting_t *it; + if (t != NULL && config_setting_is_group(conf)) { + struct config_setting_t *tt; + int i = -1; - while((it=libconfig->setting_get_elem(tt, ++itx)) && itx < MAX_SKILL_ITEM_REQUIRE) { - const char *type = config_setting_name(it); + while ((tt = libconfig->setting_get_elem(t, ++i)) != NULL && i < MAX_SKILL_ITEM_REQUIRE) { + const char *type = config_setting_name(tt); if (strlen(type) < 2) { ShowWarning("%s: Invalid required item %s specified for skill ID %d in %s! Skipping item...\n", @@ -22152,13 +22133,10 @@ static void skill_validate_item_requirements(struct config_setting_t *conf, stru int amount = 0; - if (config_setting_is_group(it)) { - // TODO: Per-level item requirements are not implemented yet! - // We just take the first level for the time being (old txt behavior) - amount = libconfig->setting_get_int_elem(it, 0); - } else { - amount = libconfig->setting_get_int(it); - } + if (config_setting_is_group(tt)) + amount = libconfig->setting_get_int_elem(tt, 0); + else + amount = libconfig->setting_get_int(tt); if (amount < 0 || amount > MAX_AMOUNT) { ShowWarning("%s: Invalid required item amount %d specified for skill ID %d in %s! Minimum is 0, maximum is %d. Skipping item...\n", @@ -22166,8 +22144,8 @@ static void skill_validate_item_requirements(struct config_setting_t *conf, stru continue; } - sk->itemid[itx] = item_id; - sk->amount[itx] = amount; + sk->itemid[i] = item_id; + sk->amount[i] = amount; } } } @@ -22398,109 +22376,98 @@ static void skill_validate_unit_interval(struct config_setting_t *conf, struct s } /** - * 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) - */ + * Validates a single unit flag when reading the skill DB. + * + * @param type The unit flag to validate. + * @param on Whether the unit flag is set for the skill. + * @param sk The s_skill_db struct where the unit flag should be set it. + * @return 0 if the passed unit flag is valid, otherwise 1. + * + **/ static int skill_validate_unit_flag_sub(const char *type, bool on, struct s_skill_db *sk) { nullpo_retr(1, type); nullpo_retr(1, sk); + if (strcmpi(type, "UF_DEFNOTENEMY") == 0) { - if (on) { + if (on) sk->unit_flag |= UF_DEFNOTENEMY; - } else { + else sk->unit_flag &= ~UF_DEFNOTENEMY; - } } else if (strcmpi(type, "UF_NOREITERATION") == 0) { - if (on) { + if (on) sk->unit_flag |= UF_NOREITERATION; - } else { + else sk->unit_flag &= ~UF_NOREITERATION; - } } else if (strcmpi(type, "UF_NOFOOTSET") == 0) { - if (on) { + if (on) sk->unit_flag |= UF_NOFOOTSET; - } else { + else sk->unit_flag &= ~UF_NOFOOTSET; - } } else if (strcmpi(type, "UF_NOOVERLAP") == 0) { - if (on) { + if (on) sk->unit_flag |= UF_NOOVERLAP; - } else { + else sk->unit_flag &= ~UF_NOOVERLAP; - } } else if (strcmpi(type, "UF_PATHCHECK") == 0) { - if (on) { + if (on) sk->unit_flag |= UF_PATHCHECK; - } else { + else sk->unit_flag &= ~UF_PATHCHECK; - } } else if (strcmpi(type, "UF_NOPC") == 0) { - if (on) { + if (on) sk->unit_flag |= UF_NOPC; - } else { + else sk->unit_flag &= ~UF_NOPC; - } } else if (strcmpi(type, "UF_NOMOB") == 0) { - if (on) { + if (on) sk->unit_flag |= UF_NOMOB; - } else { + else sk->unit_flag &= ~UF_NOMOB; - } } else if (strcmpi(type, "UF_SKILL") == 0) { - if (on) { + if (on) sk->unit_flag |= UF_SKILL; - } else { + else sk->unit_flag &= ~UF_SKILL; - } } else if (strcmpi(type, "UF_DANCE") == 0) { - if (on) { + if (on) sk->unit_flag |= UF_DANCE; - } else { + else sk->unit_flag &= ~UF_DANCE; - } } else if (strcmpi(type, "UF_ENSEMBLE") == 0) { - if (on) { + if (on) sk->unit_flag |= UF_ENSEMBLE; - } else { + else sk->unit_flag &= ~UF_ENSEMBLE; - } } else if (strcmpi(type, "UF_SONG") == 0) { - if (on) { + if (on) sk->unit_flag |= UF_SONG; - } else { + else sk->unit_flag &= ~UF_SONG; - } } else if (strcmpi(type, "UF_DUALMODE") == 0) { - if (on) { + if (on) sk->unit_flag |= UF_DUALMODE; - } else { + else sk->unit_flag &= ~UF_DUALMODE; - } } else if (strcmpi(type, "UF_RANGEDSINGLEUNIT") == 0) { - if (on) { + if (on) sk->unit_flag |= UF_RANGEDSINGLEUNIT; - } else { + else sk->unit_flag &= ~UF_RANGEDSINGLEUNIT; - } } else { - return 1; // Invalid Type + return 1; } 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) - */ + * Validates a skill's unit flags when reading the skill DB. + * + * @param conf The libconfig settings block which contains the skill's data. + * @param sk The s_skill_db struct where the unit flags should be set it. + * + **/ static void skill_validate_unit_flag(struct config_setting_t *conf, struct s_skill_db *sk) { nullpo_retv(conf); @@ -22508,28 +22475,29 @@ static void skill_validate_unit_flag(struct config_setting_t *conf, struct s_ski sk->unit_flag = 0; - struct config_setting_t *t = NULL; + struct config_setting_t *t = libconfig->setting_get_member(conf, "Flag"); - 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 (t != NULL && config_setting_is_group(t)) { + struct config_setting_t *tt; + int i = 0; + + while ((tt = libconfig->setting_get_elem(t, i++)) != NULL) { + bool on = libconfig->setting_get_bool_real(tt); - if (skill->validate_unit_flag_sub(name, libconfig->setting_get_bool_real(tt), sk)) + if (skill->validate_unit_flag_sub(config_setting_name(tt), on, sk)) ShowWarning("%s: Invalid unit flag %s specified for skill ID %d in %s! Skipping flag...\n", - __func__, name, sk->nameid, conf->file); + __func__, config_setting_name(tt), sk->nameid, conf->file); } } } /** - * 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 - */ + * Validates a skill's unit target when reading the skill DB. + * + * @param conf The libconfig settings block which contains the skill's data. + * @param sk The s_skill_db struct where the unit target should be set it. + * + **/ static void skill_validate_unit_target(struct config_setting_t *conf, struct s_skill_db *sk) { nullpo_retv(conf); @@ -22537,37 +22505,49 @@ static void skill_validate_unit_target(struct config_setting_t *conf, struct s_s sk->unit_target = BCT_NOONE; - 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_SAMEGUILD; - else if(strcmpi(type, "None") != 0) + const char *unit_target; + + if (libconfig->setting_lookup_string(conf, "Target", &unit_target) == CONFIG_TRUE) { + if (strcmpi(unit_target, "NotEnemy") == 0) + sk->unit_target = BCT_NOENEMY; + else if (strcmpi(unit_target, "NotParty") == 0) + sk->unit_target = BCT_NOPARTY; + else if (strcmpi(unit_target, "NotGuild") == 0) + sk->unit_target = BCT_NOGUILD; + else if (strcmpi(unit_target, "Friend") == 0) + sk->unit_target = BCT_NOENEMY; + else if (strcmpi(unit_target, "Party") == 0) + sk->unit_target = BCT_PARTY; + else if (strcmpi(unit_target, "Ally") == 0) + sk->unit_target = BCT_PARTY|BCT_GUILD; + else if (strcmpi(unit_target, "Guild") == 0) + sk->unit_target = BCT_GUILD; + else if (strcmpi(unit_target, "All") == 0) + sk->unit_target = BCT_ALL; + else if (strcmpi(unit_target, "Enemy") == 0) + sk->unit_target = BCT_ENEMY; + else if (strcmpi(unit_target, "Self") == 0) + sk->unit_target = BCT_SELF; + else if (strcmpi(unit_target, "SameGuild") == 0) + sk->unit_target = BCT_SAMEGUILD; + else if (strcmpi(unit_target, "None") != 0) ShowWarning("%s: Invalid unit target %s specified for skill ID %d in %s! Defaulting to None...\n", - __func__, type, sk->nameid, conf->file); + __func__, unit_target, sk->nameid, conf->file); } - if (sk->unit_flag & UF_DEFNOTENEMY && battle_config.defnotenemy) + if ((sk->unit_flag & UF_DEFNOTENEMY) != 0 && battle_config.defnotenemy != 0) sk->unit_target = BCT_NOENEMY; - //By default, target just characters. + // By default target just characters. sk->unit_target |= BL_CHAR; - if (sk->unit_flag & UF_NOPC) + if ((sk->unit_flag & UF_NOPC) != 0) sk->unit_target &= ~BL_PC; - if (sk->unit_flag & UF_NOMOB) + + if ((sk->unit_flag & UF_NOMOB) != 0) sk->unit_target &= ~BL_MOB; - if (sk->unit_flag & UF_SKILL) + + if ((sk->unit_flag & UF_SKILL) != 0) sk->unit_target |= BL_SKILL; } @@ -22608,35 +22588,44 @@ static void skill_validate_additional_fields(struct config_setting_t *conf, stru } /** - * 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 - */ + * Reads a skill DB file from relative path. + * + * @param filename The skill DB's file name including the DB path. + * @return True on success, otherwise false. + * + **/ static 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; - nullpo_retr(false, filename); + char filepath[256]; + libconfig->format_db_path(filename, filepath, sizeof(filepath)); - if (!libconfig->load_file(&skilldb, filepath)) { - return false; // Libconfig error report. + if (!exists(filepath)) { + ShowError("%s: Can't find file %s! Abort reading skills...\n", __func__, filepath); + return false; } - // 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); + struct config_t skilldb; + + if (libconfig->load_file(&skilldb, filepath) == 0) + return false; // Libconfig error report. + + struct config_setting_t *sk = libconfig->setting_get_member(skilldb.root, "skill_db"); + + if (sk == NULL) { + ShowError("%s: Skill DB could not be loaded! Please check %s.\n", __func__, filepath); libconfig->destroy(&skilldb); return false; } - while ((conf = libconfig->setting_get_elem(sk,index++))) { - struct s_skill_db tmp_db = { 0 }; + struct config_setting_t *conf; + int index = 0; + int count = 0; + + while ((conf = libconfig->setting_get_elem(sk, index++)) != NULL) { + struct s_skill_db tmp_db = {0}; /** Validate mandatory fields. **/ skill->validate_id(conf, &tmp_db, index); @@ -22689,9 +22678,7 @@ static bool skill_read_skilldb(const char *filename) } libconfig->destroy(&skilldb); - ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", count, filepath); - return true; } -- cgit v1.2.3-60-g2f50 From 6f41ba4268b87b9e23273270bd50f5ffaedf4485 Mon Sep 17 00:00:00 2001 From: Kenpachi Developer Date: Sun, 17 May 2020 02:17:18 +0200 Subject: Remove unused variable nocast from struct s_skill_db --- src/map/skill.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/map/skill.h b/src/map/skill.h index 30b70d037..1db2940af 100644 --- a/src/map/skill.h +++ b/src/map/skill.h @@ -1765,7 +1765,6 @@ struct s_skill_db { int weapon,ammo,ammo_qty[MAX_SKILL_LEVEL],state,spiritball[MAX_SKILL_LEVEL]; int itemid[MAX_SKILL_ITEM_REQUIRE],amount[MAX_SKILL_ITEM_REQUIRE]; int castnodex[MAX_SKILL_LEVEL], delaynodex[MAX_SKILL_LEVEL]; - int nocast; int unit_id[2]; int unit_layout_type[MAX_SKILL_LEVEL]; int unit_range[MAX_SKILL_LEVEL]; -- cgit v1.2.3-60-g2f50 From ad3be6ffd674983f9d3123ee7558a689eb16c416 Mon Sep 17 00:00:00 2001 From: Kenpachi Developer Date: Sat, 16 May 2020 22:45:01 +0200 Subject: Make Hit can be grouped by levels --- src/map/skill.c | 70 +++++++++++++++++++------ src/map/skill.h | 10 +++- src/plugins/HPMHooking/HPMHooking.Defs.inc | 4 +- src/plugins/HPMHooking/HPMHooking_map.Hooks.inc | 12 ++--- 4 files changed, 71 insertions(+), 25 deletions(-) diff --git a/src/map/skill.c b/src/map/skill.c index 78efaa348..d24d7ea89 100644 --- a/src/map/skill.c +++ b/src/map/skill.c @@ -176,14 +176,26 @@ static const char *skill_get_desc(int skill_id) // Skill DB -static int skill_get_hit(int skill_id) +/** + * Gets a skill's hit type by its ID and level. (See enum battle_dmg_type.) + * + * @param skill_id The skill's ID. + * @param skill_lv The skill's level. + * @return The skill's hit type corresponding to the passed level. Defaults to BDT_NORMAL (0) in case of error. + * + **/ +static int skill_get_hit(int skill_id, int skill_lv) { - int idx; if (skill_id == 0) - return 0; - idx = skill->get_index(skill_id); - Assert_ret(idx != 0); - return skill->dbs->db[idx].hit; + return BDT_NORMAL; + + Assert_retr(BDT_NORMAL, skill_lv > 0); + + int idx = skill->get_index(skill_id); + + Assert_retr(BDT_NORMAL, idx != 0); + + return skill->dbs->db[idx].hit[skill_get_lvl_idx(skill_lv)]; } static int skill_get_inf(int skill_id) @@ -2993,7 +3005,7 @@ static int skill_attack(int attack_type, struct block_list *src, struct block_li } //Skill hit type - type=(skill_id==0)?BDT_SPLASH:skill->get_hit(skill_id); + type = (skill_id == 0) ? BDT_SPLASH : skill->get_hit(skill_id, skill_lv); if(damage < dmg.div_ //Only skills that knockback even when they miss. [Skotlex] @@ -4288,7 +4300,7 @@ static int skill_castend_damage_id(struct block_list *src, struct block_list *bl sc_type sct = status->skill2sc(skill_id); if(sct != SC_NONE) status_change_end(bl, sct, INVALID_TIMER); - clif->skill_damage(src, bl, tick, status_get_amotion(src), status_get_dmotion(bl), 0, 1, skill_id, skill_lv, skill->get_hit(skill_id)); + clif->skill_damage(src, bl, tick, status_get_amotion(src), status_get_dmotion(bl), 0, 1, skill_id, skill_lv, skill->get_hit(skill_id, skill_lv)); return 1; } @@ -5626,7 +5638,7 @@ static bool skill_castend_damage_id_unknown(struct block_list *src, struct block ShowWarning("skill_castend_damage_id: Unknown skill used:%d\n", *skill_id); clif->skill_damage(src, bl, *tick, status_get_amotion(src), tstatus->dmotion, 0, abs(skill->get_num(*skill_id, *skill_lv)), - *skill_id, *skill_lv, skill->get_hit(*skill_id)); + *skill_id, *skill_lv, skill->get_hit(*skill_id, *skill_lv)); map->freeblock_unlock(); return true; } @@ -20361,18 +20373,46 @@ static void skill_validate_hittype(struct config_setting_t *conf, struct s_skill nullpo_retv(conf); nullpo_retv(sk); - sk->hit = BDT_NORMAL; + skill->level_set_value(sk->hit, BDT_NORMAL); + + struct config_setting_t *t = libconfig->setting_get_member(conf, "Hit"); + + if (t != NULL && config_setting_is_group(t)) { + for (int i = 0; i < MAX_SKILL_LEVEL; i++) { + char lv[6]; // Big enough to contain "Lv999" in case of custom MAX_SKILL_LEVEL. + safesnprintf(lv, sizeof(lv), "Lv%d", i + 1); + const char *hit_type; + + if (libconfig->setting_lookup_string(t, lv, &hit_type) == CONFIG_TRUE) { + if (strcmpi(hit_type, "BDT_SKILL") == 0) + sk->hit[i] = BDT_SKILL; + else if (strcmpi(hit_type, "BDT_MULTIHIT") == 0) + sk->hit[i] = BDT_MULTIHIT; + else if (strcmpi(hit_type, "BDT_NORMAL") != 0) + ShowWarning("%s: Invalid hit type %s specified in level %d for skill ID %d in %s! Defaulting to BDT_NORMAL...\n", + __func__, hit_type, i + 1, sk->nameid, conf->file); + } + } + + return; + } const char *hit_type; if (libconfig->setting_lookup_string(conf, "Hit", &hit_type) == CONFIG_TRUE) { - if (strcmpi(hit_type, "BDT_SKILL") == 0) - sk->hit = BDT_SKILL; - else if (strcmpi(hit_type, "BDT_MULTIHIT") == 0) - sk->hit = BDT_MULTIHIT; - else if (strcmpi(hit_type, "BDT_NORMAL") != 0) + int hit = BDT_NORMAL; + + if (strcmpi(hit_type, "BDT_SKILL") == 0) { + hit = BDT_SKILL; + } else if (strcmpi(hit_type, "BDT_MULTIHIT") == 0) { + hit = BDT_MULTIHIT; + } else if (strcmpi(hit_type, "BDT_NORMAL") != 0) { ShowWarning("%s: Invalid hit type %s specified for skill ID %d in %s! Defaulting to BDT_NORMAL...\n", __func__, hit_type, sk->nameid, conf->file); + return; + } + + skill->level_set_value(sk->hit, hit); } } diff --git a/src/map/skill.h b/src/map/skill.h index 1db2940af..c8ec04a98 100644 --- a/src/map/skill.h +++ b/src/map/skill.h @@ -1751,7 +1751,13 @@ struct s_skill_db { int nameid; char name[MAX_SKILL_NAME_LENGTH + 1]; char desc[MAX_SKILL_DESC_LENGTH + 1]; - int range[MAX_SKILL_LEVEL],hit,inf,element[MAX_SKILL_LEVEL],nk,splash[MAX_SKILL_LEVEL],max; + int range[MAX_SKILL_LEVEL]; + int hit[MAX_SKILL_LEVEL]; + int inf; + int element[MAX_SKILL_LEVEL]; + int nk; + int splash[MAX_SKILL_LEVEL]; + int max; int num[MAX_SKILL_LEVEL]; int cast[MAX_SKILL_LEVEL],walkdelay[MAX_SKILL_LEVEL],delay[MAX_SKILL_LEVEL]; #ifdef RENEWAL_CAST @@ -1957,7 +1963,7 @@ struct skill_interface { /* accesssors */ int (*get_index) (int skill_id); int (*get_type) (int skill_id); - int (*get_hit) (int skill_id); + int (*get_hit) (int skill_id, int skill_lv); int (*get_inf) (int skill_id); int (*get_ele) (int skill_id, int skill_lv); int (*get_nk) (int skill_id); diff --git a/src/plugins/HPMHooking/HPMHooking.Defs.inc b/src/plugins/HPMHooking/HPMHooking.Defs.inc index ae47330f6..4f7c82092 100644 --- a/src/plugins/HPMHooking/HPMHooking.Defs.inc +++ b/src/plugins/HPMHooking/HPMHooking.Defs.inc @@ -7232,8 +7232,8 @@ typedef int (*HPMHOOK_pre_skill_get_index) (int *skill_id); typedef int (*HPMHOOK_post_skill_get_index) (int retVal___, int skill_id); typedef int (*HPMHOOK_pre_skill_get_type) (int *skill_id); typedef int (*HPMHOOK_post_skill_get_type) (int retVal___, int skill_id); -typedef int (*HPMHOOK_pre_skill_get_hit) (int *skill_id); -typedef int (*HPMHOOK_post_skill_get_hit) (int retVal___, int skill_id); +typedef int (*HPMHOOK_pre_skill_get_hit) (int *skill_id, int *skill_lv); +typedef int (*HPMHOOK_post_skill_get_hit) (int retVal___, int skill_id, int skill_lv); typedef int (*HPMHOOK_pre_skill_get_inf) (int *skill_id); typedef int (*HPMHOOK_post_skill_get_inf) (int retVal___, int skill_id); typedef int (*HPMHOOK_pre_skill_get_ele) (int *skill_id, int *skill_lv); diff --git a/src/plugins/HPMHooking/HPMHooking_map.Hooks.inc b/src/plugins/HPMHooking/HPMHooking_map.Hooks.inc index f675caba2..268051ec8 100644 --- a/src/plugins/HPMHooking/HPMHooking_map.Hooks.inc +++ b/src/plugins/HPMHooking/HPMHooking_map.Hooks.inc @@ -76796,15 +76796,15 @@ int HP_skill_get_type(int skill_id) { } return retVal___; } -int HP_skill_get_hit(int skill_id) { +int HP_skill_get_hit(int skill_id, int skill_lv) { int hIndex = 0; int retVal___ = 0; if (HPMHooks.count.HP_skill_get_hit_pre > 0) { - int (*preHookFunc) (int *skill_id); + int (*preHookFunc) (int *skill_id, int *skill_lv); *HPMforce_return = false; for (hIndex = 0; hIndex < HPMHooks.count.HP_skill_get_hit_pre; hIndex++) { preHookFunc = HPMHooks.list.HP_skill_get_hit_pre[hIndex].func; - retVal___ = preHookFunc(&skill_id); + retVal___ = preHookFunc(&skill_id, &skill_lv); } if (*HPMforce_return) { *HPMforce_return = false; @@ -76812,13 +76812,13 @@ int HP_skill_get_hit(int skill_id) { } } { - retVal___ = HPMHooks.source.skill.get_hit(skill_id); + retVal___ = HPMHooks.source.skill.get_hit(skill_id, skill_lv); } if (HPMHooks.count.HP_skill_get_hit_post > 0) { - int (*postHookFunc) (int retVal___, int skill_id); + int (*postHookFunc) (int retVal___, int skill_id, int skill_lv); for (hIndex = 0; hIndex < HPMHooks.count.HP_skill_get_hit_post; hIndex++) { postHookFunc = HPMHooks.list.HP_skill_get_hit_post[hIndex].func; - retVal___ = postHookFunc(retVal___, skill_id); + retVal___ = postHookFunc(retVal___, skill_id, skill_lv); } } return retVal___; -- cgit v1.2.3-60-g2f50 From 29a544a23092e6b1703905f4719ebca412e9558b Mon Sep 17 00:00:00 2001 From: Kenpachi Developer Date: Sun, 17 May 2020 00:28:35 +0200 Subject: Make AttackType can be grouped by levels --- src/map/battle.c | 2 +- src/map/clif.c | 6 +- src/map/skill.c | 168 +++++++++++++++--------- src/map/skill.h | 10 +- src/plugins/HPMHooking/HPMHooking.Defs.inc | 12 +- src/plugins/HPMHooking/HPMHooking_map.Hooks.inc | 36 ++--- 6 files changed, 142 insertions(+), 92 deletions(-) diff --git a/src/map/battle.c b/src/map/battle.c index 796e0de68..236ed70b7 100644 --- a/src/map/battle.c +++ b/src/map/battle.c @@ -6340,7 +6340,7 @@ static enum damage_lv battle_weapon_attack(struct block_list *src, struct block_ skill_id = AB_DUPLELIGHT_MELEE; else skill_id = AB_DUPLELIGHT_MAGIC; - skill->attack(skill->get_type(skill_id), src, src, target, skill_id, sc->data[SC_DUPLELIGHT]->val1, tick, SD_LEVEL); + skill->attack(skill->get_type(skill_id, sc->data[SC_DUPLELIGHT]->val1), src, src, target, skill_id, sc->data[SC_DUPLELIGHT]->val1, tick, SD_LEVEL); } } diff --git a/src/map/clif.c b/src/map/clif.c index 477894805..5b0122401 100644 --- a/src/map/clif.c +++ b/src/map/clif.c @@ -19927,9 +19927,9 @@ static int clif_autoshadowspell_list(struct map_session_data *sd) WFIFOHEAD(fd, 2 * 6 + 4); WFIFOW(fd,0) = 0x442; for (i = 0, c = 0; i < MAX_SKILL_DB; i++) - if( sd->status.skill[i].flag == SKILL_FLAG_PLAGIARIZED && sd->status.skill[i].id > 0 && - sd->status.skill[i].id < GS_GLITTERING && skill->get_type(sd->status.skill[i].id) == BF_MAGIC ) - { // Can't auto cast both Extended class and 3rd class skills. + if (sd->status.skill[i].flag == SKILL_FLAG_PLAGIARIZED && sd->status.skill[i].id > 0 && sd->status.skill[i].id < GS_GLITTERING + && skill->get_type(sd->status.skill[i].id, sd->status.skill[i].lv) == BF_MAGIC) { + // Can't auto cast both Extended class and 3rd class skills. WFIFOW(fd,8+c*2) = sd->status.skill[i].id; c++; } diff --git a/src/map/skill.c b/src/map/skill.c index d24d7ea89..8be97842e 100644 --- a/src/map/skill.c +++ b/src/map/skill.c @@ -624,14 +624,26 @@ static int skill_get_delaynodex(int skill_id, int skill_lv) return skill->dbs->db[idx].delaynodex[skill_get_lvl_idx(skill_lv)]; } -static int skill_get_type(int skill_id) +/** + * Gets a skill's attack type by its ID and level. + * + * @param skill_id The skill's ID. + * @param skill_lv The skill's level. + * @return The skill's attack type corresponding to the passed level. Defaults to BF_NONE (0) in case of error. + * + **/ +static int skill_get_type(int skill_id, int skill_lv) { - int idx; if (skill_id == 0) return BF_NONE; - idx = skill->get_index(skill_id); + + Assert_retr(BF_NONE, skill_lv > 0); + + int idx = skill->get_index(skill_id); + Assert_retr(BF_NONE, idx != 0); - return skill->dbs->db[idx].skill_type; + + return skill->dbs->db[idx].skill_type[skill_get_lvl_idx(skill_lv)]; } static int skill_get_unit_id(int skill_id, int flag) @@ -2178,7 +2190,7 @@ static int skill_additional_effect(struct block_list *src, struct block_list *bl sd->auto_cast_current.type = AUTOCAST_TEMP; skill->consume_requirement(sd,temp,auto_skill_lv,1); - skill->toggle_magicpower(src, temp); + skill->toggle_magicpower(src, temp, auto_skill_lv); skill->castend_type(type, src, tbl, temp, auto_skill_lv, tick, 0); sd->auto_cast_current.type = AUTOCAST_NONE; @@ -3964,7 +3976,7 @@ static int skill_timerskill(int tid, int64 tick, int id, intptr_t data) clif->skill_nodamage(src,target,skl->skill_id,skl->skill_lv,1); break; case WZ_WATERBALL: - skill->toggle_magicpower(src, skl->skill_id); // only the first hit will be amplify + skill->toggle_magicpower(src, skl->skill_id, skl->skill_lv); // only the first hit will be amplify if (!status->isdead(target)) skill->attack(BF_MAGIC,src,src,target,skl->skill_id,skl->skill_lv,tick,skl->flag); if (skl->type>1 && !status->isdead(target) && !status->isdead(src)) { @@ -3984,7 +3996,7 @@ static int skill_timerskill(int tid, int64 tick, int id, intptr_t data) **/ case WL_CHAINLIGHTNING_ATK: skill->attack(BF_MAGIC, src, src, target, skl->skill_id, skl->skill_lv, tick, (9-skl->type)); // Hit a Lightning on the current Target - skill->toggle_magicpower(src, skl->skill_id); // only the first hit will be amplify + skill->toggle_magicpower(src, skl->skill_id, skl->skill_lv); // only the first hit will be amplify if (skl->type < (4 + skl->skill_lv - 1) && skl->x < 3) { // Remaining Chains Hit @@ -4004,7 +4016,7 @@ static int skill_timerskill(int tid, int64 tick, int id, intptr_t data) case WL_TETRAVORTEX_GROUND: clif->skill_nodamage(src, target, skl->skill_id, skl->skill_lv, 1); skill->attack(BF_MAGIC, src, src, target, skl->skill_id, skl->skill_lv, tick, skl->flag); - skill->toggle_magicpower(src, skl->skill_id); // only the first hit will be amplify + skill->toggle_magicpower(src, skl->skill_id, skl->skill_lv); // only the first hit will be amplify if( skl->type == 4 ){ const enum sc_type scs[] = { SC_BURNING, SC_BLOODING, SC_FROSTMISTY, SC_STUN }; // status inflicts are depend on what summoned element is used. int rate = skl->y, index = skl->x-1; @@ -4013,7 +4025,7 @@ static int skill_timerskill(int tid, int64 tick, int id, intptr_t data) break; case WM_REVERBERATION_MELEE: case WM_REVERBERATION_MAGIC: - skill->attack(skill->get_type(skl->skill_id),src, src, target, skl->skill_id, skl->skill_lv, 0, SD_LEVEL); + skill->attack(skill->get_type(skl->skill_id, skl->skill_lv), src, src, target, skl->skill_id, skl->skill_lv, 0, SD_LEVEL); break; case SC_FATALMENACE: if( src == target ) // Casters Part @@ -4118,7 +4130,7 @@ static int skill_timerskill(int tid, int64 tick, int id, intptr_t data) case LG_OVERBRAND_BRANDISH: skill->area_temp[1] = 0; map->foreachinpath(skill->attack_area,src->m,src->x,src->y,skl->x,skl->y,4,2,BL_CHAR, - skill->get_type(skl->skill_id),src,src,skl->skill_id,skl->skill_lv,tick,skl->flag,BCT_ENEMY); + skill->get_type(skl->skill_id, skl->skill_lv), src, src, skl->skill_id, skl->skill_lv, tick, skl->flag, BCT_ENEMY); break; default: skill->timerskill_notarget_unknown(tid, tick, src, ud, skl); @@ -4295,7 +4307,7 @@ static int skill_castend_damage_id(struct block_list *src, struct block_list *bl if (status->isdead(bl)) return 1; - if (skill_id && skill->get_type(skill_id) == BF_MAGIC && status->isimmune(bl) == 100) { + if (skill_id != 0 && skill->get_type(skill_id, skill_lv) == BF_MAGIC && status->isimmune(bl) == 100) { //GTB makes all targeted magic display miss with a single bolt. sc_type sct = status->skill2sc(skill_id); if(sct != SC_NONE) @@ -4508,7 +4520,7 @@ static int skill_castend_damage_id(struct block_list *src, struct block_list *bl skill->area_temp[1] = bl->id; map->foreachinpath(skill->attack_area,src->m,src->x,src->y,bl->x,bl->y, skill->get_splash(skill_id, skill_lv),skill->get_maxcount(skill_id,skill_lv), skill->splash_target(src), - skill->get_type(skill_id),src,src,skill_id,skill_lv,tick,flag,BCT_ENEMY); + skill->get_type(skill_id, skill_lv), src, src, skill_id, skill_lv, tick, flag, BCT_ENEMY); break; case NPC_ACIDBREATH: @@ -4519,7 +4531,7 @@ static int skill_castend_damage_id(struct block_list *src, struct block_list *bl skill->area_temp[1] = bl->id; map->foreachinpath(skill->attack_area,src->m,src->x,src->y,bl->x,bl->y, skill->get_splash(skill_id, skill_lv),skill->get_maxcount(skill_id,skill_lv), skill->splash_target(src), - skill->get_type(skill_id),src,src,skill_id,skill_lv,tick,flag,BCT_ENEMY); + skill->get_type(skill_id, skill_lv), src, src, skill_id, skill_lv, tick, flag, BCT_ENEMY); break; case MO_INVESTIGATE: @@ -4691,7 +4703,7 @@ static int skill_castend_damage_id(struct block_list *src, struct block_list *bl if ( tsc && tsc->data[SC_HOVERING] && ( skill_id == SR_WINDMILL || skill_id == LG_MOONSLASHER ) ) break; - heal = skill->attack(skill->get_type(skill_id), src, src, bl, skill_id, skill_lv, tick, sflag); + heal = skill->attack(skill->get_type(skill_id, skill_lv), src, src, bl, skill_id, skill_lv, tick, sflag); if (skill_id == NPC_VAMPIRE_GIFT && heal > 0) { clif->skill_nodamage(NULL, src, AL_HEAL, heal, 1); status->heal(src, heal, 0, STATUS_HEAL_DEFAULT); @@ -4766,7 +4778,7 @@ static int skill_castend_damage_id(struct block_list *src, struct block_list *bl case MS_MAGNUM: if( flag&1 ) { //Damage depends on distance, so add it to flag if it is > 1 - skill->attack(skill->get_type(skill_id), src, src, bl, skill_id, skill_lv, tick, flag|SD_ANIMATION|distance_bl(src, bl)); + skill->attack(skill->get_type(skill_id, skill_lv), src, src, bl, skill_id, skill_lv, tick, flag|SD_ANIMATION|distance_bl(src, bl)); } break; @@ -4774,9 +4786,9 @@ static int skill_castend_damage_id(struct block_list *src, struct block_list *bl case ML_BRANDISH: //Coded apart for it needs the flag passed to the damage calculation. if (skill->area_temp[1] != bl->id) - skill->attack(skill->get_type(skill_id), src, src, bl, skill_id, skill_lv, tick, flag|SD_ANIMATION); + skill->attack(skill->get_type(skill_id, skill_lv), src, src, bl, skill_id, skill_lv, tick, flag|SD_ANIMATION); else - skill->attack(skill->get_type(skill_id), src, src, bl, skill_id, skill_lv, tick, flag); + skill->attack(skill->get_type(skill_id, skill_lv), src, src, bl, skill_id, skill_lv, tick, flag); break; case KN_BOWLINGBASH: @@ -5171,7 +5183,7 @@ static int skill_castend_damage_id(struct block_list *src, struct block_list *bl break; case WL_DRAINLIFE: { - int heal = skill->attack(skill->get_type(skill_id), src, src, bl, skill_id, skill_lv, tick, flag); + int heal = skill->attack(skill->get_type(skill_id, skill_lv), src, src, bl, skill_id, skill_lv, tick, flag); int rate = 70 + 5 * skill_lv; heal = heal * (5 + 5 * skill_lv) / 100; @@ -5217,7 +5229,7 @@ static int skill_castend_damage_id(struct block_list *src, struct block_list *bl if (sd) { int i; clif->skill_nodamage(src, bl, skill_id, skill_lv, 1); - skill->toggle_magicpower(src, skill_id); + skill->toggle_magicpower(src, skill_id, skill_lv); // Priority is to release SpellBook if (sc && sc->data[SC_READING_SB]) { // SpellBook @@ -5461,7 +5473,7 @@ static int skill_castend_damage_id(struct block_list *src, struct block_list *bl { struct status_change *tsc = status->get_sc(bl); if( tsc && tsc->data[SC_POISON] ) { - skill->attack(skill->get_type(skill_id), src, src, bl, skill_id, skill_lv, tick, flag); + skill->attack(skill->get_type(skill_id, skill_lv), src, src, bl, skill_id, skill_lv, tick, flag); status_change_end(bl, SC_POISON, INVALID_TIMER); } else if( sd ) clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); @@ -5483,7 +5495,7 @@ static int skill_castend_damage_id(struct block_list *src, struct block_list *bl case EL_HURRICANE: case EL_TYPOON_MIS: if( flag&1 ) - skill->attack(skill->get_type(skill_id+1),src,src,bl,skill_id+1,skill_lv,tick,flag); + skill->attack(skill->get_type(skill_id + 1, skill_lv), src, src, bl, skill_id + 1, skill_lv, tick, flag); else { int i = skill->get_splash(skill_id,skill_lv); clif->skill_nodamage(src,battle->get_master(src),skill_id,skill_lv,1); @@ -5491,7 +5503,7 @@ static int skill_castend_damage_id(struct block_list *src, struct block_list *bl if( rnd()%100 < 30 ) map->foreachinrange(skill->area_sub,bl,i,BL_CHAR,src,skill_id,skill_lv,tick,flag|BCT_ENEMY|1,skill->castend_damage_id); else - skill->attack(skill->get_type(skill_id),src,src,bl,skill_id,skill_lv,tick,flag); + skill->attack(skill->get_type(skill_id, skill_lv), src, src, bl, skill_id, skill_lv, tick, flag); } break; @@ -5506,7 +5518,7 @@ static int skill_castend_damage_id(struct block_list *src, struct block_list *bl case EL_STONE_RAIN: if( flag&1 ) - skill->attack(skill->get_type(skill_id),src,src,bl,skill_id,skill_lv,tick,flag); + skill->attack(skill->get_type(skill_id, skill_lv), src, src, bl, skill_id, skill_lv, tick, flag); else { int i = skill->get_splash(skill_id,skill_lv); clif->skill_nodamage(src,battle->get_master(src),skill_id,skill_lv,1); @@ -5514,7 +5526,7 @@ static int skill_castend_damage_id(struct block_list *src, struct block_list *bl if( rnd()%100 < 30 ) map->foreachinrange(skill->area_sub,bl,i,BL_CHAR,src,skill_id,skill_lv,tick,flag|BCT_ENEMY|1,skill->castend_damage_id); else - skill->attack(skill->get_type(skill_id),src,src,bl,skill_id,skill_lv,tick,flag); + skill->attack(skill->get_type(skill_id, skill_lv), src, src, bl, skill_id, skill_lv, tick, flag); } break; @@ -5524,7 +5536,7 @@ static int skill_castend_damage_id(struct block_list *src, struct block_list *bl case EL_STONE_HAMMER: clif->skill_nodamage(src,battle->get_master(src),skill_id,skill_lv,1); clif->skill_damage(src, bl, tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, BDT_SKILL); - skill->attack(skill->get_type(skill_id),src,src,bl,skill_id,skill_lv,tick,flag); + skill->attack(skill->get_type(skill_id, skill_lv), src, src, bl, skill_id, skill_lv, tick, flag); break; case EL_TIDAL_WEAPON: @@ -5541,7 +5553,7 @@ static int skill_castend_damage_id(struct block_list *src, struct block_list *bl elemental->clean_single_effect(ele, skill_id); } if( rnd()%100 < 50 ) - skill->attack(skill->get_type(skill_id),src,src,bl,skill_id,skill_lv,tick,flag); + skill->attack(skill->get_type(skill_id, skill_lv), src, src, bl, skill_id, skill_lv, tick, flag); else { sc_start(src, src,type2,100,skill_lv,skill->get_time(skill_id,skill_lv)); sc_start(src, battle->get_master(src),type,100,ele->bl.id,skill->get_time(skill_id,skill_lv)); @@ -5554,7 +5566,7 @@ static int skill_castend_damage_id(struct block_list *src, struct block_list *bl case MH_MAGMA_FLOW: case MH_HEILIGE_STANGE: if(flag & 1) - skill->attack(skill->get_type(skill_id), src, src, bl, skill_id, skill_lv, tick, flag); + skill->attack(skill->get_type(skill_id, skill_lv), src, src, bl, skill_id, skill_lv, tick, flag); else { map->foreachinrange(skill->area_sub, bl, skill->get_splash(skill_id, skill_lv), skill->splash_target(src), src, skill_id, skill_lv, tick, flag | BCT_ENEMY | SD_SPLASH | 1, skill->castend_damage_id); } @@ -5578,7 +5590,7 @@ static int skill_castend_damage_id(struct block_list *src, struct block_list *bl break; case SU_SV_STEMSPEAR: - skill->attack(skill->get_type(skill_id), src, src, bl, skill_id, skill_lv, tick, flag); + skill->attack(skill->get_type(skill_id, skill_lv), src, src, bl, skill_id, skill_lv, tick, flag); if (status->get_lv(src) >= 30 && (rnd() % 100 < (int)(status->get_lv(src) / 30) + 10)) // TODO: Need activation chance. skill->addtimerskill(src, tick + skill->get_delay(skill_id, skill_lv), bl->id, 0, 0, skill_id, skill_lv, (skill_id == SU_SV_STEMSPEAR) ? BF_MAGIC : BF_WEAPON, flag); break; @@ -5900,7 +5912,7 @@ static int skill_castend_id(int tid, int64 tick, int id, intptr_t data) map->freeblock_lock(); // SC_MAGICPOWER needs to switch states before any damage is actually dealt - skill->toggle_magicpower(src, ud->skill_id); + skill->toggle_magicpower(src, ud->skill_id, ud->skill_lv); #if 0 // On aegis damage skills are also increase by camouflage. Need confirmation on kRO. if( ud->skill_id != RA_CAMOUFLAGE ) // only normal attack and auto cast skills benefit from its bonuses @@ -11189,7 +11201,7 @@ static int skill_castend_pos2(struct block_list *src, int x, int y, uint16 skill } // SC_MAGICPOWER needs to switch states before any damage is actually dealt - skill->toggle_magicpower(src, skill_id); + skill->toggle_magicpower(src, skill_id, skill_lv); switch(skill_id) { case PR_BENEDICTIO: @@ -11807,7 +11819,7 @@ static int skill_castend_pos2(struct block_list *src, int x, int y, uint16 skill case LG_OVERBRAND: skill->area_temp[1] = 0; map->foreachinpath(skill->attack_area,src->m,src->x,src->y,x,y,1,5,BL_CHAR, - skill->get_type(skill_id),src,src,skill_id,skill_lv,tick,flag,BCT_ENEMY); + skill->get_type(skill_id, skill_lv), src, src, skill_id, skill_lv, tick, flag, BCT_ENEMY); skill->addtimerskill(src,timer->gettick() + status_get_amotion(src), 0, x, y, LG_OVERBRAND_BRANDISH, skill_lv, 0, flag); break; @@ -12616,7 +12628,7 @@ static int skill_unit_onplace(struct skill_unit *src, struct block_list *bl, int nullpo_ret(sg=src->group); nullpo_ret(ss=map->id2bl(sg->src_id)); - if (skill->get_type(sg->skill_id) == BF_MAGIC && map->getcell(src->bl.m, &src->bl, src->bl.x, src->bl.y, CELL_CHKLANDPROTECTOR) && sg->skill_id != SA_LANDPROTECTOR) + if (skill->get_type(sg->skill_id, sg->skill_lv) == BF_MAGIC && map->getcell(src->bl.m, &src->bl, src->bl.x, src->bl.y, CELL_CHKLANDPROTECTOR) != 0 && sg->skill_id != SA_LANDPROTECTOR) return 0; //AoE skills are ineffective. [Skotlex] sc = status->get_sc(bl); @@ -12815,7 +12827,7 @@ static int skill_unit_onplace(struct skill_unit *src, struct block_list *bl, int if( status_get_mode(bl)&MD_BOSS ) break; // iRO Wiki says that this skill don't affect to Boss monsters. if( map_flag_vs(bl->m) || bl->id == src->bl.id || battle->check_target(&src->bl,bl, BCT_ENEMY) == 1 ) - skill->attack(skill->get_type(sg->skill_id), ss, &src->bl, bl, sg->skill_id, sg->skill_lv, tick, 0); + skill->attack(skill->get_type(sg->skill_id, sg->skill_lv), ss, &src->bl, bl, sg->skill_id, sg->skill_lv, tick, 0); break; case UNT_REVERBERATION: @@ -13051,7 +13063,7 @@ static int skill_unit_onplace_timer(struct skill_unit *src, struct block_list *b case WZ_STORMGUST: //SG counter does not reset per stormgust. IE: One hit from a SG and two hits from another will freeze you. if (tsc) tsc->sg_counter++; //SG hit counter. - if (skill->attack(skill->get_type(sg->skill_id),ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0) <= 0 && tsc) + if (skill->attack(skill->get_type(sg->skill_id, sg->skill_lv), ss, &src->bl, bl, sg->skill_id, sg->skill_lv, tick, 0) <= 0 && tsc != NULL) tsc->sg_counter=0; //Attack absorbed. break; #endif @@ -13060,7 +13072,7 @@ static int skill_unit_onplace_timer(struct skill_unit *src, struct block_list *b skill->attack(BF_WEAPON,ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0); break; default: - skill->attack(skill->get_type(sg->skill_id),ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0); + skill->attack(skill->get_type(sg->skill_id, sg->skill_lv), ss, &src->bl, bl, sg->skill_id, sg->skill_lv, tick, 0); } break; @@ -13381,7 +13393,7 @@ static int skill_unit_onplace_timer(struct skill_unit *src, struct block_list *b case UNT_PSYCHIC_WAVE: case UNT_MAGMA_ERUPTION: case UNT_MAKIBISHI: - skill->attack(skill->get_type(sg->skill_id),ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0); + skill->attack(skill->get_type(sg->skill_id, sg->skill_lv), ss, &src->bl, bl, sg->skill_id, sg->skill_lv, tick, 0); break; case UNT_GROUNDDRIFT_WIND: @@ -13482,7 +13494,7 @@ static int skill_unit_onplace_timer(struct skill_unit *src, struct block_list *b sec = 3000; // Couldn't trap it? sg->limit = DIFF_TICK32(tick, sg->tick) + sec; } else if( tsc->data[SC_THORNS_TRAP] && bl->id == sg->val2 ) - skill->attack(skill->get_type(GN_THORNS_TRAP), ss, ss, bl, sg->skill_id, sg->skill_lv, tick, SD_LEVEL|SD_ANIMATION); + skill->attack(skill->get_type(GN_THORNS_TRAP, sg->skill_lv), ss, ss, bl, sg->skill_id, sg->skill_lv, tick, SD_LEVEL|SD_ANIMATION); } break; @@ -13494,11 +13506,11 @@ static int skill_unit_onplace_timer(struct skill_unit *src, struct block_list *b default: sc_start4(ss, bl, SC_BURNING, 4 + 4 * sg->skill_lv, sg->skill_lv, 0, ss->id, 0, skill->get_time2(sg->skill_id, sg->skill_lv)); - skill->attack(skill->get_type(sg->skill_id), ss, &src->bl, bl, + skill->attack(skill->get_type(sg->skill_id, sg->skill_lv), ss, &src->bl, bl, sg->skill_id, sg->skill_lv + 10 * sg->val2, tick, 0); break; case 3: - skill->attack(skill->get_type(CR_ACIDDEMONSTRATION), ss, &src->bl, bl, + skill->attack(skill->get_type(CR_ACIDDEMONSTRATION, sg->skill_lv), ss, &src->bl, bl, CR_ACIDDEMONSTRATION, sd ? pc->checkskill(sd, CR_ACIDDEMONSTRATION) : sg->skill_lv, tick, 0); break; @@ -13516,7 +13528,7 @@ static int skill_unit_onplace_timer(struct skill_unit *src, struct block_list *b case UNT_HELLS_PLANT: if( battle->check_target(&src->bl,bl,BCT_ENEMY) > 0 ) - skill->attack(skill->get_type(GN_HELLS_PLANT_ATK), ss, &src->bl, bl, GN_HELLS_PLANT_ATK, sg->skill_lv, tick, 0); + skill->attack(skill->get_type(GN_HELLS_PLANT_ATK, sg->skill_lv), ss, &src->bl, bl, GN_HELLS_PLANT_ATK, sg->skill_lv, tick, 0); if( ss != bl) //The caster is the only one who can step on the Plants, without destroying them sg->limit = DIFF_TICK32(tick, sg->tick) + 100; break; @@ -13524,7 +13536,7 @@ static int skill_unit_onplace_timer(struct skill_unit *src, struct block_list *b case UNT_CLOUD_KILL: if(tsc && !tsc->data[type]) status->change_start(ss,bl,type,10000,sg->skill_lv,sg->group_id,0,0,skill->get_time2(sg->skill_id,sg->skill_lv),SCFLAG_FIXEDRATE); - skill->attack(skill->get_type(sg->skill_id),ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0); + skill->attack(skill->get_type(sg->skill_id, sg->skill_lv), ss, &src->bl, bl, sg->skill_id, sg->skill_lv, tick, 0); break; case UNT_WARMER: @@ -13649,7 +13661,7 @@ static int skill_unit_onplace_timer(struct skill_unit *src, struct block_list *b } sg->limit = DIFF_TICK32(tick, sg->tick) + sec; } else if (tsc->data[type] && bl->id == sg->val2) { - skill->attack(skill->get_type(SU_SV_ROOTTWIST_ATK), ss, &src->bl, bl, SU_SV_ROOTTWIST_ATK, sg->skill_lv, tick, SD_LEVEL|SD_ANIMATION); + skill->attack(skill->get_type(SU_SV_ROOTTWIST_ATK, sg->skill_lv), ss, &src->bl, bl, SU_SV_ROOTTWIST_ATK, sg->skill_lv, tick, SD_LEVEL|SD_ANIMATION); } } break; @@ -14103,16 +14115,16 @@ static int skill_check_condition_mob_master_sub(struct block_list *bl, va_list a * Determines if a given skill should be made to consume ammo * when used by the player. [Skotlex] *------------------------------------------*/ -static int skill_isammotype(struct map_session_data *sd, int skill_id) +static int skill_isammotype(struct map_session_data *sd, int skill_id, int skill_lv) { nullpo_ret(sd); return ( battle_config.arrow_decrement==2 && (sd->weapontype == W_BOW || (sd->weapontype >= W_REVOLVER && sd->weapontype <= W_GRENADE)) && skill_id != HT_PHANTASMIC && - skill->get_type(skill_id) == BF_WEAPON && + skill->get_type(skill_id, skill_lv) == BF_WEAPON && !(skill->get_nk(skill_id)&NK_NO_DAMAGE) && - !skill->get_spiritball(skill_id,1) //Assume spirit spheres are used as ammo instead. + !skill->get_spiritball(skill_id, skill_lv) //Assume spirit spheres are used as ammo instead. ); } @@ -15512,7 +15524,7 @@ static struct skill_condition skill_get_requirement(struct map_session_data *sd, if (req.ammo_qty) req.ammo = skill->dbs->db[idx].ammo; - if (!req.ammo && skill_id && skill->isammotype(sd, skill_id)) { + if (req.ammo == 0 && skill_id != 0 && skill->isammotype(sd, skill_id, skill_lv)) { //Assume this skill is using the weapon, therefore it requires arrows. req.ammo = 0xFFFFFFFF; //Enable use on all ammo types. req.ammo_qty = 1; @@ -17036,7 +17048,7 @@ static int skill_trap_splash(struct block_list *bl, va_list ap) } /* Fall through */ default: - skill->attack(skill->get_type(sg->skill_id), ss, src, bl, sg->skill_id, sg->skill_lv, tick, enemy_count); + skill->attack(skill->get_type(sg->skill_id, sg->skill_lv), ss, src, bl, sg->skill_id, sg->skill_lv, tick, enemy_count); break; } return 1; @@ -18847,12 +18859,12 @@ static int skill_poisoningweapon(struct map_session_data *sd, int nameid) return 0; } -static void skill_toggle_magicpower(struct block_list *bl, uint16 skill_id) +static void skill_toggle_magicpower(struct block_list *bl, uint16 skill_id, int skill_lv) { struct status_change *sc = status->get_sc(bl); // non-offensive and non-magic skills do not affect the status - if (skill->get_nk(skill_id)&NK_NO_DAMAGE || !(skill->get_type(skill_id)&BF_MAGIC)) + if ((skill->get_nk(skill_id) & NK_NO_DAMAGE) != 0 || (skill->get_type(skill_id, skill_lv) & BF_MAGIC) == 0) return; if (sc && sc->count && sc->data[SC_MAGICPOWER]) { @@ -18986,14 +18998,20 @@ static int skill_select_menu(struct map_session_data *sd, uint16 skill_id) idx = skill->get_index(skill_id); - if( skill_id >= GS_GLITTERING || skill->get_type(skill_id) != BF_MAGIC || - (id = sd->status.skill[idx].id) == 0 || sd->status.skill[idx].flag != SKILL_FLAG_PLAGIARIZED ) { + if (skill_id >= GS_GLITTERING || (id = sd->status.skill[idx].id) == 0 + || sd->status.skill[idx].flag != SKILL_FLAG_PLAGIARIZED) { clif->skill_fail(sd, SC_AUTOSHADOWSPELL, 0, 0, 0); return 0; } lv = (aslvl + 1) / 2; // The level the skill will be autocasted - lv = min(lv,sd->status.skill[idx].lv); + lv = min(lv, sd->status.skill[idx].lv); + + if (skill->get_type(skill_id, lv) != BF_MAGIC) { + clif->skill_fail(sd, SC_AUTOSHADOWSPELL, 0, 0, 0); + return 0; + } + prob = (aslvl == 10) ? 15 : (32 - 2 * aslvl); // Probability at level 10 was increased to 15. sc_start4(&sd->bl,&sd->bl,SC__AUTOSHADOWSPELL,100,id,lv,prob,0,skill->get_time(SC_AUTOSHADOWSPELL,aslvl)); return 0; @@ -20622,20 +20640,50 @@ static void skill_validate_attacktype(struct config_setting_t *conf, struct s_sk nullpo_retv(conf); nullpo_retv(sk); - sk->skill_type = BF_NONE; + skill->level_set_value(sk->skill_type, BF_NONE); + + struct config_setting_t *t = libconfig->setting_get_member(conf, "AttackType"); + + if (t != NULL && config_setting_is_group(t)) { + for (int i = 0; i < MAX_SKILL_LEVEL; i++) { + char lv[6]; // Big enough to contain "Lv999" in case of custom MAX_SKILL_LEVEL. + safesnprintf(lv, sizeof(lv), "Lv%d", i + 1); + const char *attack_type; + + if (libconfig->setting_lookup_string(t, lv, &attack_type) == CONFIG_TRUE) { + if (strcmpi(attack_type, "Weapon") == 0) + sk->skill_type[i] = BF_WEAPON; + else if (strcmpi(attack_type, "Magic") == 0) + sk->skill_type[i] = BF_MAGIC; + else if (strcmpi(attack_type, "Misc") == 0) + sk->skill_type[i] = BF_MISC; + else if (strcmpi(attack_type, "None") != 0) + ShowWarning("%s: Invalid attack type %s specified in level %d for skill ID %d in %s! Defaulting to None...\n", + __func__, attack_type, i + 1, sk->nameid, conf->file); + } + } + + return; + } const char *attack_type; if (libconfig->setting_lookup_string(conf, "AttackType", &attack_type) == CONFIG_TRUE) { - if (strcmpi(attack_type, "Weapon") == 0) - sk->skill_type = BF_WEAPON; - else if (strcmpi(attack_type, "Magic") == 0) - sk->skill_type = BF_MAGIC; - else if (strcmpi(attack_type, "Misc") == 0) - sk->skill_type = BF_MISC; - else if (strcmpi(attack_type, "None") != 0) + int attack = BF_NONE; + + if (strcmpi(attack_type, "Weapon") == 0) { + attack = BF_WEAPON; + } else if (strcmpi(attack_type, "Magic") == 0) { + attack = BF_MAGIC; + } else if (strcmpi(attack_type, "Misc") == 0) { + attack = BF_MISC; + } else if (strcmpi(attack_type, "None") != 0) { ShowWarning("%s: Invalid attack type %s specified for skill ID %d in %s! Defaulting to None...\n", __func__, attack_type, sk->nameid, conf->file); + return; + } + + skill->level_set_value(sk->skill_type, attack); } } diff --git a/src/map/skill.h b/src/map/skill.h index c8ec04a98..af8f3774b 100644 --- a/src/map/skill.h +++ b/src/map/skill.h @@ -1765,7 +1765,9 @@ struct s_skill_db { #endif int upkeep_time[MAX_SKILL_LEVEL],upkeep_time2[MAX_SKILL_LEVEL],cooldown[MAX_SKILL_LEVEL]; int castcancel,cast_def_rate; - int inf2,maxcount[MAX_SKILL_LEVEL],skill_type; + int inf2; + int maxcount[MAX_SKILL_LEVEL]; + int skill_type[MAX_SKILL_LEVEL]; int blewcount[MAX_SKILL_LEVEL]; int hp[MAX_SKILL_LEVEL],sp[MAX_SKILL_LEVEL],mhp[MAX_SKILL_LEVEL],hp_rate[MAX_SKILL_LEVEL],sp_rate[MAX_SKILL_LEVEL],zeny[MAX_SKILL_LEVEL]; int weapon,ammo,ammo_qty[MAX_SKILL_LEVEL],state,spiritball[MAX_SKILL_LEVEL]; @@ -1962,7 +1964,7 @@ struct skill_interface { int unit_group_newid; /* accesssors */ int (*get_index) (int skill_id); - int (*get_type) (int skill_id); + int (*get_type) (int skill_id, int skill_lv); int (*get_hit) (int skill_id, int skill_lv); int (*get_inf) (int skill_id); int (*get_ele) (int skill_id, int skill_lv); @@ -2013,7 +2015,7 @@ struct skill_interface { int (*get_casttype2) (int index); bool (*is_combo) (int skill_id); int (*name2id) (const char* name); - int (*isammotype) (struct map_session_data *sd, int skill_id); + int (*isammotype) (struct map_session_data *sd, int skill_id, int skill_lv); int (*castend_id) (int tid, int64 tick, int id, intptr_t data); int (*castend_pos) (int tid, int64 tick, int id, intptr_t data); int (*castend_map) ( struct map_session_data *sd,uint16 skill_id, const char *mapname); @@ -2083,7 +2085,7 @@ struct skill_interface { int (*check_unit_range_sub) (struct block_list *bl, va_list ap); int (*check_unit_range2) (struct block_list *bl, int x, int y, uint16 skill_id, uint16 skill_lv); int (*check_unit_range2_sub) (struct block_list *bl, va_list ap); - void (*toggle_magicpower) (struct block_list *bl, uint16 skill_id); + void (*toggle_magicpower) (struct block_list *bl, uint16 skill_id, int skill_lv); int (*magic_reflect) (struct block_list* src, struct block_list* bl, int type); int (*onskillusage) (struct map_session_data *sd, struct block_list *bl, uint16 skill_id, int64 tick); int (*cell_overlap) (struct block_list *bl, va_list ap); diff --git a/src/plugins/HPMHooking/HPMHooking.Defs.inc b/src/plugins/HPMHooking/HPMHooking.Defs.inc index 4f7c82092..5f789a9b1 100644 --- a/src/plugins/HPMHooking/HPMHooking.Defs.inc +++ b/src/plugins/HPMHooking/HPMHooking.Defs.inc @@ -7230,8 +7230,8 @@ typedef void (*HPMHOOK_pre_skill_read_db) (bool *minimal); typedef void (*HPMHOOK_post_skill_read_db) (bool minimal); typedef int (*HPMHOOK_pre_skill_get_index) (int *skill_id); typedef int (*HPMHOOK_post_skill_get_index) (int retVal___, int skill_id); -typedef int (*HPMHOOK_pre_skill_get_type) (int *skill_id); -typedef int (*HPMHOOK_post_skill_get_type) (int retVal___, int skill_id); +typedef int (*HPMHOOK_pre_skill_get_type) (int *skill_id, int *skill_lv); +typedef int (*HPMHOOK_post_skill_get_type) (int retVal___, int skill_id, int skill_lv); typedef int (*HPMHOOK_pre_skill_get_hit) (int *skill_id, int *skill_lv); typedef int (*HPMHOOK_post_skill_get_hit) (int retVal___, int skill_id, int skill_lv); typedef int (*HPMHOOK_pre_skill_get_inf) (int *skill_id); @@ -7330,8 +7330,8 @@ typedef bool (*HPMHOOK_pre_skill_is_combo) (int *skill_id); typedef bool (*HPMHOOK_post_skill_is_combo) (bool retVal___, int skill_id); typedef int (*HPMHOOK_pre_skill_name2id) (const char **name); typedef int (*HPMHOOK_post_skill_name2id) (int retVal___, const char *name); -typedef int (*HPMHOOK_pre_skill_isammotype) (struct map_session_data **sd, int *skill_id); -typedef int (*HPMHOOK_post_skill_isammotype) (int retVal___, struct map_session_data *sd, int skill_id); +typedef int (*HPMHOOK_pre_skill_isammotype) (struct map_session_data **sd, int *skill_id, int *skill_lv); +typedef int (*HPMHOOK_post_skill_isammotype) (int retVal___, struct map_session_data *sd, int skill_id, int skill_lv); typedef int (*HPMHOOK_pre_skill_castend_id) (int *tid, int64 *tick, int *id, intptr_t *data); typedef int (*HPMHOOK_post_skill_castend_id) (int retVal___, int tid, int64 tick, int id, intptr_t data); typedef int (*HPMHOOK_pre_skill_castend_pos) (int *tid, int64 *tick, int *id, intptr_t *data); @@ -7470,8 +7470,8 @@ typedef int (*HPMHOOK_pre_skill_check_unit_range2) (struct block_list **bl, int typedef int (*HPMHOOK_post_skill_check_unit_range2) (int retVal___, struct block_list *bl, int x, int y, uint16 skill_id, uint16 skill_lv); typedef int (*HPMHOOK_pre_skill_check_unit_range2_sub) (struct block_list **bl, va_list ap); typedef int (*HPMHOOK_post_skill_check_unit_range2_sub) (int retVal___, struct block_list *bl, va_list ap); -typedef void (*HPMHOOK_pre_skill_toggle_magicpower) (struct block_list **bl, uint16 *skill_id); -typedef void (*HPMHOOK_post_skill_toggle_magicpower) (struct block_list *bl, uint16 skill_id); +typedef void (*HPMHOOK_pre_skill_toggle_magicpower) (struct block_list **bl, uint16 *skill_id, int *skill_lv); +typedef void (*HPMHOOK_post_skill_toggle_magicpower) (struct block_list *bl, uint16 skill_id, int skill_lv); typedef int (*HPMHOOK_pre_skill_magic_reflect) (struct block_list **src, struct block_list **bl, int *type); typedef int (*HPMHOOK_post_skill_magic_reflect) (int retVal___, struct block_list *src, struct block_list *bl, int type); typedef int (*HPMHOOK_pre_skill_onskillusage) (struct map_session_data **sd, struct block_list **bl, uint16 *skill_id, int64 *tick); diff --git a/src/plugins/HPMHooking/HPMHooking_map.Hooks.inc b/src/plugins/HPMHooking/HPMHooking_map.Hooks.inc index 268051ec8..2f38a5a3d 100644 --- a/src/plugins/HPMHooking/HPMHooking_map.Hooks.inc +++ b/src/plugins/HPMHooking/HPMHooking_map.Hooks.inc @@ -76769,15 +76769,15 @@ int HP_skill_get_index(int skill_id) { } return retVal___; } -int HP_skill_get_type(int skill_id) { +int HP_skill_get_type(int skill_id, int skill_lv) { int hIndex = 0; int retVal___ = 0; if (HPMHooks.count.HP_skill_get_type_pre > 0) { - int (*preHookFunc) (int *skill_id); + int (*preHookFunc) (int *skill_id, int *skill_lv); *HPMforce_return = false; for (hIndex = 0; hIndex < HPMHooks.count.HP_skill_get_type_pre; hIndex++) { preHookFunc = HPMHooks.list.HP_skill_get_type_pre[hIndex].func; - retVal___ = preHookFunc(&skill_id); + retVal___ = preHookFunc(&skill_id, &skill_lv); } if (*HPMforce_return) { *HPMforce_return = false; @@ -76785,13 +76785,13 @@ int HP_skill_get_type(int skill_id) { } } { - retVal___ = HPMHooks.source.skill.get_type(skill_id); + retVal___ = HPMHooks.source.skill.get_type(skill_id, skill_lv); } if (HPMHooks.count.HP_skill_get_type_post > 0) { - int (*postHookFunc) (int retVal___, int skill_id); + int (*postHookFunc) (int retVal___, int skill_id, int skill_lv); for (hIndex = 0; hIndex < HPMHooks.count.HP_skill_get_type_post; hIndex++) { postHookFunc = HPMHooks.list.HP_skill_get_type_post[hIndex].func; - retVal___ = postHookFunc(retVal___, skill_id); + retVal___ = postHookFunc(retVal___, skill_id, skill_lv); } } return retVal___; @@ -78119,15 +78119,15 @@ int HP_skill_name2id(const char *name) { } return retVal___; } -int HP_skill_isammotype(struct map_session_data *sd, int skill_id) { +int HP_skill_isammotype(struct map_session_data *sd, int skill_id, int skill_lv) { int hIndex = 0; int retVal___ = 0; if (HPMHooks.count.HP_skill_isammotype_pre > 0) { - int (*preHookFunc) (struct map_session_data **sd, int *skill_id); + int (*preHookFunc) (struct map_session_data **sd, int *skill_id, int *skill_lv); *HPMforce_return = false; for (hIndex = 0; hIndex < HPMHooks.count.HP_skill_isammotype_pre; hIndex++) { preHookFunc = HPMHooks.list.HP_skill_isammotype_pre[hIndex].func; - retVal___ = preHookFunc(&sd, &skill_id); + retVal___ = preHookFunc(&sd, &skill_id, &skill_lv); } if (*HPMforce_return) { *HPMforce_return = false; @@ -78135,13 +78135,13 @@ int HP_skill_isammotype(struct map_session_data *sd, int skill_id) { } } { - retVal___ = HPMHooks.source.skill.isammotype(sd, skill_id); + retVal___ = HPMHooks.source.skill.isammotype(sd, skill_id, skill_lv); } if (HPMHooks.count.HP_skill_isammotype_post > 0) { - int (*postHookFunc) (int retVal___, struct map_session_data *sd, int skill_id); + int (*postHookFunc) (int retVal___, struct map_session_data *sd, int skill_id, int skill_lv); for (hIndex = 0; hIndex < HPMHooks.count.HP_skill_isammotype_post; hIndex++) { postHookFunc = HPMHooks.list.HP_skill_isammotype_post[hIndex].func; - retVal___ = postHookFunc(retVal___, sd, skill_id); + retVal___ = postHookFunc(retVal___, sd, skill_id, skill_lv); } } return retVal___; @@ -80039,14 +80039,14 @@ int HP_skill_check_unit_range2_sub(struct block_list *bl, va_list ap) { } return retVal___; } -void HP_skill_toggle_magicpower(struct block_list *bl, uint16 skill_id) { +void HP_skill_toggle_magicpower(struct block_list *bl, uint16 skill_id, int skill_lv) { int hIndex = 0; if (HPMHooks.count.HP_skill_toggle_magicpower_pre > 0) { - void (*preHookFunc) (struct block_list **bl, uint16 *skill_id); + void (*preHookFunc) (struct block_list **bl, uint16 *skill_id, int *skill_lv); *HPMforce_return = false; for (hIndex = 0; hIndex < HPMHooks.count.HP_skill_toggle_magicpower_pre; hIndex++) { preHookFunc = HPMHooks.list.HP_skill_toggle_magicpower_pre[hIndex].func; - preHookFunc(&bl, &skill_id); + preHookFunc(&bl, &skill_id, &skill_lv); } if (*HPMforce_return) { *HPMforce_return = false; @@ -80054,13 +80054,13 @@ void HP_skill_toggle_magicpower(struct block_list *bl, uint16 skill_id) { } } { - HPMHooks.source.skill.toggle_magicpower(bl, skill_id); + HPMHooks.source.skill.toggle_magicpower(bl, skill_id, skill_lv); } if (HPMHooks.count.HP_skill_toggle_magicpower_post > 0) { - void (*postHookFunc) (struct block_list *bl, uint16 skill_id); + void (*postHookFunc) (struct block_list *bl, uint16 skill_id, int skill_lv); for (hIndex = 0; hIndex < HPMHooks.count.HP_skill_toggle_magicpower_post; hIndex++) { postHookFunc = HPMHooks.list.HP_skill_toggle_magicpower_post[hIndex].func; - postHookFunc(bl, skill_id); + postHookFunc(bl, skill_id, skill_lv); } } return; -- cgit v1.2.3-60-g2f50 From f9ea7676708eebef5006466e8e108b863f7106d9 Mon Sep 17 00:00:00 2001 From: Kenpachi Developer Date: Sun, 17 May 2020 00:50:37 +0200 Subject: Make InterruptCast can be grouped by levels --- src/map/skill.c | 43 +++++++++++++++++++++---- src/map/skill.h | 5 +-- src/map/unit.c | 4 +-- src/plugins/HPMHooking/HPMHooking.Defs.inc | 4 +-- src/plugins/HPMHooking/HPMHooking_map.Hooks.inc | 12 +++---- 5 files changed, 49 insertions(+), 19 deletions(-) diff --git a/src/map/skill.c b/src/map/skill.c index 8be97842e..5ac1bdf71 100644 --- a/src/map/skill.c +++ b/src/map/skill.c @@ -539,14 +539,26 @@ static int skill_get_inf2(int skill_id) return skill->dbs->db[idx].inf2; } -static int skill_get_castcancel(int skill_id) +/** + * Gets a skill's cast interruptibility by its ID and level. + * + * @param skill_id The skill's ID. + * @param skill_lv The skill's level. + * @return The skill's cast interruptibility corresponding to the passed level. Defaults to 0 in case of error. + * + **/ +static int skill_get_castcancel(int skill_id, int skill_lv) { - int idx; if (skill_id == 0) return 0; - idx = skill->get_index(skill_id); + + Assert_ret(skill_lv > 0); + + int idx = skill->get_index(skill_id); + Assert_ret(idx != 0); - return skill->dbs->db[idx].castcancel; + + return skill->dbs->db[idx].castcancel[skill_get_lvl_idx(skill_lv)]; } static int skill_get_maxcount(int skill_id, int skill_lv) @@ -20923,12 +20935,29 @@ static void skill_validate_interrupt_cast(struct config_setting_t *conf, struct nullpo_retv(conf); nullpo_retv(sk); - sk->castcancel = 0; + skill->level_set_value(sk->castcancel, 0); + + struct config_setting_t *t = libconfig->setting_get_member(conf, "InterruptCast"); + + if (t != NULL && config_setting_is_group(t)) { + for (int i = 0; i < MAX_SKILL_LEVEL; i++) { + char lv[6]; // Big enough to contain "Lv999" in case of custom MAX_SKILL_LEVEL. + safesnprintf(lv, sizeof(lv), "Lv%d", i + 1); + int interrupt_cast; + + if (libconfig->setting_lookup_bool(t, lv, &interrupt_cast) == CONFIG_TRUE) + sk->castcancel[i] = (interrupt_cast != 0) ? 1 : 0; + } + + return; + } int interrupt_cast; - if (libconfig->setting_lookup_bool(conf, "InterruptCast", &interrupt_cast) == CONFIG_TRUE) - sk->castcancel = (interrupt_cast != 0) ? 1 : 0; + if (libconfig->setting_lookup_bool(conf, "InterruptCast", &interrupt_cast) == CONFIG_TRUE) { + if (interrupt_cast != 0) + skill->level_set_value(sk->castcancel, 1); + } } /** diff --git a/src/map/skill.h b/src/map/skill.h index af8f3774b..598917f2a 100644 --- a/src/map/skill.h +++ b/src/map/skill.h @@ -1764,7 +1764,8 @@ struct s_skill_db { int fixed_cast[MAX_SKILL_LEVEL]; #endif int upkeep_time[MAX_SKILL_LEVEL],upkeep_time2[MAX_SKILL_LEVEL],cooldown[MAX_SKILL_LEVEL]; - int castcancel,cast_def_rate; + int castcancel[MAX_SKILL_LEVEL]; + int cast_def_rate; int inf2; int maxcount[MAX_SKILL_LEVEL]; int skill_type[MAX_SKILL_LEVEL]; @@ -1997,7 +1998,7 @@ struct skill_interface { int (*get_ammo_qty) (int skill_id, int skill_lv); int (*get_unit_id) (int skill_id, int flag); int (*get_inf2) (int skill_id); - int (*get_castcancel) (int skill_id); + int (*get_castcancel) (int skill_id, int skill_lv); int (*get_maxcount) (int skill_id, int skill_lv); int (*get_blewcount) (int skill_id, int skill_lv); int (*get_unit_flag) (int skill_id); diff --git a/src/map/unit.c b/src/map/unit.c index 53e517045..223a4a82b 100644 --- a/src/map/unit.c +++ b/src/map/unit.c @@ -1152,7 +1152,7 @@ static int unit_stop_walking(struct block_list *bl, int flag) static int unit_skilluse_id(struct block_list *src, int target_id, uint16 skill_id, uint16 skill_lv) { int casttime = skill->cast_fix(src, skill_id, skill_lv); - int castcancel = skill->get_castcancel(skill_id); + int castcancel = skill->get_castcancel(skill_id, skill_lv); int ret = unit->skilluse_id2(src, target_id, skill_id, skill_lv, casttime, castcancel); struct map_session_data *sd = BL_CAST(BL_PC, src); @@ -1790,7 +1790,7 @@ static int unit_skilluse_id2(struct block_list *src, int target_id, uint16 skill static int unit_skilluse_pos(struct block_list *src, short skill_x, short skill_y, uint16 skill_id, uint16 skill_lv) { int casttime = skill->cast_fix(src, skill_id, skill_lv); - int castcancel = skill->get_castcancel(skill_id); + int castcancel = skill->get_castcancel(skill_id, skill_lv); int ret = unit->skilluse_pos2(src, skill_x, skill_y, skill_id, skill_lv, casttime, castcancel); struct map_session_data *sd = BL_CAST(BL_PC, src); diff --git a/src/plugins/HPMHooking/HPMHooking.Defs.inc b/src/plugins/HPMHooking/HPMHooking.Defs.inc index 5f789a9b1..8973ecfdb 100644 --- a/src/plugins/HPMHooking/HPMHooking.Defs.inc +++ b/src/plugins/HPMHooking/HPMHooking.Defs.inc @@ -7296,8 +7296,8 @@ typedef int (*HPMHOOK_pre_skill_get_unit_id) (int *skill_id, int *flag); typedef int (*HPMHOOK_post_skill_get_unit_id) (int retVal___, int skill_id, int flag); typedef int (*HPMHOOK_pre_skill_get_inf2) (int *skill_id); typedef int (*HPMHOOK_post_skill_get_inf2) (int retVal___, int skill_id); -typedef int (*HPMHOOK_pre_skill_get_castcancel) (int *skill_id); -typedef int (*HPMHOOK_post_skill_get_castcancel) (int retVal___, int skill_id); +typedef int (*HPMHOOK_pre_skill_get_castcancel) (int *skill_id, int *skill_lv); +typedef int (*HPMHOOK_post_skill_get_castcancel) (int retVal___, int skill_id, int skill_lv); typedef int (*HPMHOOK_pre_skill_get_maxcount) (int *skill_id, int *skill_lv); typedef int (*HPMHOOK_post_skill_get_maxcount) (int retVal___, int skill_id, int skill_lv); typedef int (*HPMHOOK_pre_skill_get_blewcount) (int *skill_id, int *skill_lv); diff --git a/src/plugins/HPMHooking/HPMHooking_map.Hooks.inc b/src/plugins/HPMHooking/HPMHooking_map.Hooks.inc index 2f38a5a3d..3b4948853 100644 --- a/src/plugins/HPMHooking/HPMHooking_map.Hooks.inc +++ b/src/plugins/HPMHooking/HPMHooking_map.Hooks.inc @@ -77660,15 +77660,15 @@ int HP_skill_get_inf2(int skill_id) { } return retVal___; } -int HP_skill_get_castcancel(int skill_id) { +int HP_skill_get_castcancel(int skill_id, int skill_lv) { int hIndex = 0; int retVal___ = 0; if (HPMHooks.count.HP_skill_get_castcancel_pre > 0) { - int (*preHookFunc) (int *skill_id); + int (*preHookFunc) (int *skill_id, int *skill_lv); *HPMforce_return = false; for (hIndex = 0; hIndex < HPMHooks.count.HP_skill_get_castcancel_pre; hIndex++) { preHookFunc = HPMHooks.list.HP_skill_get_castcancel_pre[hIndex].func; - retVal___ = preHookFunc(&skill_id); + retVal___ = preHookFunc(&skill_id, &skill_lv); } if (*HPMforce_return) { *HPMforce_return = false; @@ -77676,13 +77676,13 @@ int HP_skill_get_castcancel(int skill_id) { } } { - retVal___ = HPMHooks.source.skill.get_castcancel(skill_id); + retVal___ = HPMHooks.source.skill.get_castcancel(skill_id, skill_lv); } if (HPMHooks.count.HP_skill_get_castcancel_post > 0) { - int (*postHookFunc) (int retVal___, int skill_id); + int (*postHookFunc) (int retVal___, int skill_id, int skill_lv); for (hIndex = 0; hIndex < HPMHooks.count.HP_skill_get_castcancel_post; hIndex++) { postHookFunc = HPMHooks.list.HP_skill_get_castcancel_post[hIndex].func; - retVal___ = postHookFunc(retVal___, skill_id); + retVal___ = postHookFunc(retVal___, skill_id, skill_lv); } } return retVal___; -- cgit v1.2.3-60-g2f50 From a30f969b07c2d4e0f7e3189c5566fb50738babde Mon Sep 17 00:00:00 2001 From: Kenpachi Developer Date: Sun, 17 May 2020 01:12:09 +0200 Subject: Make CastDefRate can be grouped by levels --- src/map/skill.c | 44 +++++++++++++++++++++---- src/map/skill.h | 4 +-- src/map/status.c | 2 +- src/plugins/HPMHooking/HPMHooking.Defs.inc | 4 +-- src/plugins/HPMHooking/HPMHooking_map.Hooks.inc | 12 +++---- 5 files changed, 49 insertions(+), 17 deletions(-) diff --git a/src/map/skill.c b/src/map/skill.c index 5ac1bdf71..e987e4c8f 100644 --- a/src/map/skill.c +++ b/src/map/skill.c @@ -484,14 +484,26 @@ static int skill_get_time2(int skill_id, int skill_lv) return skill->dbs->db[idx].upkeep_time2[skill_get_lvl_idx(skill_lv)]; } -static int skill_get_castdef(int skill_id) +/** + * Gets a skill's cast defence rate by its ID and level. + * + * @param skill_id The skill's ID. + * @param skill_lv The skill's level. + * @return The skill's cast defence rate corresponding to the passed level. Defaults to 0 in case of error. + * + **/ +static int skill_get_castdef(int skill_id, int skill_lv) { - int idx; if (skill_id == 0) return 0; - idx = skill->get_index(skill_id); + + Assert_ret(skill_lv > 0); + + int idx = skill->get_index(skill_id); + Assert_ret(idx != 0); - return skill->dbs->db[idx].cast_def_rate; + + return skill->dbs->db[idx].cast_def_rate[skill_get_lvl_idx(skill_lv)]; } static int skill_get_weapontype(int skill_id) @@ -20972,13 +20984,33 @@ static void skill_validate_cast_def_rate(struct config_setting_t *conf, struct s nullpo_retv(conf); nullpo_retv(sk); - sk->cast_def_rate = 0; + skill->level_set_value(sk->cast_def_rate, 0); + + struct config_setting_t *t = libconfig->setting_get_member(conf, "CastDefRate"); + + if (t != NULL && config_setting_is_group(t)) { + for (int i = 0; i < MAX_SKILL_LEVEL; i++) { + char lv[6]; // Big enough to contain "Lv999" in case of custom MAX_SKILL_LEVEL. + safesnprintf(lv, sizeof(lv), "Lv%d", i + 1); + int cast_def_rate; + + if (libconfig->setting_lookup_int(t, lv, &cast_def_rate) == CONFIG_TRUE) { + if (cast_def_rate >= SHRT_MIN && cast_def_rate <= SHRT_MAX) + sk->cast_def_rate[i] = cast_def_rate; + else + ShowWarning("%s: Invalid cast defence rate %d specified in level %d for skill ID %d in %s! Minimum is %d, maximum is %d. Defaulting to 0...\n", + __func__, cast_def_rate, i + 1, sk->nameid, conf->file, SHRT_MIN, SHRT_MAX); + } + } + + return; + } int cast_def_rate; if (libconfig->setting_lookup_int(conf, "CastDefRate", &cast_def_rate) == CONFIG_TRUE) { if (cast_def_rate >= SHRT_MIN && cast_def_rate <= SHRT_MAX) - sk->cast_def_rate = cast_def_rate; + skill->level_set_value(sk->cast_def_rate, cast_def_rate); else ShowWarning("%s: Invalid cast defence rate %d specified for skill ID %d in %s! Minimum is %d, maximum is %d. Defaulting to 0...\n", __func__, cast_def_rate, sk->nameid, conf->file, SHRT_MIN, SHRT_MAX); diff --git a/src/map/skill.h b/src/map/skill.h index 598917f2a..a185535a1 100644 --- a/src/map/skill.h +++ b/src/map/skill.h @@ -1765,7 +1765,7 @@ struct s_skill_db { #endif int upkeep_time[MAX_SKILL_LEVEL],upkeep_time2[MAX_SKILL_LEVEL],cooldown[MAX_SKILL_LEVEL]; int castcancel[MAX_SKILL_LEVEL]; - int cast_def_rate; + int cast_def_rate[MAX_SKILL_LEVEL]; int inf2; int maxcount[MAX_SKILL_LEVEL]; int skill_type[MAX_SKILL_LEVEL]; @@ -1992,7 +1992,7 @@ struct skill_interface { int (*get_time2) (int skill_id, int skill_lv); int (*get_castnodex) (int skill_id, int skill_lv); int (*get_delaynodex) (int skill_id, int skill_lv); - int (*get_castdef) (int skill_id); + int (*get_castdef) (int skill_id, int skill_lv); int (*get_weapontype) (int skill_id); int (*get_ammotype) (int skill_id); int (*get_ammo_qty) (int skill_id, int skill_lv); diff --git a/src/map/status.c b/src/map/status.c index a6e6b24c0..637342632 100644 --- a/src/map/status.c +++ b/src/map/status.c @@ -6503,7 +6503,7 @@ static defType status_get_def(struct block_list *bl) int def = st ? st->def : 0; ud = unit->bl2ud(bl); if (ud && ud->skilltimer != INVALID_TIMER) - def -= def * skill->get_castdef(ud->skill_id)/100; + def -= def * skill->get_castdef(ud->skill_id, ud->skill_lv) / 100; return cap_value(def, DEFTYPE_MIN, DEFTYPE_MAX); } diff --git a/src/plugins/HPMHooking/HPMHooking.Defs.inc b/src/plugins/HPMHooking/HPMHooking.Defs.inc index 8973ecfdb..5ce0ba4e6 100644 --- a/src/plugins/HPMHooking/HPMHooking.Defs.inc +++ b/src/plugins/HPMHooking/HPMHooking.Defs.inc @@ -7284,8 +7284,8 @@ typedef int (*HPMHOOK_pre_skill_get_castnodex) (int *skill_id, int *skill_lv); typedef int (*HPMHOOK_post_skill_get_castnodex) (int retVal___, int skill_id, int skill_lv); typedef int (*HPMHOOK_pre_skill_get_delaynodex) (int *skill_id, int *skill_lv); typedef int (*HPMHOOK_post_skill_get_delaynodex) (int retVal___, int skill_id, int skill_lv); -typedef int (*HPMHOOK_pre_skill_get_castdef) (int *skill_id); -typedef int (*HPMHOOK_post_skill_get_castdef) (int retVal___, int skill_id); +typedef int (*HPMHOOK_pre_skill_get_castdef) (int *skill_id, int *skill_lv); +typedef int (*HPMHOOK_post_skill_get_castdef) (int retVal___, int skill_id, int skill_lv); typedef int (*HPMHOOK_pre_skill_get_weapontype) (int *skill_id); typedef int (*HPMHOOK_post_skill_get_weapontype) (int retVal___, int skill_id); typedef int (*HPMHOOK_pre_skill_get_ammotype) (int *skill_id); diff --git a/src/plugins/HPMHooking/HPMHooking_map.Hooks.inc b/src/plugins/HPMHooking/HPMHooking_map.Hooks.inc index 3b4948853..357ba2421 100644 --- a/src/plugins/HPMHooking/HPMHooking_map.Hooks.inc +++ b/src/plugins/HPMHooking/HPMHooking_map.Hooks.inc @@ -77498,15 +77498,15 @@ int HP_skill_get_delaynodex(int skill_id, int skill_lv) { } return retVal___; } -int HP_skill_get_castdef(int skill_id) { +int HP_skill_get_castdef(int skill_id, int skill_lv) { int hIndex = 0; int retVal___ = 0; if (HPMHooks.count.HP_skill_get_castdef_pre > 0) { - int (*preHookFunc) (int *skill_id); + int (*preHookFunc) (int *skill_id, int *skill_lv); *HPMforce_return = false; for (hIndex = 0; hIndex < HPMHooks.count.HP_skill_get_castdef_pre; hIndex++) { preHookFunc = HPMHooks.list.HP_skill_get_castdef_pre[hIndex].func; - retVal___ = preHookFunc(&skill_id); + retVal___ = preHookFunc(&skill_id, &skill_lv); } if (*HPMforce_return) { *HPMforce_return = false; @@ -77514,13 +77514,13 @@ int HP_skill_get_castdef(int skill_id) { } } { - retVal___ = HPMHooks.source.skill.get_castdef(skill_id); + retVal___ = HPMHooks.source.skill.get_castdef(skill_id, skill_lv); } if (HPMHooks.count.HP_skill_get_castdef_post > 0) { - int (*postHookFunc) (int retVal___, int skill_id); + int (*postHookFunc) (int retVal___, int skill_id, int skill_lv); for (hIndex = 0; hIndex < HPMHooks.count.HP_skill_get_castdef_post; hIndex++) { postHookFunc = HPMHooks.list.HP_skill_get_castdef_post[hIndex].func; - retVal___ = postHookFunc(retVal___, skill_id); + retVal___ = postHookFunc(retVal___, skill_id, skill_lv); } } return retVal___; -- cgit v1.2.3-60-g2f50 From 529401c53e79ed848cea6dfaab75366d05174a43 Mon Sep 17 00:00:00 2001 From: Kenpachi Developer Date: Sun, 17 May 2020 02:09:06 +0200 Subject: Make Requirements->State can be grouped by levels --- src/map/skill.c | 168 ++++++++++++++++-------- src/map/skill.h | 9 +- src/map/unit.c | 4 +- src/plugins/HPMHooking/HPMHooking.Defs.inc | 4 +- src/plugins/HPMHooking/HPMHooking_map.Hooks.inc | 12 +- 5 files changed, 130 insertions(+), 67 deletions(-) diff --git a/src/map/skill.c b/src/map/skill.c index e987e4c8f..306237565 100644 --- a/src/map/skill.c +++ b/src/map/skill.c @@ -332,14 +332,26 @@ static int skill_get_sp_rate(int skill_id, int skill_lv) return skill->dbs->db[idx].sp_rate[skill_get_lvl_idx(skill_lv)]; } -static int skill_get_state(int skill_id) +/** + * Gets a skill's required state by its ID and level. + * + * @param skill_id The skill's ID. + * @param skill_lv The skill's level. + * @return The skill's required state corresponding to the passed level. Defaults to ST_NONE (0) in case of error. + * + **/ +static int skill_get_state(int skill_id, int skill_lv) { - int idx; if (skill_id == 0) return ST_NONE; - idx = skill->get_index(skill_id); + + Assert_retr(ST_NONE, skill_lv > 0); + + int idx = skill->get_index(skill_id); + Assert_retr(ST_NONE, idx != 0); - return skill->dbs->db[idx].state; + + return skill->dbs->db[idx].state[skill_get_lvl_idx(skill_lv)]; } static int skill_get_spiritball(int skill_id, int skill_lv) @@ -3847,7 +3859,7 @@ static int skill_check_condition_mercenary(struct block_list *bl, int skill_id, sp = skill->dbs->db[idx].sp[lv-1]; hp_rate = skill->dbs->db[idx].hp_rate[lv-1]; sp_rate = skill->dbs->db[idx].sp_rate[lv-1]; - state = skill->dbs->db[idx].state; + state = skill->dbs->db[idx].state[lv - 1]; if( (mhp = skill->dbs->db[idx].mhp[lv-1]) > 0 ) hp += (st->max_hp * mhp) / 100; if( hp_rate > 0 ) @@ -5926,7 +5938,7 @@ static int skill_castend_id(int tid, int64 tick, int id, intptr_t data) break; } } - if (skill->get_state(ud->skill_id) != ST_MOVE_ENABLE) + if (skill->get_state(ud->skill_id, ud->skill_lv) != ST_MOVE_ENABLE) unit->set_walkdelay(src, tick, battle_config.default_walk_delay+skill->get_walkdelay(ud->skill_id, ud->skill_lv), 1); if(battle_config.skill_log && battle_config.skill_log&src->type) @@ -15538,7 +15550,7 @@ static struct skill_condition skill_get_requirement(struct map_session_data *sd, req.spiritball = skill->dbs->db[idx].spiritball[skill_lv-1]; - req.state = skill->dbs->db[idx].state; + req.state = skill->dbs->db[idx].state[skill_lv - 1]; req.mhp = skill->dbs->db[idx].mhp[skill_lv-1]; @@ -22120,6 +22132,71 @@ static void skill_validate_ammo_amount(struct config_setting_t *conf, struct s_s } } +/** + * Validates a single required state when reading the skill DB. + * + * @param state The required state to validate. + * @return A number greater than or equal to 0 if the passed required state is valid, otherwise -1. + * + **/ +static int skill_validate_state_sub(const char *state) +{ + nullpo_retr(-1, state); + + int ret_val = ST_NONE; + + if (strcmpi(state, "Hiding") == 0) + ret_val = ST_HIDING; + else if (strcmpi(state, "Cloaking") == 0) + ret_val = ST_CLOAKING; + else if (strcmpi(state, "Hidden") == 0) + ret_val = ST_HIDDEN; + else if (strcmpi(state, "Riding") == 0) + ret_val = ST_RIDING; + else if (strcmpi(state, "Falcon") == 0) + ret_val = ST_FALCON; + else if (strcmpi(state, "Cart") == 0) + ret_val = ST_CART; + else if (strcmpi(state, "Shield") == 0) + ret_val = ST_SHIELD; + else if (strcmpi(state, "Sight") == 0) + ret_val = ST_SIGHT; + else if (strcmpi(state, "ExplosionSpirits") == 0) + ret_val = ST_EXPLOSIONSPIRITS; + else if (strcmpi(state, "CartBoost") == 0) + ret_val = ST_CARTBOOST; + else if (strcmpi(state, "NotOverWeight") == 0) + ret_val = ST_RECOV_WEIGHT_RATE; + else if (strcmpi(state, "Moveable") == 0) + ret_val = ST_MOVE_ENABLE; + else if (strcmpi(state, "InWater") == 0) + ret_val = ST_WATER; + else if (strcmpi(state, "Dragon") == 0) + ret_val = ST_RIDINGDRAGON; + else if (strcmpi(state, "Warg") == 0) + ret_val = ST_WUG; + else if (strcmpi(state, "RidingWarg") == 0) + ret_val = ST_RIDINGWUG; + else if (strcmpi(state, "MadoGear") == 0) + ret_val = ST_MADO; + else if (strcmpi(state, "ElementalSpirit") == 0) + ret_val = ST_ELEMENTALSPIRIT; + else if (strcmpi(state, "PoisonWeapon") == 0) + ret_val = ST_POISONINGWEAPON; + else if (strcmpi(state, "RollingCutter") == 0) + ret_val = ST_ROLLINGCUTTER; + else if (strcmpi(state, "MH_Fighting") == 0) + ret_val = ST_MH_FIGHTING; + else if (strcmpi(state, "MH_Grappling") == 0) + ret_val = ST_MH_GRAPPLING; + else if (strcmpi(state, "Peco") == 0) + ret_val = ST_PECO; + else if (strcmpi(state, "None") != 0) + ret_val = -1; + + return ret_val; +} + /** * Validates a skill's required states when reading the skill DB. * @@ -22132,58 +22209,38 @@ static void skill_validate_state(struct config_setting_t *conf, struct s_skill_d nullpo_retv(conf); nullpo_retv(sk); - sk->state = ST_NONE; + skill->level_set_value(sk->state, ST_NONE); + + struct config_setting_t *t = libconfig->setting_get_member(conf, "State"); + + if (t != NULL && config_setting_is_group(t)) { + for (int i = 0; i < MAX_SKILL_LEVEL; i++) { + char lv[6]; // Big enough to contain "Lv999" in case of custom MAX_SKILL_LEVEL. + safesnprintf(lv, sizeof(lv), "Lv%d", i + 1); + const char *state; + + if (libconfig->setting_lookup_string(t, lv, &state) == CONFIG_TRUE) { + int sta = skill->validate_state_sub(state); + + if (sta > ST_NONE) + sk->state[i] = sta; + else if (sta == -1) + ShowWarning("%s: Invalid required state %s specified in level %d for skill ID %d in %s! Defaulting to None...\n", + __func__, state, i + 1, sk->nameid, conf->file); + } + } + + return; + } const char *state; if (libconfig->setting_lookup_string(conf, "State", &state) == CONFIG_TRUE) { - if (strcmpi(state, "Hiding") == 0) - sk->state = ST_HIDING; - else if (strcmpi(state, "Cloaking") == 0) - sk->state = ST_CLOAKING; - else if (strcmpi(state, "Hidden") == 0) - sk->state = ST_HIDDEN; - else if (strcmpi(state, "Riding") == 0) - sk->state = ST_RIDING; - else if (strcmpi(state, "Falcon") == 0) - sk->state = ST_FALCON; - else if (strcmpi(state, "Cart") == 0) - sk->state = ST_CART; - else if (strcmpi(state, "Shield") == 0) - sk->state = ST_SHIELD; - else if (strcmpi(state, "Sight") == 0) - sk->state = ST_SIGHT; - else if (strcmpi(state, "ExplosionSpirits") == 0) - sk->state = ST_EXPLOSIONSPIRITS; - else if (strcmpi(state, "CartBoost") == 0) - sk->state = ST_CARTBOOST; - else if (strcmpi(state, "NotOverWeight") == 0) - sk->state = ST_RECOV_WEIGHT_RATE; - else if (strcmpi(state, "Moveable") == 0) - sk->state = ST_MOVE_ENABLE; - else if (strcmpi(state, "InWater") == 0) - sk->state = ST_WATER; - else if (strcmpi(state, "Dragon") == 0) - sk->state = ST_RIDINGDRAGON; - else if (strcmpi(state, "Warg") == 0) - sk->state = ST_WUG; - else if (strcmpi(state, "RidingWarg") == 0) - sk->state = ST_RIDINGWUG; - else if (strcmpi(state, "MadoGear") == 0) - sk->state = ST_MADO; - else if (strcmpi(state, "ElementalSpirit") == 0) - sk->state = ST_ELEMENTALSPIRIT; - else if (strcmpi(state, "PoisonWeapon") == 0) - sk->state = ST_POISONINGWEAPON; - else if (strcmpi(state, "RollingCutter") == 0) - sk->state = ST_ROLLINGCUTTER; - else if (strcmpi(state, "MH_Fighting") == 0) - sk->state = ST_MH_FIGHTING; - else if (strcmpi(state, "MH_Grappling") == 0) - sk->state = ST_MH_GRAPPLING; - else if (strcmpi(state, "Peco") == 0) - sk->state = ST_PECO; - else if (strcmpi(state, "None") != 0) + int sta = skill->validate_state_sub(state); + + if (sta > ST_NONE) + skill->level_set_value(sk->state, sta); + else if (sta == -1) ShowWarning("%s: Invalid required state %s specified for skill ID %d in %s! Defaulting to None...\n", __func__, state, sk->nameid, conf->file); } @@ -23197,6 +23254,7 @@ void skill_defaults(void) skill->validate_ammotype_sub = skill_validate_ammotype_sub; skill->validate_ammotype = skill_validate_ammotype; skill->validate_ammo_amount = skill_validate_ammo_amount; + skill->validate_state_sub = skill_validate_state_sub; skill->validate_state = skill_validate_state; skill->validate_spirit_sphere_cost = skill_validate_spirit_sphere_cost; skill->validate_item_requirements = skill_validate_item_requirements; diff --git a/src/map/skill.h b/src/map/skill.h index a185535a1..daa03d24e 100644 --- a/src/map/skill.h +++ b/src/map/skill.h @@ -1771,7 +1771,11 @@ struct s_skill_db { int skill_type[MAX_SKILL_LEVEL]; int blewcount[MAX_SKILL_LEVEL]; int hp[MAX_SKILL_LEVEL],sp[MAX_SKILL_LEVEL],mhp[MAX_SKILL_LEVEL],hp_rate[MAX_SKILL_LEVEL],sp_rate[MAX_SKILL_LEVEL],zeny[MAX_SKILL_LEVEL]; - int weapon,ammo,ammo_qty[MAX_SKILL_LEVEL],state,spiritball[MAX_SKILL_LEVEL]; + int weapon; + int ammo; + int ammo_qty[MAX_SKILL_LEVEL]; + int state[MAX_SKILL_LEVEL]; + int spiritball[MAX_SKILL_LEVEL]; int itemid[MAX_SKILL_ITEM_REQUIRE],amount[MAX_SKILL_ITEM_REQUIRE]; int castnodex[MAX_SKILL_LEVEL], delaynodex[MAX_SKILL_LEVEL]; int unit_id[2]; @@ -1979,7 +1983,7 @@ struct skill_interface { int (*get_sp) (int skill_id, int skill_lv); int (*get_hp_rate) (int skill_id, int skill_lv); int (*get_sp_rate) (int skill_id, int skill_lv); - int (*get_state) (int skill_id); + int (*get_state) (int skill_id, int skill_lv); int (*get_spiritball) (int skill_id, int skill_lv); int (*get_itemid) (int skill_id, int item_idx); int (*get_itemqty) (int skill_id, int item_idx); @@ -2164,6 +2168,7 @@ struct skill_interface { int (*validate_ammotype_sub) (const char *type, bool on, struct s_skill_db *sk); void (*validate_ammotype) (struct config_setting_t *conf, struct s_skill_db *sk); void (*validate_ammo_amount) (struct config_setting_t *conf, struct s_skill_db *sk); + int (*validate_state_sub) (const char *state); void (*validate_state) (struct config_setting_t *conf, struct s_skill_db *sk); void (*validate_spirit_sphere_cost) (struct config_setting_t *conf, struct s_skill_db *sk); void (*validate_item_requirements) (struct config_setting_t *conf, struct s_skill_db *sk); diff --git a/src/map/unit.c b/src/map/unit.c index 223a4a82b..a6afb5ee7 100644 --- a/src/map/unit.c +++ b/src/map/unit.c @@ -1567,7 +1567,7 @@ static int unit_skilluse_id2(struct block_list *src, int target_id, uint16 skill //Check range when not using skill on yourself or is a combo-skill during attack //(these are supposed to always have the same range as your attack) if( src->id != target_id && (!temp || ud->attacktimer == INVALID_TIMER) ) { - if( skill->get_state(ud->skill_id) == ST_MOVE_ENABLE ) { + if (skill->get_state(ud->skill_id, ud->skill_lv) == ST_MOVE_ENABLE) { if( !unit->can_reach_bl(src, target, range + 1, 1, NULL, NULL) ) return 0; // Walk-path check failed. } else if( src->type == BL_MER && skill_id == MA_REMOVETRAP ) { @@ -1872,7 +1872,7 @@ static int unit_skilluse_pos2(struct block_list *src, short skill_x, short skill return 0; // Attacking will be handled by unit_walk_toxy_timer in this case } - if( skill->get_state(ud->skill_id) == ST_MOVE_ENABLE ) { + if (skill->get_state(ud->skill_id, ud->skill_lv) == ST_MOVE_ENABLE) { if( !unit->can_reach_bl(src, &bl, range + 1, 1, NULL, NULL) ) return 0; //Walk-path check failed. } else if( !battle->check_range(src, &bl, range) ) diff --git a/src/plugins/HPMHooking/HPMHooking.Defs.inc b/src/plugins/HPMHooking/HPMHooking.Defs.inc index 5ce0ba4e6..8a4aa7712 100644 --- a/src/plugins/HPMHooking/HPMHooking.Defs.inc +++ b/src/plugins/HPMHooking/HPMHooking.Defs.inc @@ -7258,8 +7258,8 @@ typedef int (*HPMHOOK_pre_skill_get_hp_rate) (int *skill_id, int *skill_lv); typedef int (*HPMHOOK_post_skill_get_hp_rate) (int retVal___, int skill_id, int skill_lv); typedef int (*HPMHOOK_pre_skill_get_sp_rate) (int *skill_id, int *skill_lv); typedef int (*HPMHOOK_post_skill_get_sp_rate) (int retVal___, int skill_id, int skill_lv); -typedef int (*HPMHOOK_pre_skill_get_state) (int *skill_id); -typedef int (*HPMHOOK_post_skill_get_state) (int retVal___, int skill_id); +typedef int (*HPMHOOK_pre_skill_get_state) (int *skill_id, int *skill_lv); +typedef int (*HPMHOOK_post_skill_get_state) (int retVal___, int skill_id, int skill_lv); typedef int (*HPMHOOK_pre_skill_get_spiritball) (int *skill_id, int *skill_lv); typedef int (*HPMHOOK_post_skill_get_spiritball) (int retVal___, int skill_id, int skill_lv); typedef int (*HPMHOOK_pre_skill_get_itemid) (int *skill_id, int *item_idx); diff --git a/src/plugins/HPMHooking/HPMHooking_map.Hooks.inc b/src/plugins/HPMHooking/HPMHooking_map.Hooks.inc index 357ba2421..46682e33c 100644 --- a/src/plugins/HPMHooking/HPMHooking_map.Hooks.inc +++ b/src/plugins/HPMHooking/HPMHooking_map.Hooks.inc @@ -77147,15 +77147,15 @@ int HP_skill_get_sp_rate(int skill_id, int skill_lv) { } return retVal___; } -int HP_skill_get_state(int skill_id) { +int HP_skill_get_state(int skill_id, int skill_lv) { int hIndex = 0; int retVal___ = 0; if (HPMHooks.count.HP_skill_get_state_pre > 0) { - int (*preHookFunc) (int *skill_id); + int (*preHookFunc) (int *skill_id, int *skill_lv); *HPMforce_return = false; for (hIndex = 0; hIndex < HPMHooks.count.HP_skill_get_state_pre; hIndex++) { preHookFunc = HPMHooks.list.HP_skill_get_state_pre[hIndex].func; - retVal___ = preHookFunc(&skill_id); + retVal___ = preHookFunc(&skill_id, &skill_lv); } if (*HPMforce_return) { *HPMforce_return = false; @@ -77163,13 +77163,13 @@ int HP_skill_get_state(int skill_id) { } } { - retVal___ = HPMHooks.source.skill.get_state(skill_id); + retVal___ = HPMHooks.source.skill.get_state(skill_id, skill_lv); } if (HPMHooks.count.HP_skill_get_state_post > 0) { - int (*postHookFunc) (int retVal___, int skill_id); + int (*postHookFunc) (int retVal___, int skill_id, int skill_lv); for (hIndex = 0; hIndex < HPMHooks.count.HP_skill_get_state_post; hIndex++) { postHookFunc = HPMHooks.list.HP_skill_get_state_post[hIndex].func; - retVal___ = postHookFunc(retVal___, skill_id); + retVal___ = postHookFunc(retVal___, skill_id, skill_lv); } } return retVal___; -- cgit v1.2.3-60-g2f50 From 887820f62769ffcc09de0053ba6a645b83cf74d9 Mon Sep 17 00:00:00 2001 From: Kenpachi Developer Date: Sun, 17 May 2020 04:56:18 +0200 Subject: Make Unit->Id can be grouped by levels --- src/map/map.c | 2 +- src/map/mob.c | 2 +- src/map/npc.c | 2 +- src/map/skill.c | 204 ++++++++++++++++++------ src/map/skill.h | 4 +- src/plugins/HPMHooking/HPMHooking.Defs.inc | 4 +- src/plugins/HPMHooking/HPMHooking_map.Hooks.inc | 12 +- 7 files changed, 171 insertions(+), 59 deletions(-) diff --git a/src/map/map.c b/src/map/map.c index 63d05c1c0..4477d109c 100644 --- a/src/map/map.c +++ b/src/map/map.c @@ -5265,7 +5265,7 @@ static bool map_zone_mf_cache(int m, char *flag, char *params) } } - if( modifier[0] == '\0' || !( skill_id = skill->name2id(skill_name) ) || !skill->get_unit_id( skill->name2id(skill_name), 0) || atoi(modifier) < 1 || atoi(modifier) > USHRT_MAX ) { + if (modifier[0] == '\0' || (skill_id = skill->name2id(skill_name)) == 0 || skill->get_unit_id(skill->name2id(skill_name), 1, 0) == 0 || atoi(modifier) < 1 || atoi(modifier) > USHRT_MAX) { ;/* we don't mind it, the server will take care of it next. */ } else { int idx = map->list[m].unit_count; diff --git a/src/map/mob.c b/src/map/mob.c index b6ba5e5a9..df6deb866 100644 --- a/src/map/mob.c +++ b/src/map/mob.c @@ -3871,7 +3871,7 @@ static int mob_clone_spawn(struct map_session_data *sd, int16 m, int16 x, int16 continue; /// Normal aggressive mob. Disable skills that cannot help fighting against players. (Those with flags UF_NOMOB and UF_NOPC are specific to always aid players!) [Skotlex] - if (flag == 0 && skill->get_unit_id(skill_id, 0) != 0 && + if (flag == 0 && skill->get_unit_id(skill_id, sd->status.skill[idx].lv, 0) != 0 && (skill->get_unit_flag(skill_id) & (UF_NOMOB | UF_NOPC)) > 0) continue; diff --git a/src/map/npc.c b/src/map/npc.c index 2f03623e4..5b009ab21 100644 --- a/src/map/npc.c +++ b/src/map/npc.c @@ -5104,7 +5104,7 @@ static const char *npc_parse_mapflag(const char *w1, const char *w2, const char else if (modifier[0] == '\0') { ShowWarning("npc_parse_mapflag: Missing 5th param for 'adjust_unit_duration' flag! removing flag from %s in file '%s', line '%d'.\n", map->list[m].name, filepath, strline(buffer,start-buffer)); if (retval) *retval = EXIT_FAILURE; - } else if( !( skill_id = skill->name2id(skill_name) ) || !skill->get_unit_id( skill->name2id(skill_name), 0) ) { + } else if ((skill_id = skill->name2id(skill_name)) == 0 || skill->get_unit_id(skill->name2id(skill_name), 1, 0) == 0) { ShowWarning("npc_parse_mapflag: Unknown skill (%s) for 'adjust_unit_duration' flag! removing flag from %s in file '%s', line '%d'.\n",skill_name, map->list[m].name, filepath, strline(buffer,start-buffer)); if (retval) *retval = EXIT_FAILURE; } else if ( atoi(modifier) < 1 || atoi(modifier) > USHRT_MAX ) { diff --git a/src/map/skill.c b/src/map/skill.c index 306237565..0761592ca 100644 --- a/src/map/skill.c +++ b/src/map/skill.c @@ -682,15 +682,28 @@ static int skill_get_type(int skill_id, int skill_lv) return skill->dbs->db[idx].skill_type[skill_get_lvl_idx(skill_lv)]; } -static int skill_get_unit_id(int skill_id, int flag) +/** + * Gets a skill's unit ID by its ID and level. + * + * @param skill_id The skill's ID. + * @param skill_lv The skill's level. + * @param flag + * @return The skill's unit ID corresponding to the passed level. Defaults to 0 in case of error. + * + **/ +static int skill_get_unit_id(int skill_id, int skill_lv, int flag) { - int idx; if (skill_id == 0) return 0; - idx = skill->get_index(skill_id); + + Assert_ret(skill_lv > 0); + Assert_ret(flag >= 0 && flag < ARRAYLENGTH(skill->dbs->db[0].unit_id[0])); + + int idx = skill->get_index(skill_id); + Assert_ret(idx != 0); - Assert_ret(flag >= 0 && flag < ARRAYLENGTH(skill->dbs->db[0].unit_id)); - return skill->dbs->db[idx].unit_id[flag]; + + return skill->dbs->db[idx].unit_id[skill_get_lvl_idx(skill_lv)][flag]; } static int skill_get_unit_interval(int skill_id) @@ -6220,7 +6233,7 @@ static int skill_castend_nodamage_id(struct block_list *src, struct block_list * if (skill->castend_nodamage_id_undead_unknown(src, bl, &skill_id, &skill_lv, &tick, &flag)) { //Skill is actually ground placed. - if (src == bl && skill->get_unit_id(skill_id,0)) + if (src == bl && skill->get_unit_id(skill_id, skill_lv, 0) != 0) return skill->castend_pos2(src,bl->x,bl->y,skill_id,skill_lv,tick,0); } break; @@ -12089,7 +12102,7 @@ static bool skill_dance_switch(struct skill_unit *su, int flag) // replace group->skill_id = skill_id; group->skill_lv = 1; - group->unit_id = skill->get_unit_id(skill_id,0); + group->unit_id = skill->get_unit_id(skill_id, 1, 0); group->target_flag = skill->get_unit_target(skill_id); group->bl_flag = skill->get_unit_bl_target(skill_id); group->interval = skill->get_unit_interval(skill_id); @@ -12490,7 +12503,7 @@ static struct skill_unit_group *skill_unitsetting(struct block_list *src, uint16 } nullpo_retr(NULL, layout); - nullpo_retr(NULL, group=skill->init_unitgroup(src,layout->count,skill_id,skill_lv,skill->get_unit_id(skill_id,flag&1)+subunt, limit, interval)); + nullpo_retr(NULL, group = skill->init_unitgroup(src, layout->count, skill_id, skill_lv, skill->get_unit_id(skill_id, skill_lv, flag & 1) + subunt, limit, interval)); group->val1=val1; group->val2=val2; group->val3=val3; @@ -17768,7 +17781,7 @@ static int skill_unit_timer_sub(union DBKey key, struct DBData *data, va_list ap case UNT_WARP_ACTIVE: // warp portal opens (morph to a UNT_WARP_WAITING cell) - group->unit_id = skill->get_unit_id(group->skill_id, 1); // UNT_WARP_WAITING + group->unit_id = skill->get_unit_id(group->skill_id, group->skill_lv, 1); // UNT_WARP_WAITING clif->changelook(&su->bl, LOOK_BASE, group->unit_id); // restart timers group->limit = skill->get_time(group->skill_id,group->skill_lv); @@ -22402,68 +22415,167 @@ static int skill_validate_unit_id_sub(int unit_id) } /** - * Validates a skill's unit IDs when reading the skill DB. + * Validates a skill's unit IDs if specified as single value when reading the skill DB. * - * @param conf The libconfig settings block which contains the skill's data. + * @param conf The libconfig settings block which contains the skill's unit ID data. * @param sk The s_skill_db struct where the unit IDs should be set it. + * @param index The array index to use. (-1 for whole array.) + * @param unit_id The unit ID to validate. * **/ -static void skill_validate_unit_id(struct config_setting_t *conf, struct s_skill_db *sk) +static void skill_validate_unit_id_value(struct config_setting_t *conf, struct s_skill_db *sk, int index, int unit_id) { nullpo_retv(conf); nullpo_retv(sk); - sk->unit_id[0] = 0; - sk->unit_id[1] = 0; + if (skill->validate_unit_id_sub(unit_id) == -1) { + char level_string[14]; // Big enough to contain "in level 999 " in case of custom MAX_SKILL_LEVEL. - struct config_setting_t *t = libconfig->setting_get_member(conf, "Id"); + if (index == -1) + *level_string = '\0'; + else + safesnprintf(level_string, sizeof(level_string), "in level %d ", index + 1); - if (t != NULL && config_setting_is_array(t)) { - if (libconfig->setting_length(t) == 0) { - ShowWarning("%s: No unit ID(s) specified for skill ID %d in %s! Defaulting to 0...\n", - __func__, sk->nameid, conf->file); - return; - } + ShowWarning("%s: Invalid unit ID %d specified %sfor skill ID %d in %s! Must be greater than or equal to 0. Defaulting to 0...\n", + __func__, unit_id, level_string, sk->nameid, conf->file); + + return; + } + + if (index == -1) { + for (int i = 0; i < MAX_SKILL_LEVEL; i++) + sk->unit_id[i][0] = unit_id; + } else { + sk->unit_id[index][0] = unit_id; + } +} + +/** + * Validates a skill's unit IDs if specified as array when reading the skill DB. + * + * @param conf The libconfig settings block which contains the skill's unit ID data. + * @param sk The s_skill_db struct where the unit IDs should be set it. + * @param index The array index to use. (-1 for whole array.) + * + **/ +static void skill_validate_unit_id_array(struct config_setting_t *conf, struct s_skill_db *sk, int index) +{ + nullpo_retv(conf); + nullpo_retv(sk); + + char level_string[14]; // Big enough to contain "in level 999 " in case of custom MAX_SKILL_LEVEL. + + if (index == -1) + *level_string = '\0'; + else + safesnprintf(level_string, sizeof(level_string), "in level %d ", index + 1); + + if (libconfig->setting_length(conf) == 0) { + ShowWarning("%s: No unit ID(s) specified %sfor skill ID %d in %s! Defaulting to 0...\n", + __func__, level_string, sk->nameid, conf->file); + return; + } + + if (libconfig->setting_length(conf) > 2) + ShowWarning("%s: Specified more than two unit IDs %sfor skill ID %d in %s! Reading only the first two...\n", + __func__, level_string, sk->nameid, conf->file); + + int unit_id1 = libconfig->setting_get_int_elem(conf, 0); - if (libconfig->setting_length(t) > 2) - ShowWarning("%s: Specified more than two unit IDs for skill ID %d in %s! Reading only the first two...\n", - __func__, sk->nameid, conf->file); + if (skill->validate_unit_id_sub(unit_id1) == -1) { + ShowWarning("%s: Invalid unit ID %d specified %sfor skill ID %d in %s! Must be greater than or equal to 0. Defaulting to 0...\n", + __func__, unit_id1, level_string, sk->nameid, conf->file); + unit_id1 = 0; + } + + int unit_id2 = 0; - int unit_id1 = libconfig->setting_get_int_elem(t, 0); + if (libconfig->setting_length(conf) > 1) { + unit_id2 = libconfig->setting_get_int_elem(conf, 1); - if (skill->validate_unit_id_sub(unit_id1) == -1) { - ShowWarning("%s: Unknown unit ID %d specified for skill ID %d in %s! Defaulting to 0...\n", - __func__, unit_id1, sk->nameid, conf->file); - unit_id1 = 0; + if (skill->validate_unit_id_sub(unit_id2) == -1) { + ShowWarning("%s: Invalid unit ID %d specified %sfor skill ID %d in %s! Must be greater than or equal to 0. Defaulting to 0...\n", + __func__, unit_id2, level_string, sk->nameid, conf->file); + unit_id2 = 0; } + } - int unit_id2 = 0; + if (unit_id1 == 0 && unit_id2 == 0) + return; - if (libconfig->setting_length(t) > 1) { - unit_id2 = libconfig->setting_get_int_elem(t, 1); + if (index == -1) { + for (int i = 0; i < MAX_SKILL_LEVEL; i++) { + sk->unit_id[i][0] = unit_id1; + sk->unit_id[i][1] = unit_id2; + } + } else { + sk->unit_id[index][0] = unit_id1; + sk->unit_id[index][1] = unit_id2; + } +} - if (skill->validate_unit_id_sub(unit_id2) == -1) { - ShowWarning("%s: Unknown unit ID %d specified for skill ID %d in %s! Defaulting to 0...\n", - __func__, unit_id2, sk->nameid, conf->file); - unit_id2 = 0; - } +/** + * Validates a skill's unit IDs if specified as group when reading the skill DB. + * + * @param conf The libconfig settings block which contains the skill's unit ID data. + * @param sk The s_skill_db struct where the unit IDs should be set it. + * + **/ +static void skill_validate_unit_id_group(struct config_setting_t *conf, struct s_skill_db *sk) +{ + nullpo_retv(conf); + nullpo_retv(sk); + + for (int i = 0; i < MAX_SKILL_LEVEL; i++) { + struct config_setting_t *t; + char lv[6]; // Big enough to contain "Lv999" in case of custom MAX_SKILL_LEVEL. + safesnprintf(lv, sizeof(lv), "Lv%d", i + 1); + + if ((t = libconfig->setting_get_member(conf, lv)) != NULL && config_setting_is_array(t)) { + skill_validate_unit_id_array(t, sk, i); + continue; } - sk->unit_id[0] = unit_id1; - sk->unit_id[1] = unit_id2; + int unit_id; + + if (libconfig->setting_lookup_int(conf, lv, &unit_id) == CONFIG_TRUE) + skill_validate_unit_id_value(conf, sk, i, unit_id); + } +} +/** + * Validates a skill's unit IDs when reading the skill DB. + * + * @param conf The libconfig settings block which contains the skill's data. + * @param sk The s_skill_db struct where the unit IDs should be set it. + * + **/ +static void skill_validate_unit_id(struct config_setting_t *conf, struct s_skill_db *sk) +{ + nullpo_retv(conf); + nullpo_retv(sk); + + for (int i = 0; i < MAX_SKILL_LEVEL; i++) { + sk->unit_id[i][0] = 0; + sk->unit_id[i][1] = 0; + } + + struct config_setting_t *t = libconfig->setting_get_member(conf, "Id"); + + if (t != NULL && config_setting_is_group(t)) { + skill_validate_unit_id_group(t, sk); + return; + } + + if (t != NULL && config_setting_is_array(t)) { + skill_validate_unit_id_array(t, sk, -1); return; } int unit_id; - if (libconfig->setting_lookup_int(conf, "Id", &unit_id) == CONFIG_TRUE) { - if (skill->validate_unit_id_sub(unit_id) == -1) - ShowWarning("%s: Unknown unit ID %d specified for skill ID %d in %s! Defaulting to 0...\n", - __func__, unit_id, sk->nameid, conf->file); - else - sk->unit_id[0] = unit_id; - } + if (libconfig->setting_lookup_int(conf, "Id", &unit_id) == CONFIG_TRUE) + skill_validate_unit_id_value(conf, sk, -1, unit_id); } /** diff --git a/src/map/skill.h b/src/map/skill.h index daa03d24e..b5d38e6e2 100644 --- a/src/map/skill.h +++ b/src/map/skill.h @@ -1778,7 +1778,7 @@ struct s_skill_db { int spiritball[MAX_SKILL_LEVEL]; int itemid[MAX_SKILL_ITEM_REQUIRE],amount[MAX_SKILL_ITEM_REQUIRE]; int castnodex[MAX_SKILL_LEVEL], delaynodex[MAX_SKILL_LEVEL]; - int unit_id[2]; + int unit_id[MAX_SKILL_LEVEL][2]; int unit_layout_type[MAX_SKILL_LEVEL]; int unit_range[MAX_SKILL_LEVEL]; int unit_interval; @@ -2000,7 +2000,7 @@ struct skill_interface { int (*get_weapontype) (int skill_id); int (*get_ammotype) (int skill_id); int (*get_ammo_qty) (int skill_id, int skill_lv); - int (*get_unit_id) (int skill_id, int flag); + int (*get_unit_id) (int skill_id, int skill_lv, int flag); int (*get_inf2) (int skill_id); int (*get_castcancel) (int skill_id, int skill_lv); int (*get_maxcount) (int skill_id, int skill_lv); diff --git a/src/plugins/HPMHooking/HPMHooking.Defs.inc b/src/plugins/HPMHooking/HPMHooking.Defs.inc index 8a4aa7712..494634bc0 100644 --- a/src/plugins/HPMHooking/HPMHooking.Defs.inc +++ b/src/plugins/HPMHooking/HPMHooking.Defs.inc @@ -7292,8 +7292,8 @@ typedef int (*HPMHOOK_pre_skill_get_ammotype) (int *skill_id); typedef int (*HPMHOOK_post_skill_get_ammotype) (int retVal___, int skill_id); typedef int (*HPMHOOK_pre_skill_get_ammo_qty) (int *skill_id, int *skill_lv); typedef int (*HPMHOOK_post_skill_get_ammo_qty) (int retVal___, int skill_id, int skill_lv); -typedef int (*HPMHOOK_pre_skill_get_unit_id) (int *skill_id, int *flag); -typedef int (*HPMHOOK_post_skill_get_unit_id) (int retVal___, int skill_id, int flag); +typedef int (*HPMHOOK_pre_skill_get_unit_id) (int *skill_id, int *skill_lv, int *flag); +typedef int (*HPMHOOK_post_skill_get_unit_id) (int retVal___, int skill_id, int skill_lv, int flag); typedef int (*HPMHOOK_pre_skill_get_inf2) (int *skill_id); typedef int (*HPMHOOK_post_skill_get_inf2) (int retVal___, int skill_id); typedef int (*HPMHOOK_pre_skill_get_castcancel) (int *skill_id, int *skill_lv); diff --git a/src/plugins/HPMHooking/HPMHooking_map.Hooks.inc b/src/plugins/HPMHooking/HPMHooking_map.Hooks.inc index 46682e33c..d8685e1c5 100644 --- a/src/plugins/HPMHooking/HPMHooking_map.Hooks.inc +++ b/src/plugins/HPMHooking/HPMHooking_map.Hooks.inc @@ -77606,15 +77606,15 @@ int HP_skill_get_ammo_qty(int skill_id, int skill_lv) { } return retVal___; } -int HP_skill_get_unit_id(int skill_id, int flag) { +int HP_skill_get_unit_id(int skill_id, int skill_lv, int flag) { int hIndex = 0; int retVal___ = 0; if (HPMHooks.count.HP_skill_get_unit_id_pre > 0) { - int (*preHookFunc) (int *skill_id, int *flag); + int (*preHookFunc) (int *skill_id, int *skill_lv, int *flag); *HPMforce_return = false; for (hIndex = 0; hIndex < HPMHooks.count.HP_skill_get_unit_id_pre; hIndex++) { preHookFunc = HPMHooks.list.HP_skill_get_unit_id_pre[hIndex].func; - retVal___ = preHookFunc(&skill_id, &flag); + retVal___ = preHookFunc(&skill_id, &skill_lv, &flag); } if (*HPMforce_return) { *HPMforce_return = false; @@ -77622,13 +77622,13 @@ int HP_skill_get_unit_id(int skill_id, int flag) { } } { - retVal___ = HPMHooks.source.skill.get_unit_id(skill_id, flag); + retVal___ = HPMHooks.source.skill.get_unit_id(skill_id, skill_lv, flag); } if (HPMHooks.count.HP_skill_get_unit_id_post > 0) { - int (*postHookFunc) (int retVal___, int skill_id, int flag); + int (*postHookFunc) (int retVal___, int skill_id, int skill_lv, int flag); for (hIndex = 0; hIndex < HPMHooks.count.HP_skill_get_unit_id_post; hIndex++) { postHookFunc = HPMHooks.list.HP_skill_get_unit_id_post[hIndex].func; - retVal___ = postHookFunc(retVal___, skill_id, flag); + retVal___ = postHookFunc(retVal___, skill_id, skill_lv, flag); } } return retVal___; -- cgit v1.2.3-60-g2f50 From aac63c5714ad207c53f8f69837cfcb05ffdb3137 Mon Sep 17 00:00:00 2001 From: Kenpachi Developer Date: Sun, 17 May 2020 06:33:07 +0200 Subject: Make Unit->Interval can be grouped by levels --- src/map/skill.c | 50 ++++++++++++++++++++----- src/map/skill.h | 4 +- src/plugins/HPMHooking/HPMHooking.Defs.inc | 4 +- src/plugins/HPMHooking/HPMHooking_map.Hooks.inc | 12 +++--- 4 files changed, 51 insertions(+), 19 deletions(-) diff --git a/src/map/skill.c b/src/map/skill.c index 0761592ca..d57931b54 100644 --- a/src/map/skill.c +++ b/src/map/skill.c @@ -706,14 +706,26 @@ static int skill_get_unit_id(int skill_id, int skill_lv, int flag) return skill->dbs->db[idx].unit_id[skill_get_lvl_idx(skill_lv)][flag]; } -static int skill_get_unit_interval(int skill_id) +/** + * Gets a skill's unit interval by its ID and level. + * + * @param skill_id The skill's ID. + * @param skill_lv The skill's level. + * @return The skill's unit interval corresponding to the passed level. Defaults to 0 in case of error. + * + **/ +static int skill_get_unit_interval(int skill_id, int skill_lv) { - int idx; if (skill_id == 0) return 0; - idx = skill->get_index(skill_id); + + Assert_ret(skill_lv > 0); + + int idx = skill->get_index(skill_id); + Assert_ret(idx != 0); - return skill->dbs->db[idx].unit_interval; + + return skill->dbs->db[idx].unit_interval[skill_get_lvl_idx(skill_lv)]; } static int skill_get_unit_range(int skill_id, int skill_lv) @@ -11855,7 +11867,7 @@ static int skill_castend_pos2(struct block_list *src, int x, int y, uint16 skill skill->unitsetting(src, skill_id, skill_lv, x, y, 0); // Set bomb on current Position clif->skill_nodamage(src, src, skill_id, skill_lv, 1); if( skill->blown(src, src, 3 * skill_lv, unit->getdir(src), 0) && sc) { - sc_start(src, src, SC__FEINTBOMB_MASTER, 100, 0, skill->get_unit_interval(SC_FEINTBOMB)); + sc_start(src, src, SC__FEINTBOMB_MASTER, 100, 0, skill->get_unit_interval(SC_FEINTBOMB, skill_lv)); } break; @@ -12105,7 +12117,7 @@ static bool skill_dance_switch(struct skill_unit *su, int flag) group->unit_id = skill->get_unit_id(skill_id, 1, 0); group->target_flag = skill->get_unit_target(skill_id); group->bl_flag = skill->get_unit_bl_target(skill_id); - group->interval = skill->get_unit_interval(skill_id); + group->interval = skill->get_unit_interval(skill_id, 1); } else { //Restore group->skill_id = backup.skill_id; @@ -12138,7 +12150,7 @@ static struct skill_unit_group *skill_unitsetting(struct block_list *src, uint16 limit = skill->get_time(skill_id,skill_lv); range = skill->get_unit_range(skill_id,skill_lv); - interval = skill->get_unit_interval(skill_id); + interval = skill->get_unit_interval(skill_id, skill_lv); target = skill->get_unit_target(skill_id); unit_flag = skill->get_unit_flag(skill_id); layout = skill->get_unit_layout(skill_id,skill_lv,src,x,y); @@ -22680,13 +22692,33 @@ static void skill_validate_unit_interval(struct config_setting_t *conf, struct s nullpo_retv(conf); nullpo_retv(sk); - sk->unit_interval = 0; + skill->level_set_value(sk->unit_interval, 0); + + struct config_setting_t *t = libconfig->setting_get_member(conf, "Interval"); + + if (t != NULL && config_setting_is_group(t)) { + for (int i = 0; i < MAX_SKILL_LEVEL; i++) { + char lv[6]; // Big enough to contain "Lv999" in case of custom MAX_SKILL_LEVEL. + safesnprintf(lv, sizeof(lv), "Lv%d", i + 1); + int unit_interval; + + if (libconfig->setting_lookup_int(t, lv, &unit_interval) == CONFIG_TRUE) { + if (unit_interval >= INFINITE_DURATION) + sk->unit_interval[i] = unit_interval; + else + ShowWarning("%s: Invalid unit interval %d specified in level %d for skill ID %d in %s! Must be greater than or equal to %d. Defaulting to 0...\n", + __func__, unit_interval, i + 1, sk->nameid, conf->file, INFINITE_DURATION); + } + } + + return; + } int unit_interval; if (libconfig->setting_lookup_int(conf, "Interval", &unit_interval) == CONFIG_TRUE) { if (unit_interval >= INFINITE_DURATION) - sk->unit_interval = unit_interval; + skill->level_set_value(sk->unit_interval, unit_interval); else ShowWarning("%s: Invalid unit interval %d specified for skill ID %d in %s! Must be greater than or equal to %d. Defaulting to 0...\n", __func__, unit_interval, sk->nameid, conf->file, INFINITE_DURATION); diff --git a/src/map/skill.h b/src/map/skill.h index b5d38e6e2..c32a8b639 100644 --- a/src/map/skill.h +++ b/src/map/skill.h @@ -1781,7 +1781,7 @@ struct s_skill_db { int unit_id[MAX_SKILL_LEVEL][2]; int unit_layout_type[MAX_SKILL_LEVEL]; int unit_range[MAX_SKILL_LEVEL]; - int unit_interval; + int unit_interval[MAX_SKILL_LEVEL]; int unit_target; int unit_flag; }; @@ -2007,7 +2007,7 @@ struct skill_interface { int (*get_blewcount) (int skill_id, int skill_lv); int (*get_unit_flag) (int skill_id); int (*get_unit_target) (int skill_id); - int (*get_unit_interval) (int skill_id); + int (*get_unit_interval) (int skill_id, int skill_lv); int (*get_unit_bl_target) (int skill_id); int (*get_unit_layout_type) (int skill_id, int skill_lv); int (*get_unit_range) (int skill_id, int skill_lv); diff --git a/src/plugins/HPMHooking/HPMHooking.Defs.inc b/src/plugins/HPMHooking/HPMHooking.Defs.inc index 494634bc0..e75dbaa97 100644 --- a/src/plugins/HPMHooking/HPMHooking.Defs.inc +++ b/src/plugins/HPMHooking/HPMHooking.Defs.inc @@ -7306,8 +7306,8 @@ typedef int (*HPMHOOK_pre_skill_get_unit_flag) (int *skill_id); typedef int (*HPMHOOK_post_skill_get_unit_flag) (int retVal___, int skill_id); typedef int (*HPMHOOK_pre_skill_get_unit_target) (int *skill_id); typedef int (*HPMHOOK_post_skill_get_unit_target) (int retVal___, int skill_id); -typedef int (*HPMHOOK_pre_skill_get_unit_interval) (int *skill_id); -typedef int (*HPMHOOK_post_skill_get_unit_interval) (int retVal___, int skill_id); +typedef int (*HPMHOOK_pre_skill_get_unit_interval) (int *skill_id, int *skill_lv); +typedef int (*HPMHOOK_post_skill_get_unit_interval) (int retVal___, int skill_id, int skill_lv); typedef int (*HPMHOOK_pre_skill_get_unit_bl_target) (int *skill_id); typedef int (*HPMHOOK_post_skill_get_unit_bl_target) (int retVal___, int skill_id); typedef int (*HPMHOOK_pre_skill_get_unit_layout_type) (int *skill_id, int *skill_lv); diff --git a/src/plugins/HPMHooking/HPMHooking_map.Hooks.inc b/src/plugins/HPMHooking/HPMHooking_map.Hooks.inc index d8685e1c5..80c309a8c 100644 --- a/src/plugins/HPMHooking/HPMHooking_map.Hooks.inc +++ b/src/plugins/HPMHooking/HPMHooking_map.Hooks.inc @@ -77795,15 +77795,15 @@ int HP_skill_get_unit_target(int skill_id) { } return retVal___; } -int HP_skill_get_unit_interval(int skill_id) { +int HP_skill_get_unit_interval(int skill_id, int skill_lv) { int hIndex = 0; int retVal___ = 0; if (HPMHooks.count.HP_skill_get_unit_interval_pre > 0) { - int (*preHookFunc) (int *skill_id); + int (*preHookFunc) (int *skill_id, int *skill_lv); *HPMforce_return = false; for (hIndex = 0; hIndex < HPMHooks.count.HP_skill_get_unit_interval_pre; hIndex++) { preHookFunc = HPMHooks.list.HP_skill_get_unit_interval_pre[hIndex].func; - retVal___ = preHookFunc(&skill_id); + retVal___ = preHookFunc(&skill_id, &skill_lv); } if (*HPMforce_return) { *HPMforce_return = false; @@ -77811,13 +77811,13 @@ int HP_skill_get_unit_interval(int skill_id) { } } { - retVal___ = HPMHooks.source.skill.get_unit_interval(skill_id); + retVal___ = HPMHooks.source.skill.get_unit_interval(skill_id, skill_lv); } if (HPMHooks.count.HP_skill_get_unit_interval_post > 0) { - int (*postHookFunc) (int retVal___, int skill_id); + int (*postHookFunc) (int retVal___, int skill_id, int skill_lv); for (hIndex = 0; hIndex < HPMHooks.count.HP_skill_get_unit_interval_post; hIndex++) { postHookFunc = HPMHooks.list.HP_skill_get_unit_interval_post[hIndex].func; - retVal___ = postHookFunc(retVal___, skill_id); + retVal___ = postHookFunc(retVal___, skill_id, skill_lv); } } return retVal___; -- cgit v1.2.3-60-g2f50 From 451c0804fb72d5c4131c191f2f98dd9d7d9efbd7 Mon Sep 17 00:00:00 2001 From: Kenpachi Developer Date: Sun, 17 May 2020 07:05:01 +0200 Subject: Make Unit->Target can be grouped by levels --- src/map/mob.c | 2 +- src/map/skill.c | 192 ++++++++++++++++-------- src/map/skill.h | 7 +- src/plugins/HPMHooking/HPMHooking.Defs.inc | 8 +- src/plugins/HPMHooking/HPMHooking_map.Hooks.inc | 24 +-- 5 files changed, 152 insertions(+), 81 deletions(-) diff --git a/src/map/mob.c b/src/map/mob.c index df6deb866..056a1a8b1 100644 --- a/src/map/mob.c +++ b/src/map/mob.c @@ -3904,7 +3904,7 @@ static int mob_clone_spawn(struct map_session_data *sd, int16 m, int16 x, int16 mob_skills[i].state = MSS_IDLE; mob_skills[i].target = MST_AROUND2; mob_skills[i].delay = 60000; - } else if (skill->get_unit_target(skill_id) == BCT_ENEMY) { /// Target Enemy. + } else if (skill->get_unit_target(skill_id, sd->status.skill[idx].lv) == BCT_ENEMY) { /// Target Enemy. mob_skills[i].state = MSS_ANYTARGET; mob_skills[i].target = MST_TARGET; mob_skills[i].cond1 = MSC_ALWAYS; diff --git a/src/map/skill.c b/src/map/skill.c index d57931b54..9b9c17b21 100644 --- a/src/map/skill.c +++ b/src/map/skill.c @@ -743,24 +743,48 @@ static int skill_get_unit_range(int skill_id, int skill_lv) return skill->dbs->db[idx].unit_range[skill_get_lvl_idx(skill_lv)]; } -static int skill_get_unit_target(int skill_id) +/** + * Gets a skill's unit target by its ID and level. + * + * @param skill_id The skill's ID. + * @param skill_lv The skill's level. + * @return The skill's unit target corresponding to the passed level. Defaults to BCT_NOONE (0) in case of error. + * + **/ +static int skill_get_unit_target(int skill_id, int skill_lv) { - int idx; if (skill_id == 0) return BCT_NOONE; - idx = skill->get_index(skill_id); + + Assert_retr(BCT_NOONE, skill_lv > 0); + + int idx = skill->get_index(skill_id); + Assert_retr(BCT_NOONE, idx != 0); - return skill->dbs->db[idx].unit_target & BCT_ALL; + + return (skill->dbs->db[idx].unit_target[skill_get_lvl_idx(skill_lv)] & BCT_ALL); } -static int skill_get_unit_bl_target(int skill_id) +/** + * Gets a skill's unit target as bl type by its ID and level. + * + * @param skill_id The skill's ID. + * @param skill_lv The skill's level. + * @return The skill's unit target as bl type corresponding to the passed level. Defaults to BL_NUL (0) in case of error. + * + **/ +static int skill_get_unit_bl_target(int skill_id, int skill_lv) { - int idx; if (skill_id == 0) return BL_NUL; - idx = skill->get_index(skill_id); - Assert_retr(BL_NUL, idx != 0); - return skill->dbs->db[idx].unit_target & BL_ALL; + + Assert_retr(BCT_NOONE, skill_lv > 0); + + int idx = skill->get_index(skill_id); + + Assert_retr(BCT_NOONE, idx != 0); + + return (skill->dbs->db[idx].unit_target[skill_get_lvl_idx(skill_lv)] & BL_ALL); } static int skill_get_unit_flag(int skill_id) @@ -12115,8 +12139,8 @@ static bool skill_dance_switch(struct skill_unit *su, int flag) group->skill_id = skill_id; group->skill_lv = 1; group->unit_id = skill->get_unit_id(skill_id, 1, 0); - group->target_flag = skill->get_unit_target(skill_id); - group->bl_flag = skill->get_unit_bl_target(skill_id); + group->target_flag = skill->get_unit_target(skill_id, 1); + group->bl_flag = skill->get_unit_bl_target(skill_id, 1); group->interval = skill->get_unit_interval(skill_id, 1); } else { //Restore @@ -12151,7 +12175,7 @@ static struct skill_unit_group *skill_unitsetting(struct block_list *src, uint16 limit = skill->get_time(skill_id,skill_lv); range = skill->get_unit_range(skill_id,skill_lv); interval = skill->get_unit_interval(skill_id, skill_lv); - target = skill->get_unit_target(skill_id); + target = skill->get_unit_target(skill_id, skill_lv); unit_flag = skill->get_unit_flag(skill_id); layout = skill->get_unit_layout(skill_id,skill_lv,src,x,y); @@ -12520,7 +12544,7 @@ static struct skill_unit_group *skill_unitsetting(struct block_list *src, uint16 group->val2=val2; group->val3=val3; group->target_flag=target; - group->bl_flag= skill->get_unit_bl_target(skill_id); + group->bl_flag= skill->get_unit_bl_target(skill_id, skill_lv); group->state.ammo_consume = (sd && sd->state.arrow_atk && skill_id != GS_GROUNDDRIFT); //Store if this skill needs to consume ammo. group->state.song_dance = ((unit_flag&(UF_DANCE|UF_SONG)) ? 1 : 0)|((unit_flag&UF_ENSEMBLE) ? 2 : 0); //Signals if this is a song/dance/duet group->state.guildaura = ( skill_id >= GD_LEADERSHIP && skill_id <= GD_HAWKEYES )?1:0; @@ -22842,10 +22866,51 @@ static void skill_validate_unit_flag(struct config_setting_t *conf, struct s_ski } /** - * Validates a skill's unit target when reading the skill DB. + * Validates a single unit target when reading the skill DB. + * + * @param target The unit target to validate. + * @return A number greater than or equal to 0 if the passed unit target is valid, otherwise -1. + * + **/ +static int skill_validate_unit_target_sub(const char *target) +{ + nullpo_retr(-1, target); + + int ret_val = BCT_NOONE; + + if (strcmpi(target, "NotEnemy") == 0) + ret_val = BCT_NOENEMY; + else if (strcmpi(target, "NotParty") == 0) + ret_val = BCT_NOPARTY; + else if (strcmpi(target, "NotGuild") == 0) + ret_val = BCT_NOGUILD; + else if (strcmpi(target, "Friend") == 0) + ret_val = BCT_NOENEMY; + else if (strcmpi(target, "Party") == 0) + ret_val = BCT_PARTY; + else if (strcmpi(target, "Ally") == 0) + ret_val = BCT_PARTY|BCT_GUILD; + else if (strcmpi(target, "Guild") == 0) + ret_val = BCT_GUILD; + else if (strcmpi(target, "All") == 0) + ret_val = BCT_ALL; + else if (strcmpi(target, "Enemy") == 0) + ret_val = BCT_ENEMY; + else if (strcmpi(target, "Self") == 0) + ret_val = BCT_SELF; + else if (strcmpi(target, "SameGuild") == 0) + ret_val = BCT_SAMEGUILD; + else if (strcmpi(target, "None") != 0) + ret_val = -1; + + return ret_val; +} + +/** + * Validates a skill's unit targets when reading the skill DB. * * @param conf The libconfig settings block which contains the skill's data. - * @param sk The s_skill_db struct where the unit target should be set it. + * @param sk The s_skill_db struct where the unit targets should be set it. * **/ static void skill_validate_unit_target(struct config_setting_t *conf, struct s_skill_db *sk) @@ -22853,52 +22918,56 @@ static void skill_validate_unit_target(struct config_setting_t *conf, struct s_s nullpo_retv(conf); nullpo_retv(sk); - sk->unit_target = BCT_NOONE; - - const char *unit_target; - - if (libconfig->setting_lookup_string(conf, "Target", &unit_target) == CONFIG_TRUE) { - if (strcmpi(unit_target, "NotEnemy") == 0) - sk->unit_target = BCT_NOENEMY; - else if (strcmpi(unit_target, "NotParty") == 0) - sk->unit_target = BCT_NOPARTY; - else if (strcmpi(unit_target, "NotGuild") == 0) - sk->unit_target = BCT_NOGUILD; - else if (strcmpi(unit_target, "Friend") == 0) - sk->unit_target = BCT_NOENEMY; - else if (strcmpi(unit_target, "Party") == 0) - sk->unit_target = BCT_PARTY; - else if (strcmpi(unit_target, "Ally") == 0) - sk->unit_target = BCT_PARTY|BCT_GUILD; - else if (strcmpi(unit_target, "Guild") == 0) - sk->unit_target = BCT_GUILD; - else if (strcmpi(unit_target, "All") == 0) - sk->unit_target = BCT_ALL; - else if (strcmpi(unit_target, "Enemy") == 0) - sk->unit_target = BCT_ENEMY; - else if (strcmpi(unit_target, "Self") == 0) - sk->unit_target = BCT_SELF; - else if (strcmpi(unit_target, "SameGuild") == 0) - sk->unit_target = BCT_SAMEGUILD; - else if (strcmpi(unit_target, "None") != 0) - ShowWarning("%s: Invalid unit target %s specified for skill ID %d in %s! Defaulting to None...\n", - __func__, unit_target, sk->nameid, conf->file); - } - - if ((sk->unit_flag & UF_DEFNOTENEMY) != 0 && battle_config.defnotenemy != 0) - sk->unit_target = BCT_NOENEMY; - - // By default target just characters. - sk->unit_target |= BL_CHAR; - - if ((sk->unit_flag & UF_NOPC) != 0) - sk->unit_target &= ~BL_PC; - - if ((sk->unit_flag & UF_NOMOB) != 0) - sk->unit_target &= ~BL_MOB; - - if ((sk->unit_flag & UF_SKILL) != 0) - sk->unit_target |= BL_SKILL; + skill->level_set_value(sk->unit_target, BCT_NOONE); + + struct config_setting_t *t = libconfig->setting_get_member(conf, "Target"); + + if (t != NULL && config_setting_is_group(t)) { + for (int i = 0; i < MAX_SKILL_LEVEL; i++) { + char lv[6]; // Big enough to contain "Lv999" in case of custom MAX_SKILL_LEVEL. + safesnprintf(lv, sizeof(lv), "Lv%d", i + 1); + const char *unit_target; + + if (libconfig->setting_lookup_string(t, lv, &unit_target) == CONFIG_TRUE) { + int target = skill->validate_unit_target_sub(unit_target); + + if (target > BCT_NOONE) + sk->unit_target[i] = target; + else if (target == -1) + ShowWarning("%s: Invalid unit target %s specified in level %d for skill ID %d in %s! Defaulting to None...\n", + __func__, unit_target, i + 1, sk->nameid, conf->file); + } + } + } else { + const char *unit_target; + + if (libconfig->setting_lookup_string(conf, "Target", &unit_target) == CONFIG_TRUE) { + int target = skill->validate_unit_target_sub(unit_target); + + if (target > BCT_NOONE) + skill->level_set_value(sk->unit_target, target); + else if (target == -1) + ShowWarning("%s: Invalid unit target %s specified for skill ID %d in %s! Defaulting to None...\n", + __func__, unit_target, sk->nameid, conf->file); + } + } + + for (int i = 0; i < MAX_SKILL_LEVEL; i++) { + if ((sk->unit_flag & UF_DEFNOTENEMY) != 0 && battle_config.defnotenemy != 0) + sk->unit_target[i] = BCT_NOENEMY; + + // By default target just characters. + sk->unit_target[i] |= BL_CHAR; + + if ((sk->unit_flag & UF_NOPC) != 0) + sk->unit_target[i] &= ~BL_PC; + + if ((sk->unit_flag & UF_NOMOB) != 0) + sk->unit_target[i] &= ~BL_MOB; + + if ((sk->unit_flag & UF_SKILL) != 0) + sk->unit_target[i] |= BL_SKILL; + } } /** @@ -23410,6 +23479,7 @@ void skill_defaults(void) skill->validate_unit_interval = skill_validate_unit_interval; skill->validate_unit_flag_sub = skill_validate_unit_flag_sub; skill->validate_unit_flag = skill_validate_unit_flag; + skill->validate_unit_target_sub = skill_validate_unit_target_sub; skill->validate_unit_target = skill_validate_unit_target; skill->validate_unit = skill_validate_unit; skill->validate_additional_fields = skill_validate_additional_fields; diff --git a/src/map/skill.h b/src/map/skill.h index c32a8b639..2d68e695d 100644 --- a/src/map/skill.h +++ b/src/map/skill.h @@ -1782,7 +1782,7 @@ struct s_skill_db { int unit_layout_type[MAX_SKILL_LEVEL]; int unit_range[MAX_SKILL_LEVEL]; int unit_interval[MAX_SKILL_LEVEL]; - int unit_target; + int unit_target[MAX_SKILL_LEVEL]; int unit_flag; }; @@ -2006,9 +2006,9 @@ struct skill_interface { int (*get_maxcount) (int skill_id, int skill_lv); int (*get_blewcount) (int skill_id, int skill_lv); int (*get_unit_flag) (int skill_id); - int (*get_unit_target) (int skill_id); + int (*get_unit_target) (int skill_id, int skill_lv); int (*get_unit_interval) (int skill_id, int skill_lv); - int (*get_unit_bl_target) (int skill_id); + int (*get_unit_bl_target) (int skill_id, int skill_lv); int (*get_unit_layout_type) (int skill_id, int skill_lv); int (*get_unit_range) (int skill_id, int skill_lv); int (*get_cooldown) (int skill_id, int skill_lv); @@ -2180,6 +2180,7 @@ struct skill_interface { void (*validate_unit_interval) (struct config_setting_t *conf, struct s_skill_db *sk); int (*validate_unit_flag_sub) (const char *type, bool on, struct s_skill_db *sk); void (*validate_unit_flag) (struct config_setting_t *conf, struct s_skill_db *sk); + int (*validate_unit_target_sub) (const char *target); void (*validate_unit_target) (struct config_setting_t *conf, struct s_skill_db *sk); void (*validate_unit) (struct config_setting_t *conf, struct s_skill_db *sk); void (*validate_additional_fields) (struct config_setting_t *conf, struct s_skill_db *sk); diff --git a/src/plugins/HPMHooking/HPMHooking.Defs.inc b/src/plugins/HPMHooking/HPMHooking.Defs.inc index e75dbaa97..645afa240 100644 --- a/src/plugins/HPMHooking/HPMHooking.Defs.inc +++ b/src/plugins/HPMHooking/HPMHooking.Defs.inc @@ -7304,12 +7304,12 @@ typedef int (*HPMHOOK_pre_skill_get_blewcount) (int *skill_id, int *skill_lv); typedef int (*HPMHOOK_post_skill_get_blewcount) (int retVal___, int skill_id, int skill_lv); typedef int (*HPMHOOK_pre_skill_get_unit_flag) (int *skill_id); typedef int (*HPMHOOK_post_skill_get_unit_flag) (int retVal___, int skill_id); -typedef int (*HPMHOOK_pre_skill_get_unit_target) (int *skill_id); -typedef int (*HPMHOOK_post_skill_get_unit_target) (int retVal___, int skill_id); +typedef int (*HPMHOOK_pre_skill_get_unit_target) (int *skill_id, int *skill_lv); +typedef int (*HPMHOOK_post_skill_get_unit_target) (int retVal___, int skill_id, int skill_lv); typedef int (*HPMHOOK_pre_skill_get_unit_interval) (int *skill_id, int *skill_lv); typedef int (*HPMHOOK_post_skill_get_unit_interval) (int retVal___, int skill_id, int skill_lv); -typedef int (*HPMHOOK_pre_skill_get_unit_bl_target) (int *skill_id); -typedef int (*HPMHOOK_post_skill_get_unit_bl_target) (int retVal___, int skill_id); +typedef int (*HPMHOOK_pre_skill_get_unit_bl_target) (int *skill_id, int *skill_lv); +typedef int (*HPMHOOK_post_skill_get_unit_bl_target) (int retVal___, int skill_id, int skill_lv); typedef int (*HPMHOOK_pre_skill_get_unit_layout_type) (int *skill_id, int *skill_lv); typedef int (*HPMHOOK_post_skill_get_unit_layout_type) (int retVal___, int skill_id, int skill_lv); typedef int (*HPMHOOK_pre_skill_get_unit_range) (int *skill_id, int *skill_lv); diff --git a/src/plugins/HPMHooking/HPMHooking_map.Hooks.inc b/src/plugins/HPMHooking/HPMHooking_map.Hooks.inc index 80c309a8c..1e29af351 100644 --- a/src/plugins/HPMHooking/HPMHooking_map.Hooks.inc +++ b/src/plugins/HPMHooking/HPMHooking_map.Hooks.inc @@ -77768,15 +77768,15 @@ int HP_skill_get_unit_flag(int skill_id) { } return retVal___; } -int HP_skill_get_unit_target(int skill_id) { +int HP_skill_get_unit_target(int skill_id, int skill_lv) { int hIndex = 0; int retVal___ = 0; if (HPMHooks.count.HP_skill_get_unit_target_pre > 0) { - int (*preHookFunc) (int *skill_id); + int (*preHookFunc) (int *skill_id, int *skill_lv); *HPMforce_return = false; for (hIndex = 0; hIndex < HPMHooks.count.HP_skill_get_unit_target_pre; hIndex++) { preHookFunc = HPMHooks.list.HP_skill_get_unit_target_pre[hIndex].func; - retVal___ = preHookFunc(&skill_id); + retVal___ = preHookFunc(&skill_id, &skill_lv); } if (*HPMforce_return) { *HPMforce_return = false; @@ -77784,13 +77784,13 @@ int HP_skill_get_unit_target(int skill_id) { } } { - retVal___ = HPMHooks.source.skill.get_unit_target(skill_id); + retVal___ = HPMHooks.source.skill.get_unit_target(skill_id, skill_lv); } if (HPMHooks.count.HP_skill_get_unit_target_post > 0) { - int (*postHookFunc) (int retVal___, int skill_id); + int (*postHookFunc) (int retVal___, int skill_id, int skill_lv); for (hIndex = 0; hIndex < HPMHooks.count.HP_skill_get_unit_target_post; hIndex++) { postHookFunc = HPMHooks.list.HP_skill_get_unit_target_post[hIndex].func; - retVal___ = postHookFunc(retVal___, skill_id); + retVal___ = postHookFunc(retVal___, skill_id, skill_lv); } } return retVal___; @@ -77822,15 +77822,15 @@ int HP_skill_get_unit_interval(int skill_id, int skill_lv) { } return retVal___; } -int HP_skill_get_unit_bl_target(int skill_id) { +int HP_skill_get_unit_bl_target(int skill_id, int skill_lv) { int hIndex = 0; int retVal___ = 0; if (HPMHooks.count.HP_skill_get_unit_bl_target_pre > 0) { - int (*preHookFunc) (int *skill_id); + int (*preHookFunc) (int *skill_id, int *skill_lv); *HPMforce_return = false; for (hIndex = 0; hIndex < HPMHooks.count.HP_skill_get_unit_bl_target_pre; hIndex++) { preHookFunc = HPMHooks.list.HP_skill_get_unit_bl_target_pre[hIndex].func; - retVal___ = preHookFunc(&skill_id); + retVal___ = preHookFunc(&skill_id, &skill_lv); } if (*HPMforce_return) { *HPMforce_return = false; @@ -77838,13 +77838,13 @@ int HP_skill_get_unit_bl_target(int skill_id) { } } { - retVal___ = HPMHooks.source.skill.get_unit_bl_target(skill_id); + retVal___ = HPMHooks.source.skill.get_unit_bl_target(skill_id, skill_lv); } if (HPMHooks.count.HP_skill_get_unit_bl_target_post > 0) { - int (*postHookFunc) (int retVal___, int skill_id); + int (*postHookFunc) (int retVal___, int skill_id, int skill_lv); for (hIndex = 0; hIndex < HPMHooks.count.HP_skill_get_unit_bl_target_post; hIndex++) { postHookFunc = HPMHooks.list.HP_skill_get_unit_bl_target_post[hIndex].func; - retVal___ = postHookFunc(retVal___, skill_id); + retVal___ = postHookFunc(retVal___, skill_id, skill_lv); } } return retVal___; -- cgit v1.2.3-60-g2f50 From 84149ebf0165ed67cd420335ddc66e7f58ac93ea Mon Sep 17 00:00:00 2001 From: Kenpachi Developer Date: Sun, 17 May 2020 07:21:21 +0200 Subject: Add missing types to skill_validate_unit_target_sub() --- src/map/skill.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/map/skill.c b/src/map/skill.c index 9b9c17b21..7a46026b1 100644 --- a/src/map/skill.c +++ b/src/map/skill.c @@ -22900,6 +22900,10 @@ static int skill_validate_unit_target_sub(const char *target) ret_val = BCT_SELF; else if (strcmpi(target, "SameGuild") == 0) ret_val = BCT_SAMEGUILD; + else if (strcmpi(target, "GuildAlly") == 0) + ret_val = BCT_GUILDALLY; + else if (strcmpi(target, "Neutral") == 0) + ret_val = BCT_NEUTRAL; else if (strcmpi(target, "None") != 0) ret_val = -1; -- cgit v1.2.3-60-g2f50 From 932e22bc99c12078a1a101cfc69778dd049d86f6 Mon Sep 17 00:00:00 2001 From: Kenpachi Developer Date: Thu, 21 May 2020 08:46:41 +0200 Subject: Make Requirements->Items->Amount can be grouped by levels --- src/map/skill.c | 266 +++++++++++++++++++----- src/map/skill.h | 17 +- src/plugins/HPMHooking/HPMHooking.Defs.inc | 4 +- src/plugins/HPMHooking/HPMHooking_map.Hooks.inc | 12 +- 4 files changed, 236 insertions(+), 63 deletions(-) diff --git a/src/map/skill.c b/src/map/skill.c index 7a46026b1..09165506f 100644 --- a/src/map/skill.c +++ b/src/map/skill.c @@ -369,26 +369,50 @@ static int skill_get_spiritball(int skill_id, int skill_lv) return skill->dbs->db[idx].spiritball[skill_get_lvl_idx(skill_lv)]; } +/** + * Gets a skill's required item's ID by the skill's ID and the item's index. + * + * @param skill_id The skill's ID. + * @param item_idx The item's index. + * @return The skill's required item's ID corresponding to the passed index. Defaults to 0 in case of error. + * + **/ static int skill_get_itemid(int skill_id, int item_idx) { - int idx; if (skill_id == 0) return 0; - idx = skill->get_index(skill_id); - Assert_ret(idx != 0); + Assert_ret(item_idx >= 0 && item_idx < MAX_SKILL_ITEM_REQUIRE); - return skill->dbs->db[idx].itemid[item_idx]; + + int idx = skill->get_index(skill_id); + + Assert_ret(idx != 0); + + return skill->dbs->db[idx].req_items.item[item_idx].id; } -static int skill_get_itemqty(int skill_id, int item_idx) +/** + * Gets a skill's required item's amount by the skill's ID and level and the item's index. + * + * @param skill_id The skill's ID. + * @param item_idx The item's index. + * @param skill_lv The skill's level. + * @return The skill's required item's amount corresponding to the passed index and level. Defaults to 0 in case of error. + * + **/ +static int skill_get_itemqty(int skill_id, int item_idx, int skill_lv) { - int idx; if (skill_id == 0) return 0; - idx = skill->get_index(skill_id); - Assert_ret(idx != 0); + Assert_ret(item_idx >= 0 && item_idx < MAX_SKILL_ITEM_REQUIRE); - return skill->dbs->db[idx].amount[item_idx]; + Assert_ret(skill_lv > 0); + + int idx = skill->get_index(skill_id); + + Assert_ret(idx != 0); + + return skill->dbs->db[idx].req_items.item[item_idx].amount[skill_get_lvl_idx(skill_lv)]; } static int skill_get_zeny(int skill_id, int skill_lv) @@ -3901,8 +3925,8 @@ static int skill_check_condition_mercenary(struct block_list *bl, int skill_id, // Requirements for (i = 0; i < MAX_SKILL_ITEM_REQUIRE; i++) { - itemid[i] = skill->dbs->db[idx].itemid[i]; - amount[i] = skill->dbs->db[idx].amount[i]; + itemid[i] = skill->get_itemid(skill_id, i); + amount[i] = skill->get_itemqty(skill_id, i, lv); } hp = skill->dbs->db[idx].hp[lv-1]; sp = skill->dbs->db[idx].sp[lv-1]; @@ -7787,7 +7811,7 @@ static int skill_castend_nodamage_id(struct block_list *src, struct block_list * map->freeblock_unlock(); return 1; } - if (sd->inventory_data[inventory_idx] == NULL || sd->status.inventory[inventory_idx].amount < skill->get_itemqty(skill_id, item_idx)) { + if (sd->inventory_data[inventory_idx] == NULL || sd->status.inventory[inventory_idx].amount < skill->get_itemqty(skill_id, item_idx, skill_lv)) { clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); map->freeblock_unlock(); return 1; @@ -8415,7 +8439,7 @@ static int skill_castend_nodamage_id(struct block_list *src, struct block_list * if (nameid > 0) { int success; struct item item_tmp = { 0 }; - int amount = skill->get_itemqty(su->group->skill_id, i); + int amount = skill->get_itemqty(su->group->skill_id, i, skill_lv); item_tmp.nameid = nameid; item_tmp.identify = 1; if ((success = pc->additem(sd, &item_tmp, amount, LOG_TYPE_SKILL)) != 0) { @@ -11645,7 +11669,7 @@ static int skill_castend_pos2(struct block_list *src, int x, int y, uint16 skill int bonus; if (inventory_idx == INDEX_NOT_FOUND || item_id <= 0 || sd->inventory_data[inventory_idx] == NULL - || sd->status.inventory[inventory_idx].amount < skill->get_itemqty(skill_id, item_idx) + || sd->status.inventory[inventory_idx].amount < skill->get_itemqty(skill_id, item_idx, skill_lv) ) { clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); return 1; @@ -15634,11 +15658,11 @@ static struct skill_condition skill_get_requirement(struct map_session_data *sd, continue; break; case AB_ADORAMUS: - if( itemid_isgemstone(skill->dbs->db[idx].itemid[i]) && skill->check_pc_partner(sd,skill_id,&skill_lv, 1, 2) ) + if (itemid_isgemstone(skill->get_itemid(skill_id, i)) && skill->check_pc_partner(sd, skill_id, &skill_lv, 1, 2) != 0) continue; break; case WL_COMET: - if( itemid_isgemstone(skill->dbs->db[idx].itemid[i]) && skill->check_pc_partner(sd,skill_id,&skill_lv, 1, 0) ) + if (itemid_isgemstone(skill->get_itemid(skill_id, i)) && skill->check_pc_partner(sd, skill_id, &skill_lv, 1, 0) != 0) continue; break; case GN_FIRE_EXPANSION: @@ -15664,8 +15688,12 @@ static struct skill_condition skill_get_requirement(struct map_session_data *sd, } } - req.itemid[i] = skill->dbs->db[idx].itemid[i]; - req.amount[i] = skill->dbs->db[idx].amount[i]; + int amount; + + if ((amount = skill->get_itemqty(skill_id, i, skill_lv)) >= 0) { + req.itemid[i] = skill->get_itemid(skill_id, i); + req.amount[i] = amount; + } if (itemid_isgemstone(req.itemid[i]) && skill_id != HW_GANBANTEIN) { if (sd->special_state.no_gemstone) { @@ -15709,8 +15737,8 @@ static struct skill_condition skill_get_requirement(struct map_session_data *sd, case SO_FIRE_INSIGNIA: case SO_WIND_INSIGNIA: case SO_EARTH_INSIGNIA: - req.itemid[skill_lv-1] = skill->dbs->db[idx].itemid[skill_lv-1]; - req.amount[skill_lv-1] = skill->dbs->db[idx].amount[skill_lv-1]; + req.itemid[skill_lv - 1] = skill->get_itemid(skill_id, skill_lv - 1); + req.amount[skill_lv - 1] = skill->get_itemqty(skill_id, skill_lv - 1, skill_lv); break; } if (skill_id == NC_REPAIR) { @@ -22340,6 +22368,53 @@ static void skill_validate_spirit_sphere_cost(struct config_setting_t *conf, str } } +/** + * Validates a skill's required items amounts when reading the skill DB. + * + * @param conf The libconfig settings block which contains the skill's data. + * @param sk The s_skill_db struct where the required items amounts should be set it. + * + **/ +static void skill_validate_item_requirements_sub_item_amount(struct config_setting_t *conf, struct s_skill_db *sk, int item_index) +{ + nullpo_retv(conf); + nullpo_retv(sk); + + for (int i = 0; i < MAX_SKILL_LEVEL; i++) + sk->req_items.item[item_index].amount[i] = 0; + + if (config_setting_is_group(conf)) { + for (int i = 0; i < MAX_SKILL_LEVEL; i++) { + char lv[6]; // Big enough to contain "Lv999" in case of custom MAX_SKILL_LEVEL. + safesnprintf(lv, sizeof(lv), "Lv%d", i + 1); + int amount; + + if (libconfig->setting_lookup_int(conf, lv, &amount) == CONFIG_TRUE) { + if (amount >= 0 && amount <= MAX_AMOUNT) + sk->req_items.item[item_index].amount[i] = amount; + else + ShowWarning("%s: Invalid required item amount %d specified in level %d for skill ID %d in %s! Minimum is 0, maximum is %d. Defaulting to 0...\n", + __func__, amount, i + 1, sk->nameid, conf->file, MAX_AMOUNT); + } else { + // Items is not required for this skill level. (Not even in inventory!) + sk->req_items.item[item_index].amount[i] = -1; + } + } + + return; + } + + int amount = libconfig->setting_get_int(conf); + + if (amount >= 0 && amount <= MAX_AMOUNT) { + for (int i = 0; i < MAX_SKILL_LEVEL; i++) + sk->req_items.item[item_index].amount[i] = amount; + } else { + ShowWarning("%s: Invalid required item amount %d specified for skill ID %d in %s! Minimum is 0, maximum is %d. Defaulting to 0...\n", + __func__, amount, sk->nameid, conf->file, MAX_AMOUNT); + } +} + /** * Validates a skill's required items when reading the skill DB. * @@ -22347,64 +22422,145 @@ static void skill_validate_spirit_sphere_cost(struct config_setting_t *conf, str * @param sk The s_skill_db struct where the required items should be set it. * **/ -static void skill_validate_item_requirements(struct config_setting_t *conf, struct s_skill_db *sk) +static void skill_validate_item_requirements_sub_items(struct config_setting_t *conf, struct s_skill_db *sk) { nullpo_retv(conf); nullpo_retv(sk); - skill->level_set_value(sk->itemid, 0); - skill->level_set_value(sk->amount, 0); + for (int i = 0; i < MAX_SKILL_ITEM_REQUIRE; i++) { + sk->req_items.item[i].id = 0; - struct config_setting_t *t = libconfig->setting_get_member(conf, "Items"); + for (int j = 0; j < MAX_SKILL_LEVEL; j++) + sk->req_items.item[i].amount[j] = 0; + } - if (t != NULL && config_setting_is_group(conf)) { - struct config_setting_t *tt; - int i = -1; + int item_index = 0; + int count = libconfig->setting_length(conf); - while ((tt = libconfig->setting_get_elem(t, ++i)) != NULL && i < MAX_SKILL_ITEM_REQUIRE) { - const char *type = config_setting_name(tt); + for (int i = 0; i < count; i++) { + struct config_setting_t *t = libconfig->setting_get_elem(conf, i); - if (strlen(type) < 2) { - ShowWarning("%s: Invalid required item %s specified for skill ID %d in %s! Skipping item...\n", - __func__, type, sk->nameid, conf->file); + if (t != NULL && strcasecmp(config_setting_name(t), "Any") != 0) { + if (item_index >= MAX_SKILL_ITEM_REQUIRE) { + ShowWarning("%s: Too many required items specified for skill ID %d in %s! Skipping item %s...\n", + __func__, sk->nameid, conf->file, config_setting_name(t)); continue; } - int item_id = 0; + int item_id = skill->validate_requirements_item_name(config_setting_name(t)); - if (type[0] == 'I' && type[1] == 'D') { - item_id = atoi(type + 2); - - if (item_id == 0 || itemdb->exists(item_id) == NULL) { - ShowWarning("%s: Invalid required item %s specified for skill ID %d in %s! Skipping item...\n", - __func__, type, sk->nameid, conf->file); - continue; - } - } else if (!script->get_constant(type, &item_id)) { + if (item_id == 0) { ShowWarning("%s: Invalid required item %s specified for skill ID %d in %s! Skipping item...\n", - __func__, type, sk->nameid, conf->file); + __func__, config_setting_name(t), sk->nameid, conf->file); continue; } - int amount = 0; + int j; - if (config_setting_is_group(tt)) - amount = libconfig->setting_get_int_elem(tt, 0); - else - amount = libconfig->setting_get_int(tt); + ARR_FIND(0, MAX_SKILL_ITEM_REQUIRE, j, sk->req_items.item[j].id == item_id); - if (amount < 0 || amount > MAX_AMOUNT) { - ShowWarning("%s: Invalid required item amount %d specified for skill ID %d in %s! Minimum is 0, maximum is %d. Skipping item...\n", - __func__, amount, sk->nameid, conf->file, MAX_AMOUNT); + if (j < MAX_SKILL_ITEM_REQUIRE) { + ShowWarning("%s: Duplicate required item %s specified for skill ID %d in %s! Skipping item...\n", + __func__, config_setting_name(t), sk->nameid, conf->file); continue; } - sk->itemid[i] = item_id; - sk->amount[i] = amount; + sk->req_items.item[item_index].id = item_id; + skill->validate_item_requirements_sub_item_amount(t, sk, item_index); + item_index++; } } } +/** + * Validates a skill's required items any-flag when reading the skill DB. + * + * @param conf The libconfig settings block which contains the skill's data. + * @param sk The s_skill_db struct where the required items any-flag should be set it. + * + **/ +static void skill_validate_item_requirements_sub_any_flag(struct config_setting_t *conf, struct s_skill_db *sk) +{ + nullpo_retv(conf); + nullpo_retv(sk); + + for (int i = 0; i < MAX_SKILL_LEVEL; i++) + sk->req_items.any[i] = false; + + struct config_setting_t *t = libconfig->setting_get_member(conf, "Any"); + + if (t != NULL && config_setting_is_group(t)) { + for (int i = 0; i < MAX_SKILL_LEVEL; i++) { + char lv[6]; // Big enough to contain "Lv999" in case of custom MAX_SKILL_LEVEL. + safesnprintf(lv, sizeof(lv), "Lv%d", i + 1); + int any_flag; + + if (libconfig->setting_lookup_bool(t, lv, &any_flag) == CONFIG_TRUE) + sk->req_items.any[i] = (any_flag != 0); + } + + return; + } + + int any_flag; + + if (libconfig->setting_lookup_bool(conf, "Any", &any_flag) == CONFIG_TRUE && any_flag != 0) { + for (int i = 0; i < MAX_SKILL_LEVEL; i++) + sk->req_items.any[i] = true; + } +} + +/** + * Validates a skill's required items when reading the skill DB. + * + * @param conf The libconfig settings block which contains the skill's data. + * @param sk The s_skill_db struct where the required items should be set it. + * + **/ +static void skill_validate_item_requirements(struct config_setting_t *conf, struct s_skill_db *sk) +{ + nullpo_retv(conf); + nullpo_retv(sk); + + struct config_setting_t *t = libconfig->setting_get_member(conf, "Items"); + + if (t != NULL && config_setting_is_group(t)) { + skill->validate_item_requirements_sub_any_flag(t, sk); + skill->validate_item_requirements_sub_items(t, sk); + } +} + +/** + * Validates a required item's config setting name when reading the skill DB. + * + * @param name The config setting name to validate. + * @return The corresponding item ID if the passed config setting name is valid, otherwise 0. + * + **/ +static int skill_validate_requirements_item_name(const char *name) +{ + nullpo_ret(name); + + int item_id = 0; + + if (strlen(name) > 2 && name[0] == 'I' && name[1] == 'D') { + if ((item_id = atoi(name + 2)) == 0) + return 0; + + struct item_data *it = itemdb->exists(item_id); + + if (it == NULL) + return 0; + + return it->nameid; + } + + if (!script->get_constant(name, &item_id)) + return 0; + + return item_id; +} + /** * Validates a skill's requirements when reading the skill DB. * @@ -23474,7 +23630,11 @@ void skill_defaults(void) skill->validate_state_sub = skill_validate_state_sub; skill->validate_state = skill_validate_state; skill->validate_spirit_sphere_cost = skill_validate_spirit_sphere_cost; + skill->validate_item_requirements_sub_item_amount = skill_validate_item_requirements_sub_item_amount; + skill->validate_item_requirements_sub_items = skill_validate_item_requirements_sub_items; + skill->validate_item_requirements_sub_any_flag = skill_validate_item_requirements_sub_any_flag; skill->validate_item_requirements = skill_validate_item_requirements; + skill->validate_requirements_item_name = skill_validate_requirements_item_name; skill->validate_requirements = skill_validate_requirements; skill->validate_unit_id_sub = skill_validate_unit_id_sub; skill->validate_unit_id = skill_validate_unit_id; diff --git a/src/map/skill.h b/src/map/skill.h index 2d68e695d..374b76b7f 100644 --- a/src/map/skill.h +++ b/src/map/skill.h @@ -1741,6 +1741,15 @@ enum autocast_type { * Structures **/ +/** A container holding all required items. **/ +struct skill_required_item_data { + struct { + int id; + int amount[MAX_SKILL_LEVEL]; + } item[MAX_SKILL_ITEM_REQUIRE]; + bool any[MAX_SKILL_LEVEL]; +}; + struct skill_condition { int weapon,ammo,ammo_qty,hp,sp,zeny,spiritball,mhp,state; int itemid[MAX_SKILL_ITEM_REQUIRE],amount[MAX_SKILL_ITEM_REQUIRE]; @@ -1776,7 +1785,6 @@ struct s_skill_db { int ammo_qty[MAX_SKILL_LEVEL]; int state[MAX_SKILL_LEVEL]; int spiritball[MAX_SKILL_LEVEL]; - int itemid[MAX_SKILL_ITEM_REQUIRE],amount[MAX_SKILL_ITEM_REQUIRE]; int castnodex[MAX_SKILL_LEVEL], delaynodex[MAX_SKILL_LEVEL]; int unit_id[MAX_SKILL_LEVEL][2]; int unit_layout_type[MAX_SKILL_LEVEL]; @@ -1784,6 +1792,7 @@ struct s_skill_db { int unit_interval[MAX_SKILL_LEVEL]; int unit_target[MAX_SKILL_LEVEL]; int unit_flag; + struct skill_required_item_data req_items; }; struct s_skill_unit_layout { @@ -1986,7 +1995,7 @@ struct skill_interface { int (*get_state) (int skill_id, int skill_lv); int (*get_spiritball) (int skill_id, int skill_lv); int (*get_itemid) (int skill_id, int item_idx); - int (*get_itemqty) (int skill_id, int item_idx); + int (*get_itemqty) (int skill_id, int item_idx, int skill_lv); int (*get_zeny) (int skill_id, int skill_lv); int (*get_num) (int skill_id, int skill_lv); int (*get_cast) (int skill_id, int skill_lv); @@ -2171,7 +2180,11 @@ struct skill_interface { int (*validate_state_sub) (const char *state); void (*validate_state) (struct config_setting_t *conf, struct s_skill_db *sk); void (*validate_spirit_sphere_cost) (struct config_setting_t *conf, struct s_skill_db *sk); + void (*validate_item_requirements_sub_item_amount) (struct config_setting_t *conf, struct s_skill_db *sk, int item_index); + void (*validate_item_requirements_sub_items) (struct config_setting_t *conf, struct s_skill_db *sk); + void (*validate_item_requirements_sub_any_flag) (struct config_setting_t *conf, struct s_skill_db *sk); void (*validate_item_requirements) (struct config_setting_t *conf, struct s_skill_db *sk); + int (*validate_requirements_item_name) (const char *name); void (*validate_requirements) (struct config_setting_t *conf, struct s_skill_db *sk); int (*validate_unit_id_sub) (int unit_id); void (*validate_unit_id) (struct config_setting_t *conf, struct s_skill_db *sk); diff --git a/src/plugins/HPMHooking/HPMHooking.Defs.inc b/src/plugins/HPMHooking/HPMHooking.Defs.inc index 645afa240..9be0fdae9 100644 --- a/src/plugins/HPMHooking/HPMHooking.Defs.inc +++ b/src/plugins/HPMHooking/HPMHooking.Defs.inc @@ -7264,8 +7264,8 @@ typedef int (*HPMHOOK_pre_skill_get_spiritball) (int *skill_id, int *skill_lv); typedef int (*HPMHOOK_post_skill_get_spiritball) (int retVal___, int skill_id, int skill_lv); typedef int (*HPMHOOK_pre_skill_get_itemid) (int *skill_id, int *item_idx); typedef int (*HPMHOOK_post_skill_get_itemid) (int retVal___, int skill_id, int item_idx); -typedef int (*HPMHOOK_pre_skill_get_itemqty) (int *skill_id, int *item_idx); -typedef int (*HPMHOOK_post_skill_get_itemqty) (int retVal___, int skill_id, int item_idx); +typedef int (*HPMHOOK_pre_skill_get_itemqty) (int *skill_id, int *item_idx, int *skill_lv); +typedef int (*HPMHOOK_post_skill_get_itemqty) (int retVal___, int skill_id, int item_idx, int skill_lv); typedef int (*HPMHOOK_pre_skill_get_zeny) (int *skill_id, int *skill_lv); typedef int (*HPMHOOK_post_skill_get_zeny) (int retVal___, int skill_id, int skill_lv); typedef int (*HPMHOOK_pre_skill_get_num) (int *skill_id, int *skill_lv); diff --git a/src/plugins/HPMHooking/HPMHooking_map.Hooks.inc b/src/plugins/HPMHooking/HPMHooking_map.Hooks.inc index 1e29af351..1cea69e4a 100644 --- a/src/plugins/HPMHooking/HPMHooking_map.Hooks.inc +++ b/src/plugins/HPMHooking/HPMHooking_map.Hooks.inc @@ -77228,15 +77228,15 @@ int HP_skill_get_itemid(int skill_id, int item_idx) { } return retVal___; } -int HP_skill_get_itemqty(int skill_id, int item_idx) { +int HP_skill_get_itemqty(int skill_id, int item_idx, int skill_lv) { int hIndex = 0; int retVal___ = 0; if (HPMHooks.count.HP_skill_get_itemqty_pre > 0) { - int (*preHookFunc) (int *skill_id, int *item_idx); + int (*preHookFunc) (int *skill_id, int *item_idx, int *skill_lv); *HPMforce_return = false; for (hIndex = 0; hIndex < HPMHooks.count.HP_skill_get_itemqty_pre; hIndex++) { preHookFunc = HPMHooks.list.HP_skill_get_itemqty_pre[hIndex].func; - retVal___ = preHookFunc(&skill_id, &item_idx); + retVal___ = preHookFunc(&skill_id, &item_idx, &skill_lv); } if (*HPMforce_return) { *HPMforce_return = false; @@ -77244,13 +77244,13 @@ int HP_skill_get_itemqty(int skill_id, int item_idx) { } } { - retVal___ = HPMHooks.source.skill.get_itemqty(skill_id, item_idx); + retVal___ = HPMHooks.source.skill.get_itemqty(skill_id, item_idx, skill_lv); } if (HPMHooks.count.HP_skill_get_itemqty_post > 0) { - int (*postHookFunc) (int retVal___, int skill_id, int item_idx); + int (*postHookFunc) (int retVal___, int skill_id, int item_idx, int skill_lv); for (hIndex = 0; hIndex < HPMHooks.count.HP_skill_get_itemqty_post; hIndex++) { postHookFunc = HPMHooks.list.HP_skill_get_itemqty_post[hIndex].func; - retVal___ = postHookFunc(retVal___, skill_id, item_idx); + retVal___ = postHookFunc(retVal___, skill_id, item_idx, skill_lv); } } return retVal___; -- cgit v1.2.3-60-g2f50 From 4f36f0c78cc8e082263d74ccd4ad7fb1abbf2c64 Mon Sep 17 00:00:00 2001 From: Kenpachi Developer Date: Thu, 21 May 2020 10:47:21 +0200 Subject: Remove hard-coded required item amounts and update skill DBs accordingly --- db/pre-re/skill_db.conf | 186 +++++++++++++++++++++++++++++++++--------------- db/re/skill_db.conf | 178 +++++++++++++++++++++++++++++++-------------- src/map/skill.c | 60 ---------------- 3 files changed, 253 insertions(+), 171 deletions(-) diff --git a/db/pre-re/skill_db.conf b/db/pre-re/skill_db.conf index f65e35357..800c21803 100644 --- a/db/pre-re/skill_db.conf +++ b/db/pre-re/skill_db.conf @@ -2850,7 +2850,13 @@ skill_db: ( Requirements: { SPCost: 75 Items: { - Blue_Gemstone: 1 + Blue_Gemstone: { + Lv6: 1 + Lv7: 1 + Lv8: 1 + Lv9: 1 + Lv10: 1 + } } } Unit: { @@ -6739,16 +6745,21 @@ skill_db: ( Requirements: { SPCost: 1 Items: { - Red_Potion: 1 - Orange_Potion: 1 - Yellow_Potion: 1 - White_Potion: 1 - Blue_Potion: 1 - Fruit_Of_Mastela: 1 - Royal_Jelly: 1 - Seed_Of_Yggdrasil: 1 - Yggdrasilberry: 1 - Berserk_Potion: 1 + Red_Potion: { + Lv1: 1 + } + Orange_Potion: { + Lv2: 1 + } + Yellow_Potion: { + Lv3: 1 + } + White_Potion: { + Lv4: 1 + } + Blue_Potion: { + Lv5: 1 + } } } }, @@ -14309,16 +14320,22 @@ skill_db: ( Requirements: { SPCost: 30 Items: { - Red_Slim_Potion: 1 - Red_Slim_Potion: 1 - Red_Slim_Potion: 1 - Red_Slim_Potion: 1 - Red_Slim_Potion: 1 - Yellow_Slim_Potion: 1 - Yellow_Slim_Potion: 1 - Yellow_Slim_Potion: 1 - Yellow_Slim_Potion: 1 - White_Slim_Potion: 1 + Red_Slim_Potion: { + Lv1: 1 + Lv2: 1 + Lv3: 1 + Lv4: 1 + Lv5: 1 + } + Yellow_Slim_Potion: { + Lv6: 1 + Lv7: 1 + Lv8: 1 + Lv9: 1 + } + White_Slim_Potion: { + Lv10: 1 + } } } }, @@ -14807,8 +14824,12 @@ skill_db: ( Requirements: { SPCost: 10 Items: { - Mushroom_Spore: 1 - Stem: 1 + Mushroom_Spore: { + Lv1: 1 + } + Stem: { + Lv2: 1 + } } } }, @@ -22589,10 +22610,18 @@ skill_db: ( SPCost: 100 State: "MadoGear" Items: { - Scarlet_Pts: 1 - Lime_Green_Pts: 1 - Yellow_Wish_Pts: 1 - Indigo_Pts: 1 + Scarlet_Pts: { + Lv1: 1 + } + Lime_Green_Pts: { + Lv2: 1 + } + Yellow_Wish_Pts: { + Lv3: 1 + } + Indigo_Pts: { + Lv4: 1 + } Magic_Gear_Fuel: 2 Shape_Shifter: 0 } @@ -22898,6 +22927,17 @@ skill_db: ( } State: "MadoGear" Items: { + RepairA: { + Lv1: 1 + Lv2: 1 + } + RepairB: { + Lv3: 1 + Lv4: 1 + } + RepairC: { + Lv5: 1 + } Repair_Kit: 0 } } @@ -28507,9 +28547,13 @@ skill_db: ( Lv10: 550 } Items: { - Boody_Red: 3 - Boody_Red: 6 - Flame_Heart: 1 + Boody_Red: { + Lv1: 3 + Lv2: 6 + } + Flame_Heart: { + Lv3: 1 + } } } }, @@ -28566,9 +28610,13 @@ skill_db: ( Lv10: 550 } Items: { - Crystal_Blue: 3 - Crystal_Blue: 6 - Mistic_Frozen: 1 + Crystal_Blue: { + Lv1: 3 + Lv2: 6 + } + Mistic_Frozen: { + Lv3: 1 + } } } }, @@ -28625,9 +28673,13 @@ skill_db: ( Lv10: 550 } Items: { - Wind_Of_Verdure: 3 - Wind_Of_Verdure: 6 - Rough_Wind: 1 + Wind_Of_Verdure: { + Lv1: 3 + Lv2: 6 + } + Rough_Wind: { + Lv3: 1 + } } } }, @@ -28684,9 +28736,13 @@ skill_db: ( Lv10: 550 } Items: { - Yellow_Live: 3 - Yellow_Live: 6 - Great_Nature: 1 + Yellow_Live: { + Lv1: 3 + Lv2: 6 + } + Great_Nature: { + Lv3: 1 + } } } }, @@ -28800,9 +28856,11 @@ skill_db: ( Lv10: 94 } Items: { - Scarlet_Pts: 1 - Scarlet_Pts: 2 - Scarlet_Pts: 3 + Scarlet_Pts: { + Lv1: 1 + Lv2: 2 + Lv3: 3 + } } } Unit: { @@ -28849,9 +28907,11 @@ skill_db: ( Lv10: 94 } Items: { - Indigo_Pts: 1 - Indigo_Pts: 2 - Indigo_Pts: 3 + Indigo_Pts: { + Lv1: 1 + Lv2: 2 + Lv3: 3 + } } } Unit: { @@ -28898,9 +28958,11 @@ skill_db: ( Lv10: 94 } Items: { - Yellow_Wish_Pts: 1 - Yellow_Wish_Pts: 2 - Yellow_Wish_Pts: 3 + Yellow_Wish_Pts: { + Lv1: 1 + Lv2: 2 + Lv3: 3 + } } } Unit: { @@ -28947,9 +29009,11 @@ skill_db: ( Lv10: 94 } Items: { - Lime_Green_Pts: 1 - Lime_Green_Pts: 2 - Lime_Green_Pts: 3 + Lime_Green_Pts: { + Lv1: 1 + Lv2: 2 + Lv3: 3 + } } } Unit: { @@ -29540,11 +29604,21 @@ skill_db: ( Lv10: 75 } Items: { - Oil_Bottle: 1 - Explosive_Powder: 1 - Smoke_Powder: 1 - Tear_Gas: 1 - Acid_Bottle: 1 + Oil_Bottle: { + Lv1: 1 + } + Explosive_Powder: { + Lv2: 1 + } + Smoke_Powder: { + Lv3: 1 + } + Tear_Gas: { + Lv4: 1 + } + Acid_Bottle: { + Lv5: 1 + } } } }, diff --git a/db/re/skill_db.conf b/db/re/skill_db.conf index 4bbdd19f3..2ff5fe9c0 100644 --- a/db/re/skill_db.conf +++ b/db/re/skill_db.conf @@ -7070,16 +7070,21 @@ skill_db: ( Requirements: { SPCost: 1 Items: { - Red_Potion: 1 - Orange_Potion: 1 - Yellow_Potion: 1 - White_Potion: 1 - Blue_Potion: 1 - Fruit_Of_Mastela: 1 - Royal_Jelly: 1 - Seed_Of_Yggdrasil: 1 - Yggdrasilberry: 1 - Berserk_Potion: 1 + Red_Potion: { + Lv1: 1 + } + Orange_Potion: { + Lv2: 1 + } + Yellow_Potion: { + Lv3: 1 + } + White_Potion: { + Lv4: 1 + } + Blue_Potion: { + Lv5: 1 + } } } }, @@ -14671,16 +14676,22 @@ skill_db: ( Requirements: { SPCost: 30 Items: { - Red_Slim_Potion: 1 - Red_Slim_Potion: 1 - Red_Slim_Potion: 1 - Red_Slim_Potion: 1 - Red_Slim_Potion: 1 - Yellow_Slim_Potion: 1 - Yellow_Slim_Potion: 1 - Yellow_Slim_Potion: 1 - Yellow_Slim_Potion: 1 - White_Slim_Potion: 1 + Red_Slim_Potion: { + Lv1: 1 + Lv2: 1 + Lv3: 1 + Lv4: 1 + Lv5: 1 + } + Yellow_Slim_Potion: { + Lv6: 1 + Lv7: 1 + Lv8: 1 + Lv9: 1 + } + White_Slim_Potion: { + Lv10: 1 + } } } }, @@ -15166,8 +15177,12 @@ skill_db: ( Requirements: { SPCost: 10 Items: { - Mushroom_Spore: 1 - Stem: 1 + Mushroom_Spore: { + Lv1: 1 + } + Stem: { + Lv2: 1 + } } } }, @@ -23209,10 +23224,18 @@ skill_db: ( SPCost: 100 State: "MadoGear" Items: { - Scarlet_Pts: 1 - Lime_Green_Pts: 1 - Yellow_Wish_Pts: 1 - Indigo_Pts: 1 + Scarlet_Pts: { + Lv1: 1 + } + Lime_Green_Pts: { + Lv2: 1 + } + Yellow_Wish_Pts: { + Lv3: 1 + } + Indigo_Pts: { + Lv4: 1 + } Magic_Gear_Fuel: 2 Shape_Shifter: 0 } @@ -23523,6 +23546,17 @@ skill_db: ( } State: "MadoGear" Items: { + RepairA: { + Lv1: 1 + Lv2: 1 + } + RepairB: { + Lv3: 1 + Lv4: 1 + } + RepairC: { + Lv5: 1 + } Repair_Kit: 0 } } @@ -29317,9 +29351,13 @@ skill_db: ( Lv10: 550 } Items: { - Boody_Red: 3 - Boody_Red: 6 - Flame_Heart: 1 + Boody_Red: { + Lv1: 3 + Lv2: 6 + } + Flame_Heart: { + Lv3: 1 + } } } }, @@ -29377,9 +29415,13 @@ skill_db: ( Lv10: 550 } Items: { - Crystal_Blue: 3 - Crystal_Blue: 6 - Mistic_Frozen: 1 + Crystal_Blue: { + Lv1: 3 + Lv2: 6 + } + Mistic_Frozen: { + Lv3: 1 + } } } }, @@ -29437,9 +29479,13 @@ skill_db: ( Lv10: 550 } Items: { - Wind_Of_Verdure: 3 - Wind_Of_Verdure: 6 - Rough_Wind: 1 + Wind_Of_Verdure: { + Lv1: 3 + Lv2: 6 + } + Rough_Wind: { + Lv3: 1 + } } } }, @@ -29497,9 +29543,13 @@ skill_db: ( Lv10: 550 } Items: { - Yellow_Live: 3 - Yellow_Live: 6 - Great_Nature: 1 + Yellow_Live: { + Lv1: 3 + Lv2: 6 + } + Great_Nature: { + Lv3: 1 + } } } }, @@ -29615,9 +29665,11 @@ skill_db: ( Lv10: 94 } Items: { - Scarlet_Pts: 1 - Scarlet_Pts: 2 - Scarlet_Pts: 3 + Scarlet_Pts: { + Lv1: 1 + Lv2: 2 + Lv3: 3 + } } } Unit: { @@ -29665,9 +29717,11 @@ skill_db: ( Lv10: 94 } Items: { - Indigo_Pts: 1 - Indigo_Pts: 2 - Indigo_Pts: 3 + Indigo_Pts: { + Lv1: 1 + Lv2: 2 + Lv3: 3 + } } } Unit: { @@ -29715,9 +29769,11 @@ skill_db: ( Lv10: 94 } Items: { - Yellow_Wish_Pts: 1 - Yellow_Wish_Pts: 2 - Yellow_Wish_Pts: 3 + Yellow_Wish_Pts: { + Lv1: 1 + Lv2: 2 + Lv3: 3 + } } } Unit: { @@ -29765,9 +29821,11 @@ skill_db: ( Lv10: 94 } Items: { - Lime_Green_Pts: 1 - Lime_Green_Pts: 2 - Lime_Green_Pts: 3 + Lime_Green_Pts: { + Lv1: 1 + Lv2: 2 + Lv3: 3 + } } } Unit: { @@ -30363,11 +30421,21 @@ skill_db: ( Lv10: 75 } Items: { - Oil_Bottle: 1 - Explosive_Powder: 1 - Smoke_Powder: 1 - Tear_Gas: 1 - Acid_Bottle: 1 + Oil_Bottle: { + Lv1: 1 + } + Explosive_Powder: { + Lv2: 1 + } + Smoke_Powder: { + Lv3: 1 + } + Tear_Gas: { + Lv4: 1 + } + Acid_Bottle: { + Lv5: 1 + } } } }, diff --git a/src/map/skill.c b/src/map/skill.c index 09165506f..0e90cdbc2 100644 --- a/src/map/skill.c +++ b/src/map/skill.c @@ -15640,23 +15640,11 @@ static struct skill_condition skill_get_requirement(struct map_session_data *sd, } for( i = 0; i < MAX_SKILL_ITEM_REQUIRE; i++ ) { - int item_idx = (skill_lv - 1) % MAX_SKILL_ITEM_REQUIRE; - if ((skill_id == AM_POTIONPITCHER || skill_id == CR_SLIMPITCHER || skill_id == CR_CULTIVATION) && i != item_idx) - continue; - switch( skill_id ) { case AM_CALLHOMUN: if (sd->status.hom_id) //Don't delete items when hom is already out. continue; break; - case NC_SHAPESHIFT: - if( i < 4 ) - continue; - break; - case WZ_FIREPILLAR: // celest - if (skill_lv <= 5) // no gems required at level 1-5 - continue; - break; case AB_ADORAMUS: if (itemid_isgemstone(skill->get_itemid(skill_id, i)) && skill->check_pc_partner(sd, skill_id, &skill_lv, 1, 2) != 0) continue; @@ -15665,21 +15653,6 @@ static struct skill_condition skill_get_requirement(struct map_session_data *sd, if (itemid_isgemstone(skill->get_itemid(skill_id, i)) && skill->check_pc_partner(sd, skill_id, &skill_lv, 1, 0) != 0) continue; break; - case GN_FIRE_EXPANSION: - if( i < 5 ) - continue; - break; - case SO_SUMMON_AGNI: - case SO_SUMMON_AQUA: - case SO_SUMMON_VENTUS: - case SO_SUMMON_TERA: - case SO_WATER_INSIGNIA: - case SO_FIRE_INSIGNIA: - case SO_WIND_INSIGNIA: - case SO_EARTH_INSIGNIA: - if( i < 3 ) - continue; - break; default: { if (skill->get_requirement_item_unknown(sc, sd, &skill_id, &skill_lv, &idx, &i)) @@ -15725,39 +15698,6 @@ static struct skill_condition skill_get_requirement(struct map_session_data *sd, } } - /* requirements are level-dependent */ - switch( skill_id ) { - case NC_SHAPESHIFT: - case GN_FIRE_EXPANSION: - case SO_SUMMON_AGNI: - case SO_SUMMON_AQUA: - case SO_SUMMON_VENTUS: - case SO_SUMMON_TERA: - case SO_WATER_INSIGNIA: - case SO_FIRE_INSIGNIA: - case SO_WIND_INSIGNIA: - case SO_EARTH_INSIGNIA: - req.itemid[skill_lv - 1] = skill->get_itemid(skill_id, skill_lv - 1); - req.amount[skill_lv - 1] = skill->get_itemqty(skill_id, skill_lv - 1, skill_lv); - break; - } - if (skill_id == NC_REPAIR) { - switch(skill_lv) { - case 1: - case 2: - req.itemid[1] = ITEMID_REPAIRA; - break; - case 3: - case 4: - req.itemid[1] = ITEMID_REPAIRB; - break; - case 5: - req.itemid[1] = ITEMID_REPAIRC; - break; - } - req.amount[1] = 1; - } - // Check for cost reductions due to skills & SCs switch(skill_id) { case MC_MAMMONITE: -- cgit v1.2.3-60-g2f50 From e776d742cceae6d0f104692697c03f61d50fcf37 Mon Sep 17 00:00:00 2001 From: Kenpachi Developer Date: Sun, 17 May 2020 20:39:18 +0200 Subject: Implement Requirements->Items->Any code --- src/map/skill.c | 280 +++++++++++++++++++++++++++++++++++++++++++------------- src/map/skill.h | 4 + 2 files changed, 221 insertions(+), 63 deletions(-) diff --git a/src/map/skill.c b/src/map/skill.c index 0e90cdbc2..27598f88b 100644 --- a/src/map/skill.c +++ b/src/map/skill.c @@ -415,6 +415,28 @@ static int skill_get_itemqty(int skill_id, int item_idx, int skill_lv) return skill->dbs->db[idx].req_items.item[item_idx].amount[skill_get_lvl_idx(skill_lv)]; } +/** + * Gets a skill's required items any-flag by the skill's ID and level. + * + * @param skill_id The skill's ID. + * @param skill_lv The skill's level. + * @return The skill's required items any-flag corresponding to the passed level. Defaults to false in case of error. + * + **/ +static bool skill_get_item_any_flag(int skill_id, int skill_lv) +{ + if (skill_id == 0) + return false; + + Assert_retr(false, skill_lv > 0); + + int idx = skill->get_index(skill_id); + + Assert_retr(false, idx != 0); + + return skill->dbs->db[idx].req_items.any[skill_get_lvl_idx(skill_lv)]; +} + static int skill_get_zeny(int skill_id, int skill_lv) { int idx; @@ -3906,9 +3928,9 @@ static int skill_check_condition_mercenary(struct block_list *bl, int skill_id, { struct status_data *st; struct map_session_data *sd = NULL; - int i, hp, sp, hp_rate, sp_rate, state, mhp; + int hp, sp, hp_rate, sp_rate, state, mhp; int idx; - int itemid[MAX_SKILL_ITEM_REQUIRE], amount[MAX_SKILL_ITEM_REQUIRE], index[MAX_SKILL_ITEM_REQUIRE]; + int itemid[MAX_SKILL_ITEM_REQUIRE], amount[MAX_SKILL_ITEM_REQUIRE]; if( lv < 1 || lv > MAX_SKILL_LEVEL ) return 0; @@ -3924,7 +3946,7 @@ static int skill_check_condition_mercenary(struct block_list *bl, int skill_id, return 0; // Requirements - for (i = 0; i < MAX_SKILL_ITEM_REQUIRE; i++) { + for (int i = 0; i < MAX_SKILL_ITEM_REQUIRE; i++) { itemid[i] = skill->get_itemid(skill_id, i); amount[i] = skill->get_itemqty(skill_id, i, lv); } @@ -3981,21 +4003,27 @@ static int skill_check_condition_mercenary(struct block_list *bl, int skill_id, if( !(type&1) ) return 1; - // Check item existences - for (i = 0; i < ARRAYLENGTH(itemid); i++) { - index[i] = INDEX_NOT_FOUND; - if (itemid[i] < 1) continue; // No item - index[i] = pc->search_inventory(sd, itemid[i]); - if (index[i] == INDEX_NOT_FOUND || sd->status.inventory[index[i]].amount < amount[i]) { - clif->skill_fail(sd, skill_id, USESKILL_FAIL_NEED_ITEM, amount[i], itemid[i]); - return 0; - } - } + bool items_required = skill->items_required(sd, skill_id, lv); + + if (items_required && skill->check_condition_required_items(sd, skill_id, lv) != 0) + return 0; + + int any_item_index = INDEX_NOT_FOUND; - // Consume items - for (i = 0; i < ARRAYLENGTH(itemid); i++) { - if (index[i] != INDEX_NOT_FOUND) - pc->delitem(sd, index[i], amount[i], 0, DELITEM_SKILLUSE, LOG_TYPE_CONSUME); + if (items_required) + any_item_index = skill->get_any_item_index(sd, skill_id, lv); + + for (int i = 0; i < MAX_SKILL_ITEM_REQUIRE && items_required; i++) { + if (itemid[i] == 0) + continue; + + if (any_item_index != INDEX_NOT_FOUND && any_item_index != i) + continue; + + int inventory_index = pc->search_inventory(sd, itemid[i]); + + if (inventory_index != INDEX_NOT_FOUND) + pc->delitem(sd, inventory_index, amount[i], 0, DELITEM_SKILLUSE, LOG_TYPE_CONSUME); } if( type&2 ) @@ -15216,12 +15244,127 @@ static int skill_check_condition_castbegin_unknown(struct status_change *sc, uin return -1; } +/** + * Checks if a skill's item requirements are fulfilled. + * + * @param sd The character who casts the skill. + * @param skill_id The skill's ID. + * @param skill_lv The skill's level. + * @return 0 on success or 1 in case of error. + * + **/ +static int skill_check_condition_required_items(struct map_session_data *sd, int skill_id, int skill_lv) +{ + nullpo_retr(1, sd); + + struct skill_condition req = skill->get_requirement(sd, skill_id, skill_lv); + + if (skill->get_item_any_flag(skill_id, skill_lv)) { + for (int i = 0; i < MAX_SKILL_ITEM_REQUIRE; i++) { + if (req.itemid[i] == 0) + continue; + + int inv_idx = pc->search_inventory(sd, req.itemid[i]); + + if (inv_idx == INDEX_NOT_FOUND) + continue; + + if ((req.amount[i] > 0 && sd->status.inventory[inv_idx].amount >= req.amount[i]) + || (req.amount[i] == 0 && sd->status.inventory[inv_idx].amount > 0)) { + return 0; + } + } + } + + /** + * Find first missing item and show skill failed message if item any-flag is false + * or item any-flag check didn't find an item with sufficient amount. + * + **/ + for (int i = 0; i < MAX_SKILL_ITEM_REQUIRE; i++) { + if (req.itemid[i] == 0) + continue; + + int inv_idx = pc->search_inventory(sd, req.itemid[i]); + + if (inv_idx == INDEX_NOT_FOUND || sd->status.inventory[inv_idx].amount < req.amount[i]) { + useskill_fail_cause cause = USESKILL_FAIL_NEED_ITEM; + + switch (skill_id) { + case NC_SILVERSNIPER: + case NC_MAGICDECOY: + cause = USESKILL_FAIL_STUFF_INSUFFICIENT; + break; + default: + switch (req.itemid[i]) { + case ITEMID_RED_GEMSTONE: + cause = USESKILL_FAIL_REDJAMSTONE; + break; + case ITEMID_BLUE_GEMSTONE: + cause = USESKILL_FAIL_BLUEJAMSTONE; + break; + case ITEMID_HOLY_WATER: + cause = USESKILL_FAIL_HOLYWATER; + break; + case ITEMID_ANSILA: + cause = USESKILL_FAIL_ANCILLA; + break; + case ITEMID_ACCELERATOR: + case ITEMID_HOVERING_BOOSTER: + case ITEMID_SUICIDAL_DEVICE: + case ITEMID_SHAPE_SHIFTER: + case ITEMID_COOLING_DEVICE: + case ITEMID_MAGNETIC_FIELD_GENERATOR: + case ITEMID_BARRIER_BUILDER: + case ITEMID_CAMOUFLAGE_GENERATOR: + case ITEMID_REPAIR_KIT: + case ITEMID_MONKEY_SPANNER: + cause = USESKILL_FAIL_NEED_EQUIPMENT; + FALLTHROUGH + default: + clif->skill_fail(sd, skill_id, cause, max(1, req.amount[i]), req.itemid[i]); + return 1; + } + + break; + } + + clif->skill_fail(sd, skill_id, cause, 0, 0); + return 1; + } + } + + return 0; +} + +/** + * Checks if a skill has item requirements. + * + * @param sd The character who casts the skill. + * @param skill_id The skill's ID. + * @param skill_lv The skill's level. + * @return True if skill has item requirements, otherwise false. + * + **/ +static bool skill_items_required(struct map_session_data *sd, int skill_id, int skill_lv) +{ + nullpo_retr(false, sd); + + struct skill_condition req = skill->get_requirement(sd, skill_id, skill_lv); + + for (int i = 0; i < MAX_SKILL_ITEM_REQUIRE; i++) { + if (req.itemid[i] != 0) + return true; + } + + return false; +} + static int skill_check_condition_castend(struct map_session_data *sd, uint16 skill_id, uint16 skill_lv) { struct skill_condition require; struct status_data *st; int i; - int index[MAX_SKILL_ITEM_REQUIRE]; nullpo_ret(sd); @@ -15373,48 +15516,10 @@ static int skill_check_condition_castend(struct map_session_data *sd, uint16 ski } } - for( i = 0; i < MAX_SKILL_ITEM_REQUIRE; ++i ) { - if( !require.itemid[i] ) - continue; - index[i] = pc->search_inventory(sd,require.itemid[i]); - if (index[i] == INDEX_NOT_FOUND || sd->status.inventory[index[i]].amount < require.amount[i]) { - useskill_fail_cause cause = USESKILL_FAIL_NEED_ITEM; - switch( skill_id ){ - case NC_SILVERSNIPER: - case NC_MAGICDECOY: - cause = USESKILL_FAIL_STUFF_INSUFFICIENT; - break; - default: - switch(require.itemid[i]){ - case ITEMID_RED_GEMSTONE: - cause = USESKILL_FAIL_REDJAMSTONE; break; - case ITEMID_BLUE_GEMSTONE: - cause = USESKILL_FAIL_BLUEJAMSTONE; break; - case ITEMID_HOLY_WATER: - cause = USESKILL_FAIL_HOLYWATER; break; - case ITEMID_ANSILA: - cause = USESKILL_FAIL_ANCILLA; break; - case ITEMID_ACCELERATOR: - case ITEMID_HOVERING_BOOSTER: - case ITEMID_SUICIDAL_DEVICE: - case ITEMID_SHAPE_SHIFTER: - case ITEMID_COOLING_DEVICE: - case ITEMID_MAGNETIC_FIELD_GENERATOR: - case ITEMID_BARRIER_BUILDER: - case ITEMID_CAMOUFLAGE_GENERATOR: - case ITEMID_REPAIR_KIT: - case ITEMID_MONKEY_SPANNER: - cause = USESKILL_FAIL_NEED_EQUIPMENT; - /* Fall through */ - default: - clif->skill_fail(sd, skill_id, cause, max(1, require.amount[i]), require.itemid[i]); - return 0; - } - } - clif->skill_fail(sd, skill_id, cause, 0, 0); - return 0; - } - } + bool items_required = skill->items_required(sd, skill_id, skill_lv); + + if (items_required && skill->check_condition_required_items(sd, skill_id, skill_lv) != 0) + return 0; return 1; } @@ -15424,6 +15529,43 @@ static bool skill_check_condition_castend_unknown(struct map_session_data *sd, u return false; } +/** + * Gets the array index of the first required item with sufficient amount. + * + * @param sd The character who casts the skill. + * @param skill_id The skill's ID. + * @param skill_lv The skill's level. + * @return A number greater than or equal to 0 on success, otherwise INDEX_NOT_FOUND (-1). + * + **/ +static int skill_get_any_item_index(struct map_session_data *sd, int skill_id, int skill_lv) +{ + nullpo_retr(INDEX_NOT_FOUND, sd); + + int any_item_index = INDEX_NOT_FOUND; + + if (skill->get_item_any_flag(skill_id, skill_lv)) { + struct skill_condition req = skill->get_requirement(sd, skill_id, skill_lv); + + for (int i = 0; i < MAX_SKILL_ITEM_REQUIRE; i++) { + if (req.itemid[i] == 0) + continue; + + int inv_idx = pc->search_inventory(sd, req.itemid[i]); + + if (inv_idx == INDEX_NOT_FOUND) + continue; + + if (req.amount[i] == 0 || sd->status.inventory[inv_idx].amount >= req.amount[i]) { + any_item_index = i; + break; + } + } + } + + return any_item_index; +} + // type&2: consume items (after skill was used) // type&1: consume the others (before skill was used) static int skill_consume_requirement(struct map_session_data *sd, uint16 skill_id, uint16 skill_lv, short type) @@ -15476,16 +15618,24 @@ static int skill_consume_requirement(struct map_session_data *sd, uint16 skill_i if( type&2 ) { struct status_change *sc = &sd->sc; - int n,i; + int n; if( !sc->count ) sc = NULL; - for( i = 0; i < MAX_SKILL_ITEM_REQUIRE; ++i ) - { + bool items_required = skill->items_required(sd, skill_id, skill_lv); + int any_item_index = INDEX_NOT_FOUND; + + if (items_required) + any_item_index = skill->get_any_item_index(sd, skill_id, skill_lv); + + for (int i = 0; i < MAX_SKILL_ITEM_REQUIRE && items_required; i++) { if( !req.itemid[i] ) continue; + if (any_item_index != INDEX_NOT_FOUND && any_item_index != i) + continue; + if( itemid_isgemstone(req.itemid[i]) && skill_id != HW_GANBANTEIN && sc && sc->data[SC_SOULLINK] && sc->data[SC_SOULLINK]->val2 == SL_WIZARD ) continue; //Gemstones are checked, but not subtracted from inventory. @@ -23386,6 +23536,7 @@ void skill_defaults(void) skill->get_spiritball = skill_get_spiritball; skill->get_itemid = skill_get_itemid; skill->get_itemqty = skill_get_itemqty; + skill->get_item_any_flag = skill_get_item_any_flag; skill->get_zeny = skill_get_zeny; skill->get_num = skill_get_num; skill->get_cast = skill_get_cast; @@ -23445,7 +23596,10 @@ void skill_defaults(void) skill->vf_cast_fix = skill_vfcastfix; skill->delay_fix = skill_delay_fix; skill->check_condition_castbegin = skill_check_condition_castbegin; + skill->check_condition_required_items = skill_check_condition_required_items; + skill->items_required = skill_items_required; skill->check_condition_castend = skill_check_condition_castend; + skill->get_any_item_index = skill_get_any_item_index; skill->consume_requirement = skill_consume_requirement; skill->get_requirement = skill_get_requirement; skill->check_pc_partner = skill_check_pc_partner; diff --git a/src/map/skill.h b/src/map/skill.h index 374b76b7f..dc3de84fe 100644 --- a/src/map/skill.h +++ b/src/map/skill.h @@ -1996,6 +1996,7 @@ struct skill_interface { int (*get_spiritball) (int skill_id, int skill_lv); int (*get_itemid) (int skill_id, int item_idx); int (*get_itemqty) (int skill_id, int item_idx, int skill_lv); + bool (*get_item_any_flag) (int skill_id, int skill_lv); int (*get_zeny) (int skill_id, int skill_lv); int (*get_num) (int skill_id, int skill_lv); int (*get_cast) (int skill_id, int skill_lv); @@ -2055,7 +2056,10 @@ struct skill_interface { int (*vf_cast_fix) ( struct block_list *bl, double time, uint16 skill_id, uint16 skill_lv); int (*delay_fix) ( struct block_list *bl, uint16 skill_id, uint16 skill_lv); int (*check_condition_castbegin) (struct map_session_data *sd, uint16 skill_id, uint16 skill_lv); + int (*check_condition_required_items) (struct map_session_data *sd, int skill_id, int skill_lv); + bool (*items_required) (struct map_session_data *sd, int skill_id, int skill_lv); int (*check_condition_castend) (struct map_session_data *sd, uint16 skill_id, uint16 skill_lv); + int (*get_any_item_index) (struct map_session_data *sd, int skill_id, int skill_lv); int (*consume_requirement) (struct map_session_data *sd, uint16 skill_id, uint16 skill_lv, short type); struct skill_condition (*get_requirement) (struct map_session_data *sd, uint16 skill_id, uint16 skill_lv); int (*check_pc_partner) (struct map_session_data *sd, uint16 skill_id, uint16* skill_lv, int range, int cast_flag); -- cgit v1.2.3-60-g2f50 From 3fd249c757242ec20f44427391496a9e9397407a Mon Sep 17 00:00:00 2001 From: Kenpachi Developer Date: Thu, 21 May 2020 10:57:55 +0200 Subject: Implement Requirements->Equip code --- src/map/skill.c | 341 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/map/skill.h | 11 ++ 2 files changed, 352 insertions(+) diff --git a/src/map/skill.c b/src/map/skill.c index 27598f88b..383db09c9 100644 --- a/src/map/skill.c +++ b/src/map/skill.c @@ -437,6 +437,74 @@ static bool skill_get_item_any_flag(int skill_id, int skill_lv) return skill->dbs->db[idx].req_items.any[skill_get_lvl_idx(skill_lv)]; } +/** + * Gets a skill's required equipment's ID by the skill's ID and the equipment item's index. + * + * @param skill_id The skill's ID. + * @param item_idx The equipment item's index. + * @return The skill's required equipment's ID corresponding to the passed index. Defaults to 0 in case of error. + * + **/ +static int skill_get_equip_id(int skill_id, int item_idx) +{ + if (skill_id == 0) + return 0; + + Assert_ret(item_idx >= 0 && item_idx < MAX_SKILL_ITEM_REQUIRE); + + int idx = skill->get_index(skill_id); + + Assert_ret(idx != 0); + + return skill->dbs->db[idx].req_equip.item[item_idx].id; +} + +/** + * Gets a skill's required equipment's amount by the skill's ID and level and the equipment item's index. + * + * @param skill_id The skill's ID. + * @param item_idx The equipment item's index. + * @param skill_lv The skill's level. + * @return The skill's required equipment item's amount corresponding to the passed index and level. Defaults to 0 in case of error. + * + **/ +static int skill_get_equip_amount(int skill_id, int item_idx, int skill_lv) +{ + if (skill_id == 0) + return 0; + + Assert_ret(item_idx >= 0 && item_idx < MAX_SKILL_ITEM_REQUIRE); + Assert_ret(skill_lv > 0); + + int idx = skill->get_index(skill_id); + + Assert_ret(idx != 0); + + return skill->dbs->db[idx].req_equip.item[item_idx].amount[skill_get_lvl_idx(skill_lv)]; +} + +/** + * Gets a skill's required equipment any-flag by the skill's ID and level. + * + * @param skill_id The skill's ID. + * @param skill_lv The skill's level. + * @return The skill's required equipment's any-flag corresponding to the passed level. Defaults to false in case of error. + * + **/ +static bool skill_get_equip_any_flag(int skill_id, int skill_lv) +{ + if (skill_id == 0) + return false; + + Assert_retr(false, skill_lv > 0); + + int idx = skill->get_index(skill_id); + + Assert_retr(false, idx != 0); + + return skill->dbs->db[idx].req_equip.any[skill_get_lvl_idx(skill_lv)]; +} + static int skill_get_zeny(int skill_id, int skill_lv) { int idx; @@ -4026,6 +4094,9 @@ static int skill_check_condition_mercenary(struct block_list *bl, int skill_id, pc->delitem(sd, inventory_index, amount[i], 0, DELITEM_SKILLUSE, LOG_TYPE_CONSUME); } + if (skill->check_condition_required_equip(sd, skill_id, lv) != 0) + return 0; + if( type&2 ) return 1; @@ -14276,6 +14347,90 @@ static bool skill_is_combo(int skill_id) return false; } +/** + * Checks if a skill's equipment requirements are fulfilled. + * + * @param sd The character who casts the skill. + * @param skill_id The skill's ID. + * @param skill_lv The skill's level. + * @return 0 on success or 1 in case of error. + * + **/ +static int skill_check_condition_required_equip(struct map_session_data *sd, int skill_id, int skill_lv) +{ + nullpo_retr(1, sd); + + struct skill_condition req = skill->get_requirement(sd, skill_id, skill_lv); + bool any_equip_flag = skill->get_equip_any_flag(skill_id, skill_lv); + bool any_equip_found = false; + int fail_id = 0; + int fail_amount = 0; + + for (int i = 0; i < MAX_SKILL_ITEM_REQUIRE; i++) { + if (req.equip_id[i] == 0) + continue; + + int req_id = req.equip_id[i]; + int req_amount = req.equip_amount[i]; + int found_amount = 0; + + for (int j = 0; j < EQI_MAX; j++) { + int inv_idx = sd->equip_index[j]; + + if (inv_idx == INDEX_NOT_FOUND || sd->inventory_data[inv_idx] == NULL) + continue; + + if ((j == EQI_HAND_R && sd->equip_index[EQI_HAND_L] == inv_idx) + || (j == EQI_HEAD_MID && sd->equip_index[EQI_HEAD_LOW] == inv_idx) + || (j == EQI_HEAD_TOP && sd->equip_index[EQI_HEAD_MID] == inv_idx) + || (j == EQI_HEAD_TOP && sd->equip_index[EQI_HEAD_LOW] == inv_idx) + || (j == EQI_COSTUME_MID && sd->equip_index[EQI_COSTUME_LOW] == inv_idx) + || (j == EQI_COSTUME_TOP && sd->equip_index[EQI_COSTUME_MID] == inv_idx) + || (j == EQI_COSTUME_TOP && sd->equip_index[EQI_COSTUME_LOW] == inv_idx)) { + continue; // Equipment uses more than one slot; only process it once! + } + + if (itemdb_type(req_id) != IT_CARD) { + if (sd->inventory_data[inv_idx]->nameid != req_id) + continue; + + if (itemdb_type(req_id) == IT_AMMO) + found_amount += sd->status.inventory[inv_idx].amount; + else + found_amount++; + } else { + if (itemdb_isspecial(sd->status.inventory[inv_idx].card[0])) + continue; + + for (int k = 0; k < sd->inventory_data[inv_idx]->slot; k++) { + if (sd->status.inventory[inv_idx].card[k] == req_id) + found_amount++; + } + } + } + + if (any_equip_flag) { + if (found_amount >= req_amount) { + any_equip_found = true; + break; + } else if (fail_id == 0) { // Save ID/amount of first missing equipment for skill fail message. + fail_id = req_id; + fail_amount = req_amount; + } + } else if (found_amount < req_amount) { + clif->skill_fail(sd, skill_id, USESKILL_FAIL_NEED_EQUIPMENT, req_amount, req_id); + return 1; + } + } + + if (any_equip_flag && !any_equip_found) { + clif->skill_fail(sd, skill_id, USESKILL_FAIL_NEED_EQUIPMENT, fail_amount, fail_id); + return 1; + } + + return 0; +} + static int skill_check_condition_castbegin(struct map_session_data *sd, uint16 skill_id, uint16 skill_lv) { struct status_data *st; @@ -15198,6 +15353,9 @@ static int skill_check_condition_castbegin(struct map_session_data *sd, uint16 s return 0; } + if (skill->check_condition_required_equip(sd, skill_id, skill_lv) != 0) + return 0; + if (require.sp > 0 && st->sp < (unsigned int)require.sp && sd->auto_cast_current.type == AUTOCAST_NONE) { // Auto-cast skills don't consume SP. clif->skill_fail(sd, skill_id, USESKILL_FAIL_SP_INSUFFICIENT, 0, 0); return 0; @@ -15818,6 +15976,11 @@ static struct skill_condition skill_get_requirement(struct map_session_data *sd, req.amount[i] = amount; } + if ((amount = skill->get_equip_amount(skill_id, i, skill_lv)) > 0) { + req.equip_id[i] = skill->get_equip_id(skill_id, i); + req.equip_amount[i] = amount; + } + if (itemid_isgemstone(req.itemid[i]) && skill_id != HW_GANBANTEIN) { if (sd->special_state.no_gemstone) { // All gem skills except Hocus Pocus and Ganbantein can cast for free with Mistress card [helvetica] @@ -22620,6 +22783,175 @@ static void skill_validate_item_requirements(struct config_setting_t *conf, stru } } +/** + * Validates a skill's required equipment amounts when reading the skill DB. + * + * @param conf The libconfig settings block which contains the skill's data. + * @param sk The s_skill_db struct where the required equipment amounts should be set it. + * + **/ +static void skill_validate_equip_requirements_sub_item_amount(struct config_setting_t *conf, struct s_skill_db *sk, int item_index) +{ + nullpo_retv(conf); + nullpo_retv(sk); + + for (int i = 0; i < MAX_SKILL_LEVEL; i++) + sk->req_equip.item[item_index].amount[i] = 0; + + if (config_setting_is_group(conf)) { + for (int i = 0; i < MAX_SKILL_LEVEL; i++) { + char lv[6]; // Big enough to contain "Lv999" in case of custom MAX_SKILL_LEVEL. + safesnprintf(lv, sizeof(lv), "Lv%d", i + 1); + int amount; + + if (libconfig->setting_lookup_int(conf, lv, &amount) == CONFIG_TRUE) { + if (amount > 0) { + sk->req_equip.item[item_index].amount[i] = amount; + } else { + ShowWarning("%s: Invalid required equipment amount %d specified in level %d for skill ID %d in %s! Must be greater than 0. Defaulting to 1...\n", + __func__, amount, i + 1, sk->nameid, conf->file); + sk->req_equip.item[item_index].amount[i] = 1; + } + } + } + + return; + } + + int amount = libconfig->setting_get_int(conf); + + if (amount <= 0) { + ShowWarning("%s: Invalid required equipment amount %d specified for skill ID %d in %s! Must be greater than 0. Defaulting to 1...\n", + __func__, amount, sk->nameid, conf->file); + amount = 1; + } + + for (int i = 0; i < MAX_SKILL_LEVEL; i++) + sk->req_equip.item[item_index].amount[i] = amount; +} + +/** + * Validates a skill's required equipment when reading the skill DB. + * + * @param conf The libconfig settings block which contains the skill's data. + * @param sk The s_skill_db struct where the required equipment should be set it. + * + **/ +static void skill_validate_equip_requirements_sub_items(struct config_setting_t *conf, struct s_skill_db *sk) +{ + nullpo_retv(conf); + nullpo_retv(sk); + + for (int i = 0; i < MAX_SKILL_ITEM_REQUIRE; i++) { + sk->req_equip.item[i].id = 0; + + for (int j = 0; j < MAX_SKILL_LEVEL; j++) + sk->req_equip.item[i].amount[j] = 0; + } + + int item_index = 0; + int count = libconfig->setting_length(conf); + + for (int i = 0; i < count; i++) { + struct config_setting_t *t = libconfig->setting_get_elem(conf, i); + + if (t != NULL && strcasecmp(config_setting_name(t), "Any") != 0) { + if (item_index >= MAX_SKILL_ITEM_REQUIRE) { + ShowWarning("%s: Too many required equipment items specified for skill ID %d in %s! Skipping item %s...\n", + __func__, sk->nameid, conf->file, config_setting_name(t)); + continue; + } + + int item_id = skill->validate_requirements_item_name(config_setting_name(t)); + struct item_data *it = itemdb->exists(item_id); + + if (item_id == 0 || it == NULL) { + ShowWarning("%s: Invalid required equipment item %s specified for skill ID %d in %s! Skipping item...\n", + __func__, config_setting_name(t), sk->nameid, conf->file); + continue; + } + + if (it->type != IT_WEAPON && it->type != IT_AMMO && it->type != IT_ARMOR && it->type != IT_CARD) { + ShowWarning("%s: Non-equipment item %s specified for skill ID %d in %s! Skipping item...\n", + __func__, config_setting_name(t), sk->nameid, conf->file); + continue; + } + + int j; + + ARR_FIND(0, MAX_SKILL_ITEM_REQUIRE, j, sk->req_equip.item[j].id == item_id); + + if (j < MAX_SKILL_ITEM_REQUIRE) { + ShowWarning("%s: Duplicate required equipment item %s specified for skill ID %d in %s! Skipping item...\n", + __func__, config_setting_name(t), sk->nameid, conf->file); + continue; + } + + sk->req_equip.item[item_index].id = item_id; + skill->validate_equip_requirements_sub_item_amount(t, sk, item_index); + item_index++; + } + } +} + +/** + * Validates a skill's required equipment any-flag when reading the skill DB. + * + * @param conf The libconfig settings block which contains the skill's data. + * @param sk The s_skill_db struct where the required equipment any-flag should be set it. + * + **/ +static void skill_validate_equip_requirements_sub_any_flag(struct config_setting_t *conf, struct s_skill_db *sk) +{ + nullpo_retv(conf); + nullpo_retv(sk); + + for (int i = 0; i < MAX_SKILL_LEVEL; i++) + sk->req_equip.any[i] = false; + + struct config_setting_t *t = libconfig->setting_get_member(conf, "Any"); + + if (t != NULL && config_setting_is_group(t)) { + for (int i = 0; i < MAX_SKILL_LEVEL; i++) { + char lv[6]; // Big enough to contain "Lv999" in case of custom MAX_SKILL_LEVEL. + safesnprintf(lv, sizeof(lv), "Lv%d", i + 1); + int any_flag; + + if (libconfig->setting_lookup_bool(t, lv, &any_flag) == CONFIG_TRUE) + sk->req_equip.any[i] = (any_flag != 0); + } + + return; + } + + int any_flag; + + if (libconfig->setting_lookup_bool(conf, "Any", &any_flag) == CONFIG_TRUE && any_flag != 0) { + for (int i = 0; i < MAX_SKILL_LEVEL; i++) + sk->req_equip.any[i] = true; + } +} + +/** + * Validates a skill's required equipment when reading the skill DB. + * + * @param conf The libconfig settings block which contains the skill's data. + * @param sk The s_skill_db struct where the required equipment should be set it. + * + **/ +static void skill_validate_equip_requirements(struct config_setting_t *conf, struct s_skill_db *sk) +{ + nullpo_retv(conf); + nullpo_retv(sk); + + struct config_setting_t *t = libconfig->setting_get_member(conf, "Equip"); + + if (t != NULL && config_setting_is_group(t)) { + skill->validate_equip_requirements_sub_any_flag(t, sk); + skill->validate_equip_requirements_sub_items(t, sk); + } +} + /** * Validates a required item's config setting name when reading the skill DB. * @@ -22678,6 +23010,7 @@ static void skill_validate_requirements(struct config_setting_t *conf, struct s_ skill->validate_state(t, sk); skill->validate_spirit_sphere_cost(t, sk); skill->validate_item_requirements(t, sk); + skill->validate_equip_requirements(t, sk); } } @@ -23537,6 +23870,9 @@ void skill_defaults(void) skill->get_itemid = skill_get_itemid; skill->get_itemqty = skill_get_itemqty; skill->get_item_any_flag = skill_get_item_any_flag; + skill->get_equip_id = skill_get_equip_id; + skill->get_equip_amount = skill_get_equip_amount; + skill->get_equip_any_flag = skill_get_equip_any_flag; skill->get_zeny = skill_get_zeny; skill->get_num = skill_get_num; skill->get_cast = skill_get_cast; @@ -23595,6 +23931,7 @@ void skill_defaults(void) skill->cast_fix_sc = skill_castfix_sc; skill->vf_cast_fix = skill_vfcastfix; skill->delay_fix = skill_delay_fix; + skill->check_condition_required_equip = skill_check_condition_required_equip; skill->check_condition_castbegin = skill_check_condition_castbegin; skill->check_condition_required_items = skill_check_condition_required_items; skill->items_required = skill_items_required; @@ -23728,6 +24065,10 @@ void skill_defaults(void) skill->validate_item_requirements_sub_items = skill_validate_item_requirements_sub_items; skill->validate_item_requirements_sub_any_flag = skill_validate_item_requirements_sub_any_flag; skill->validate_item_requirements = skill_validate_item_requirements; + skill->validate_equip_requirements_sub_item_amount = skill_validate_equip_requirements_sub_item_amount; + skill->validate_equip_requirements_sub_items = skill_validate_equip_requirements_sub_items; + skill->validate_equip_requirements_sub_any_flag = skill_validate_equip_requirements_sub_any_flag; + skill->validate_equip_requirements = skill_validate_equip_requirements; skill->validate_requirements_item_name = skill_validate_requirements_item_name; skill->validate_requirements = skill_validate_requirements; skill->validate_unit_id_sub = skill_validate_unit_id_sub; diff --git a/src/map/skill.h b/src/map/skill.h index dc3de84fe..04a6170dd 100644 --- a/src/map/skill.h +++ b/src/map/skill.h @@ -1753,6 +1753,8 @@ struct skill_required_item_data { struct skill_condition { int weapon,ammo,ammo_qty,hp,sp,zeny,spiritball,mhp,state; int itemid[MAX_SKILL_ITEM_REQUIRE],amount[MAX_SKILL_ITEM_REQUIRE]; + int equip_id[MAX_SKILL_ITEM_REQUIRE]; + int equip_amount[MAX_SKILL_ITEM_REQUIRE]; }; // Database skills @@ -1793,6 +1795,7 @@ struct s_skill_db { int unit_target[MAX_SKILL_LEVEL]; int unit_flag; struct skill_required_item_data req_items; + struct skill_required_item_data req_equip; }; struct s_skill_unit_layout { @@ -1997,6 +2000,9 @@ struct skill_interface { int (*get_itemid) (int skill_id, int item_idx); int (*get_itemqty) (int skill_id, int item_idx, int skill_lv); bool (*get_item_any_flag) (int skill_id, int skill_lv); + int (*get_equip_id) (int skill_id, int item_idx); + int (*get_equip_amount) (int skill_id, int item_idx, int skill_lv); + bool (*get_equip_any_flag) (int skill_id, int skill_lv); int (*get_zeny) (int skill_id, int skill_lv); int (*get_num) (int skill_id, int skill_lv); int (*get_cast) (int skill_id, int skill_lv); @@ -2055,6 +2061,7 @@ struct skill_interface { int (*cast_fix_sc) ( struct block_list *bl, int time); int (*vf_cast_fix) ( struct block_list *bl, double time, uint16 skill_id, uint16 skill_lv); int (*delay_fix) ( struct block_list *bl, uint16 skill_id, uint16 skill_lv); + int (*check_condition_required_equip) (struct map_session_data *sd, int skill_id, int skill_lv); int (*check_condition_castbegin) (struct map_session_data *sd, uint16 skill_id, uint16 skill_lv); int (*check_condition_required_items) (struct map_session_data *sd, int skill_id, int skill_lv); bool (*items_required) (struct map_session_data *sd, int skill_id, int skill_lv); @@ -2188,6 +2195,10 @@ struct skill_interface { void (*validate_item_requirements_sub_items) (struct config_setting_t *conf, struct s_skill_db *sk); void (*validate_item_requirements_sub_any_flag) (struct config_setting_t *conf, struct s_skill_db *sk); void (*validate_item_requirements) (struct config_setting_t *conf, struct s_skill_db *sk); + void (*validate_equip_requirements_sub_item_amount) (struct config_setting_t *conf, struct s_skill_db *sk, int item_index); + void (*validate_equip_requirements_sub_items) (struct config_setting_t *conf, struct s_skill_db *sk); + void (*validate_equip_requirements_sub_any_flag) (struct config_setting_t *conf, struct s_skill_db *sk); + void (*validate_equip_requirements) (struct config_setting_t *conf, struct s_skill_db *sk); int (*validate_requirements_item_name) (const char *name); void (*validate_requirements) (struct config_setting_t *conf, struct s_skill_db *sk); int (*validate_unit_id_sub) (int unit_id); -- cgit v1.2.3-60-g2f50 From 48582f7e139ead473ee32477d48bfbab8093304c Mon Sep 17 00:00:00 2001 From: Kenpachi Developer Date: Thu, 21 May 2020 11:18:01 +0200 Subject: Remove hard-coded required equipment and update skill DBs accordingly --- db/pre-re/skill_db.conf | 12 +++++++++--- db/re/skill_db.conf | 15 +++++++++------ src/map/itemdb.h | 1 - src/map/skill.c | 16 ---------------- 4 files changed, 18 insertions(+), 26 deletions(-) diff --git a/db/pre-re/skill_db.conf b/db/pre-re/skill_db.conf index 800c21803..d777bc864 100644 --- a/db/pre-re/skill_db.conf +++ b/db/pre-re/skill_db.conf @@ -22155,8 +22155,12 @@ skill_db: ( Requirements: { SPCost: 50 State: "MadoGear" - Items: { - Pilebuncker: 0 + Equip: { + Pilebuncker: 1 + Pilebuncker_S: 1 + Pilebuncker_P: 1 + Pilebuncker_T: 1 + Any: true } } }, @@ -22481,9 +22485,11 @@ skill_db: ( SPCost: 25 State: "MadoGear" Items: { - Hovering_Booster: 0 Magic_Gear_Fuel: 1 } + Equip: { + Hovering_Booster: 1 + } } }, { diff --git a/db/re/skill_db.conf b/db/re/skill_db.conf index 2ff5fe9c0..21bb45f2d 100644 --- a/db/re/skill_db.conf +++ b/db/re/skill_db.conf @@ -22754,11 +22754,12 @@ skill_db: ( Requirements: { SPCost: 50 State: "MadoGear" - Items: { - Pilebuncker: 0 - Pilebuncker_S: 0 - Pilebuncker_P: 0 - Pilebuncker_T: 0 + Equip: { + Pilebuncker: 1 + Pilebuncker_S: 1 + Pilebuncker_P: 1 + Pilebuncker_T: 1 + Any: true } } }, @@ -23094,9 +23095,11 @@ skill_db: ( SPCost: 25 State: "MadoGear" Items: { - Hovering_Booster: 0 Magic_Gear_Fuel: 1 } + Equip: { + Hovering_Booster: 1 + } } }, { diff --git a/src/map/itemdb.h b/src/map/itemdb.h index 5f0790b10..4b06a21d6 100644 --- a/src/map/itemdb.h +++ b/src/map/itemdb.h @@ -606,7 +606,6 @@ struct item_data { #define itemid_isgemstone(n) ((n) >= ITEMID_YELLOW_GEMSTONE && (n) <= ITEMID_BLUE_GEMSTONE) #define itemdb_is_GNbomb(n) ((n) >= ITEMID_APPLE_BOMB && (n) <= ITEMID_VERY_HARD_LUMP) #define itemdb_is_GNthrowable(n) ((n) >= ITEMID_MYSTERIOUS_POWDER && (n) <= ITEMID_BLACK_THING_TO_THROW) -#define itemid_is_pilebunker(n) ((n) == ITEMID_PILEBUNCKER || (n) == ITEMID_PILEBUNCKER_P || (n) == ITEMID_PILEBUNCKER_S || (n) == ITEMID_PILEBUNCKER_T) #define itemdb_is_shadowequip(n) ((n) & (EQP_SHADOW_ARMOR|EQP_SHADOW_WEAPON|EQP_SHADOW_SHIELD|EQP_SHADOW_SHOES|EQP_SHADOW_ACC_R|EQP_SHADOW_ACC_L)) #define itemdb_is_costumeequip(n) ((n) & (EQP_COSTUME_HEAD_TOP|EQP_COSTUME_HEAD_MID|EQP_COSTUME_HEAD_LOW|EQP_COSTUME_GARMENT)) diff --git a/src/map/skill.c b/src/map/skill.c index 383db09c9..86e8b73c2 100644 --- a/src/map/skill.c +++ b/src/map/skill.c @@ -15130,22 +15130,6 @@ static int skill_check_condition_castbegin(struct map_session_data *sd, uint16 s require.sp -= require.sp * 20 * count / 100; // -20% each W/M in the party. } break; - case NC_PILEBUNKER: - if (sd->equip_index[EQI_HAND_R] < 0 - || !itemid_is_pilebunker(sd->status.inventory[sd->equip_index[EQI_HAND_R]].nameid) - ) { - clif->skill_fail(sd, skill_id, USESKILL_FAIL_THIS_WEAPON, 0, 0); - return 0; - } - break; - case NC_HOVERING: - if (( sd->equip_index[EQI_ACC_L] >= 0 && sd->status.inventory[sd->equip_index[EQI_ACC_L]].nameid == ITEMID_HOVERING_BOOSTER ) || - ( sd->equip_index[EQI_ACC_R] >= 0 && sd->status.inventory[sd->equip_index[EQI_ACC_R]].nameid == ITEMID_HOVERING_BOOSTER )); - else { - clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); - return 0; - } - break; case SO_FIREWALK: case SO_ELECTRICWALK: // Can't be casted until you've walked all cells. if( sc && sc->data[SC_PROPERTYWALK] && -- cgit v1.2.3-60-g2f50 From 752b2db9829c1a3182c139bea0527e8486093b3d Mon Sep 17 00:00:00 2001 From: Kenpachi Developer Date: Thu, 21 May 2020 05:27:22 +0200 Subject: Fix Requirements->MaxHPTrigger code in skill_check_condition_mercenary() --- src/map/skill.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/map/skill.c b/src/map/skill.c index 86e8b73c2..e9c658730 100644 --- a/src/map/skill.c +++ b/src/map/skill.c @@ -3996,7 +3996,7 @@ static int skill_check_condition_mercenary(struct block_list *bl, int skill_id, { struct status_data *st; struct map_session_data *sd = NULL; - int hp, sp, hp_rate, sp_rate, state, mhp; + int hp, sp, hp_rate, sp_rate, state; int idx; int itemid[MAX_SKILL_ITEM_REQUIRE], amount[MAX_SKILL_ITEM_REQUIRE]; @@ -4023,8 +4023,7 @@ static int skill_check_condition_mercenary(struct block_list *bl, int skill_id, hp_rate = skill->dbs->db[idx].hp_rate[lv-1]; sp_rate = skill->dbs->db[idx].sp_rate[lv-1]; state = skill->dbs->db[idx].state[lv - 1]; - if( (mhp = skill->dbs->db[idx].mhp[lv-1]) > 0 ) - hp += (st->max_hp * mhp) / 100; + if( hp_rate > 0 ) hp += (st->hp * hp_rate) / 100; else @@ -4057,6 +4056,13 @@ static int skill_check_condition_mercenary(struct block_list *bl, int skill_id, clif->skill_fail(sd, skill_id, USESKILL_FAIL_SP_INSUFFICIENT, 0, 0); return 0; } + + int mhp = skill->get_mhp(skill_id, lv); + + if (mhp > 0 && get_percentage(st->hp, st->max_hp) > mhp) { + clif->skill_fail(sd, skill_id, USESKILL_FAIL_HP_INSUFFICIENT, 0, 0); + return 0; + } } if( !type ) -- cgit v1.2.3-60-g2f50 From 60b6560faee5664d481b4ed0570c1cbbe31e2335 Mon Sep 17 00:00:00 2001 From: Kenpachi Developer Date: Thu, 21 May 2020 06:11:54 +0200 Subject: Implement Requirements->MaxSPTrigger code --- src/map/skill.c | 84 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/map/skill.h | 4 +++ 2 files changed, 88 insertions(+) diff --git a/src/map/skill.c b/src/map/skill.c index e9c658730..1b27fc2ad 100644 --- a/src/map/skill.c +++ b/src/map/skill.c @@ -744,6 +744,28 @@ static int skill_get_mhp(int skill_id, int skill_lv) return skill->dbs->db[idx].mhp[skill_get_lvl_idx(skill_lv)]; } +/** + * Gets a skill's maximum SP trigger by its ID and level. + * + * @param skill_id The skill's ID. + * @param skill_lv The skill's level. + * @return The skill's maximum SP trigger corresponding to the passed level. Defaults to 0 in case of error. + * + **/ +static int skill_get_msp(int skill_id, int skill_lv) +{ + if (skill_id == 0) + return 0; + + Assert_ret(skill_lv > 0); + + int idx = skill->get_index(skill_id); + + Assert_ret(idx != 0); + + return skill->dbs->db[idx].msp[skill_get_lvl_idx(skill_lv)]; +} + static int skill_get_castnodex(int skill_id, int skill_lv) { int idx; @@ -4063,6 +4085,13 @@ static int skill_check_condition_mercenary(struct block_list *bl, int skill_id, clif->skill_fail(sd, skill_id, USESKILL_FAIL_HP_INSUFFICIENT, 0, 0); return 0; } + + int msp = skill->get_msp(skill_id, lv); + + if (msp > 0 && get_percentage(st->sp, st->max_sp) > msp) { + clif->skill_fail(sd, skill_id, USESKILL_FAIL_SP_INSUFFICIENT, 0, 0); + return 0; + } } if( !type ) @@ -15338,6 +15367,11 @@ static int skill_check_condition_castbegin(struct map_session_data *sd, uint16 s return 0; } + if (require.msp > 0 && get_percentage(st->sp, st->max_sp) > require.msp) { + clif->skill_fail(sd, skill_id, USESKILL_FAIL_SP_INSUFFICIENT, 0, 0); + return 0; + } + if( require.weapon && !pc_check_weapontype(sd,require.weapon) ) { clif->skill_fail(sd, skill_id, USESKILL_FAIL_THIS_WEAPON, 0, 0); return 0; @@ -15925,6 +15959,8 @@ static struct skill_condition skill_get_requirement(struct map_session_data *sd, req.mhp = skill->dbs->db[idx].mhp[skill_lv-1]; + req.msp = skill->get_msp(skill_id, skill_lv); + req.weapon = skill->dbs->db[idx].weapon; req.ammo_qty = skill->dbs->db[idx].ammo_qty[skill_lv-1]; @@ -22039,6 +22075,51 @@ static void skill_validate_max_hp_trigger(struct config_setting_t *conf, struct } } +/** + * Validates a skill's maximum SP trigger when reading the skill DB. + * + * @param conf The libconfig settings block which contains the skill's data. + * @param sk The s_skill_db struct where the maximum SP trigger should be set it. + * + **/ +static void skill_validate_max_sp_trigger(struct config_setting_t *conf, struct s_skill_db *sk) +{ + nullpo_retv(conf); + nullpo_retv(sk); + + skill->level_set_value(sk->msp, 0); + + struct config_setting_t *t = libconfig->setting_get_member(conf, "MaxSPTrigger"); + + if (t != NULL && config_setting_is_group(t)) { + for (int i = 0; i < MAX_SKILL_LEVEL; i++) { + char lv[6]; // Big enough to contain "Lv999" in case of custom MAX_SKILL_LEVEL. + safesnprintf(lv, sizeof(lv), "Lv%d", i + 1); + int max_sp_trigger; + + if (libconfig->setting_lookup_int(t, lv, &max_sp_trigger) == CONFIG_TRUE) { + if (max_sp_trigger >= 0 && max_sp_trigger <= 100) + sk->msp[i] = max_sp_trigger; + else + ShowWarning("%s: Invalid maximum SP trigger %d specified in level %d for skill ID %d in %s! Minimum is 0, maximum is 100. Defaulting to 0...\n", + __func__, max_sp_trigger, i + 1, sk->nameid, conf->file); + } + } + + return; + } + + int max_sp_trigger; + + if (libconfig->setting_lookup_int(conf, "MaxSPTrigger", &max_sp_trigger) == CONFIG_TRUE) { + if (max_sp_trigger >= 0 && max_sp_trigger <= 100) + skill->level_set_value(sk->msp, max_sp_trigger); + else + ShowWarning("%s: Invalid maximum SP trigger %d specified for skill ID %d in %s! Minimum is 0, maximum is 100. Defaulting to 0...\n", + __func__, max_sp_trigger, sk->nameid, conf->file); + } +} + /** * Validates a skill's Zeny cost when reading the skill DB. * @@ -22993,6 +23074,7 @@ static void skill_validate_requirements(struct config_setting_t *conf, struct s_ skill->validate_hp_rate_cost(t, sk); skill->validate_sp_rate_cost(t, sk); skill->validate_max_hp_trigger(t, sk); + skill->validate_max_sp_trigger(t, sk); skill->validate_zeny_cost(t, sk); skill->validate_weapontype(t, sk); skill->validate_ammotype(t, sk); @@ -23852,6 +23934,7 @@ void skill_defaults(void) skill->get_splash = skill_get_splash; skill->get_hp = skill_get_hp; skill->get_mhp = skill_get_mhp; + skill->get_msp = skill_get_msp; skill->get_sp = skill_get_sp; skill->get_hp_rate = skill_get_hp_rate; skill->get_sp_rate = skill_get_sp_rate; @@ -24042,6 +24125,7 @@ void skill_defaults(void) skill->validate_hp_rate_cost = skill_validate_hp_rate_cost; skill->validate_sp_rate_cost = skill_validate_sp_rate_cost; skill->validate_max_hp_trigger = skill_validate_max_hp_trigger; + skill->validate_max_sp_trigger = skill_validate_max_sp_trigger; skill->validate_zeny_cost = skill_validate_zeny_cost; skill->validate_weapontype_sub = skill_validate_weapontype_sub; skill->validate_weapontype = skill_validate_weapontype; diff --git a/src/map/skill.h b/src/map/skill.h index 04a6170dd..b9178321d 100644 --- a/src/map/skill.h +++ b/src/map/skill.h @@ -1752,6 +1752,7 @@ struct skill_required_item_data { struct skill_condition { int weapon,ammo,ammo_qty,hp,sp,zeny,spiritball,mhp,state; + int msp; int itemid[MAX_SKILL_ITEM_REQUIRE],amount[MAX_SKILL_ITEM_REQUIRE]; int equip_id[MAX_SKILL_ITEM_REQUIRE]; int equip_amount[MAX_SKILL_ITEM_REQUIRE]; @@ -1782,6 +1783,7 @@ struct s_skill_db { int skill_type[MAX_SKILL_LEVEL]; int blewcount[MAX_SKILL_LEVEL]; int hp[MAX_SKILL_LEVEL],sp[MAX_SKILL_LEVEL],mhp[MAX_SKILL_LEVEL],hp_rate[MAX_SKILL_LEVEL],sp_rate[MAX_SKILL_LEVEL],zeny[MAX_SKILL_LEVEL]; + int msp[MAX_SKILL_LEVEL]; int weapon; int ammo; int ammo_qty[MAX_SKILL_LEVEL]; @@ -1992,6 +1994,7 @@ struct skill_interface { int (*get_splash) (int skill_id, int skill_lv); int (*get_hp) (int skill_id, int skill_lv); int (*get_mhp) (int skill_id, int skill_lv); + int (*get_msp) (int skill_id, int skill_lv); int (*get_sp) (int skill_id, int skill_lv); int (*get_hp_rate) (int skill_id, int skill_lv); int (*get_sp_rate) (int skill_id, int skill_lv); @@ -2182,6 +2185,7 @@ struct skill_interface { void (*validate_hp_rate_cost) (struct config_setting_t *conf, struct s_skill_db *sk); void (*validate_sp_rate_cost) (struct config_setting_t *conf, struct s_skill_db *sk); void (*validate_max_hp_trigger) (struct config_setting_t *conf, struct s_skill_db *sk); + void (*validate_max_sp_trigger) (struct config_setting_t *conf, struct s_skill_db *sk); void (*validate_zeny_cost) (struct config_setting_t *conf, struct s_skill_db *sk); int (*validate_weapontype_sub) (const char *type, bool on, struct s_skill_db *sk); void (*validate_weapontype) (struct config_setting_t *conf, struct s_skill_db *sk); -- cgit v1.2.3-60-g2f50 From 560b765a844c453ce2c7cff24a63735f11dc4f0f Mon Sep 17 00:00:00 2001 From: Kenpachi Developer Date: Thu, 21 May 2020 12:36:15 +0200 Subject: Update entry structure in skill DBs --- db/pre-re/skill_db.conf | 33 +++++++++++++++++++++++---------- db/re/skill_db.conf | 36 +++++++++++++++++++++++++----------- 2 files changed, 48 insertions(+), 21 deletions(-) diff --git a/db/pre-re/skill_db.conf b/db/pre-re/skill_db.conf index d777bc864..446df3c86 100644 --- a/db/pre-re/skill_db.conf +++ b/db/pre-re/skill_db.conf @@ -43,7 +43,7 @@ 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") + Hit: Hit Type (int) (optional, default "BDT_NORMAL") (can be grouped by Levels) Types - "BDT_SKILL", "BDT_MULTIHIT" or "BDT_NORMAL" SkillType: { (bool, defaults to "Passive") Passive: true/false (boolean, defaults to false) @@ -78,7 +78,7 @@ HiddenTrap: true/false (boolean, defaults to false) IsCombo: true/false (boolean, defaults to false) } - AttackType: "Attack Type" (string, defaults to "None") + AttackType: "Attack Type" (string, defaults to "None") (can be grouped by Levels) Types: "None", "Weapon", "Magic" or "Misc" Element: "Element Type" (string) (Optional field - Default "Ele_Neutral") (can be grouped by Levels) @@ -103,8 +103,8 @@ 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 false) - CastDefRate: Cast Defense Reduction (int, defaults to 0) + InterruptCast: Cast Interruption (bool, defaults to false) (can be grouped by Levels) + CastDefRate: Cast Defense Reduction (int, defaults to 0) (can be grouped by Levels) 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 @@ -140,6 +140,8 @@ 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. + MaxHPTrigger: Max HP % trigger (int, defaults to 0) (can be grouped by Levels) + MaxSPTrigger: Max SP % trigger (int, defaults to 0) (can be grouped by Levels) 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) @@ -215,7 +217,12 @@ 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. + Any: true/false (boolean, defaults to false) (can be grouped by Levels) + } + Equip: { + ItemID or Aegis_Name : Amount (int, defaults to 1) (can be grouped by Levels) + Item example: "ID1103" or "Sword__". + Any: true/false (boolean, defaults to false) (can be grouped by Levels) } } Unit: { @@ -223,16 +230,22 @@ 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") + Target: "Unit Target" (string, defaults to "None") (can be grouped by Levels) Types: All - affects everyone NotEnemy - affects anyone who isn't an enemy - Friend - affects party, guildmates and neutral players + NotParty - affects anyone who isn't a party member + NotGuild - affects anyone who isn't a guild member or guild ally + Friend - same as NotEnemy Party - affects party only - Guild - affects guild only - Ally - affects party and guildmates only - Sameguild - affects guild but not allies + Guild - affects guild members and guild allies + Ally - affects party members, guild members and guild allies + Sameguild - affects guild members but not guild allies Enemy - affects enemies only + Self - affects only yourself + GuildAlly - affects guild allies but not guild members + Neutral - affects anyone who isn't a party member, guild member, + guild ally, enemy and not yourself None - affects nobody Flag: { UF_DEFNOTENEMY: true/false (boolean, defaults to false) diff --git a/db/re/skill_db.conf b/db/re/skill_db.conf index 21bb45f2d..ecca6e2e4 100644 --- a/db/re/skill_db.conf +++ b/db/re/skill_db.conf @@ -43,7 +43,7 @@ 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") + Hit: Hit Type (int) (optional, default "BDT_NORMAL") (can be grouped by Levels) Types - "BDT_SKILL", "BDT_MULTIHIT" or "BDT_NORMAL" SkillType: { (bool, defaults to "Passive") Passive: true/false (boolean, defaults to false) @@ -78,7 +78,7 @@ HiddenTrap: true/false (boolean, defaults to false) IsCombo: true/false (boolean, defaults to false) } - AttackType: "Attack Type" (string, defaults to "None") + AttackType: "Attack Type" (string, defaults to "None") (can be grouped by Levels) Types: "None", "Weapon", "Magic" or "Misc" Element: "Element Type" (string) (Optional field - Default "Ele_Neutral") (can be grouped by Levels) @@ -103,8 +103,8 @@ 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 false) - CastDefRate: Cast Defense Reduction (int, defaults to 0) + InterruptCast: Cast Interruption (bool, defaults to false) (can be grouped by Levels) + CastDefRate: Cast Defense Reduction (int, defaults to 0) (can be grouped by Levels) 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 @@ -118,7 +118,8 @@ CoolDown: Skill Cooldown (in ms) (int, defaults to 0) (can be grouped by Levels) 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. + 0 means no fixed cast time. Is only read if + RENEWAL_CAST is defined. CastTimeOptions: { IgnoreDex: true/false (boolean, defaults to false) IgnoreStatusEffect: true/false (boolean, defaults to false) @@ -142,6 +143,8 @@ 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. + MaxHPTrigger: Max HP % trigger (int, defaults to 0) (can be grouped by Levels) + MaxSPTrigger: Max SP % trigger (int, defaults to 0) (can be grouped by Levels) 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) @@ -217,7 +220,12 @@ 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. + Any: true/false (boolean, defaults to false) (can be grouped by Levels) + } + Equip: { + ItemID or Aegis_Name : Amount (int, defaults to 1) (can be grouped by Levels) + Item example: "ID1103" or "Sword__". + Any: true/false (boolean, defaults to false) (can be grouped by Levels) } } Unit: { @@ -225,16 +233,22 @@ 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") + Target: "Unit Target" (string, defaults to "None") (can be grouped by Levels) Types: All - affects everyone NotEnemy - affects anyone who isn't an enemy - Friend - affects party, guildmates and neutral players + NotParty - affects anyone who isn't a party member + NotGuild - affects anyone who isn't a guild member or guild ally + Friend - same as NotEnemy Party - affects party only - Guild - affects guild only - Ally - affects party and guildmates only - Sameguild - affects guild but not allies + Guild - affects guild members and guild allies + Ally - affects party members, guild members and guild allies + Sameguild - affects guild members but not guild allies Enemy - affects enemies only + Self - affects only yourself + GuildAlly - affects guild allies but not guild members + Neutral - affects anyone who isn't a party member, guild member, + guild ally, enemy and not yourself None - affects nobody Flag: { UF_DEFNOTENEMY: true/false (boolean, defaults to false) -- cgit v1.2.3-60-g2f50