diff --git a/db/pre-re/autospell_db.conf b/db/pre-re/autospell_db.conf new file mode 100644 index 00000000000..e5752a82a8c --- /dev/null +++ b/db/pre-re/autospell_db.conf @@ -0,0 +1,135 @@ +//================= Hercules Database ===================================== +//= _ _ _ +//= | | | | | | +//= | |_| | ___ _ __ ___ _ _| | ___ ___ +//= | _ |/ _ \ '__/ __| | | | |/ _ \/ __| +//= | | | | __/ | | (__| |_| | | __/\__ \ +//= \_| |_/\___|_| \___|\__,_|_|\___||___/ +//================= License =============================================== +//= This file is part of Hercules. +//= http://herc.ws - http://github.com/HerculesWS/Hercules +//= +//= Copyright (C) 2023 Hercules Dev Team +//= +//= Hercules is free software: you can redistribute it and/or modify +//= it under the terms of the GNU General Public License as published by +//= the Free Software Foundation, either version 3 of the License, or +//= (at your option) any later version. +//= +//= This program is distributed in the hope that it will be useful, +//= but WITHOUT ANY WARRANTY; without even the implied warranty of +//= MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//= GNU General Public License for more details. +//= +//= You should have received a copy of the GNU General Public License +//= along with this program. If not, see . +//========================================================================= +//= AutoSpell skills configuration +//= +//= This file lists the skills available for Sage AutoSpell (Hindsight) skill. +//= +//= Notes: +//= - The maximum number of entries is controlled by MAX_AUTOSPELL_DB (src/map/skill.h) +//= - Additionally, some official clients have a hard limit on the number of skills +//= (this is limited by packet size and can't be changed) +//= - SkillLevel and SpiritBoost configures the basic behavior of the skills, the requirement +//= for the player to have learned the skill at this level is applied on source +//========================================================================= + +autospell_db: ( +/************************************************************************** + ************* Entry structure ******************************************** + ************************************************************************** +{ + SkillId: Skill ID/Name (constant string or int) + SkillLevel: Highest usable level (int, defaults to 0) (can be grouped by AutoSpell Levels) + (Level 0 would mean not usable in this AutoSpell level) + SpiritBoost: Use max level when under Sage Spirit? (boolean, defaults to false) +}, +**************************************************************************/ + +{ + SkillId: "MG_NAPALMBEAT" + SkillLevel: 3 + SpiritBoost: false +}, +{ + SkillId: "MG_COLDBOLT" + SkillLevel: { + // Lv1: 0 + Lv2: 1 + Lv3: 2 + Lv4: 3 + Lv5: 3 + Lv6: 3 + Lv7: 3 + Lv8: 3 + Lv9: 3 + Lv10: 3 + } + SpiritBoost: true +}, +{ + SkillId: "MG_FIREBOLT" + SkillLevel: { + // Lv1: 0 + Lv2: 1 + Lv3: 2 + Lv4: 3 + Lv5: 3 + Lv6: 3 + Lv7: 3 + Lv8: 3 + Lv9: 3 + Lv10: 3 + } + SpiritBoost: true +}, +{ + SkillId: "MG_LIGHTNINGBOLT" + SkillLevel: { + // Lv1: 0 + Lv2: 1 + Lv3: 2 + Lv4: 3 + Lv5: 3 + Lv6: 3 + Lv7: 3 + Lv8: 3 + Lv9: 3 + Lv10: 3 + } + SpiritBoost: true +}, +{ + SkillId: "MG_SOULSTRIKE" + SkillLevel: { + // Lv1 .. Lv4: 0 + Lv5: 1 + Lv6: 2 + Lv7: 3 + Lv8: 3 + Lv9: 3 + Lv10: 3 + } + SpiritBoost: false +}, +{ + SkillId: "MG_FIREBALL" + SkillLevel: { + // Lv1 .. Lv7: 0 + Lv8: 1 + Lv9: 2 + Lv10: 2 + } + SpiritBoost: false +}, +{ + SkillId: "MG_FROSTDIVER" + SkillLevel: { + // Lv1 .. Lv9: 0 + Lv10: 1 + } + SpiritBoost: false +}, +) diff --git a/db/re/autospell_db.conf b/db/re/autospell_db.conf new file mode 100644 index 00000000000..e5752a82a8c --- /dev/null +++ b/db/re/autospell_db.conf @@ -0,0 +1,135 @@ +//================= Hercules Database ===================================== +//= _ _ _ +//= | | | | | | +//= | |_| | ___ _ __ ___ _ _| | ___ ___ +//= | _ |/ _ \ '__/ __| | | | |/ _ \/ __| +//= | | | | __/ | | (__| |_| | | __/\__ \ +//= \_| |_/\___|_| \___|\__,_|_|\___||___/ +//================= License =============================================== +//= This file is part of Hercules. +//= http://herc.ws - http://github.com/HerculesWS/Hercules +//= +//= Copyright (C) 2023 Hercules Dev Team +//= +//= Hercules is free software: you can redistribute it and/or modify +//= it under the terms of the GNU General Public License as published by +//= the Free Software Foundation, either version 3 of the License, or +//= (at your option) any later version. +//= +//= This program is distributed in the hope that it will be useful, +//= but WITHOUT ANY WARRANTY; without even the implied warranty of +//= MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//= GNU General Public License for more details. +//= +//= You should have received a copy of the GNU General Public License +//= along with this program. If not, see . +//========================================================================= +//= AutoSpell skills configuration +//= +//= This file lists the skills available for Sage AutoSpell (Hindsight) skill. +//= +//= Notes: +//= - The maximum number of entries is controlled by MAX_AUTOSPELL_DB (src/map/skill.h) +//= - Additionally, some official clients have a hard limit on the number of skills +//= (this is limited by packet size and can't be changed) +//= - SkillLevel and SpiritBoost configures the basic behavior of the skills, the requirement +//= for the player to have learned the skill at this level is applied on source +//========================================================================= + +autospell_db: ( +/************************************************************************** + ************* Entry structure ******************************************** + ************************************************************************** +{ + SkillId: Skill ID/Name (constant string or int) + SkillLevel: Highest usable level (int, defaults to 0) (can be grouped by AutoSpell Levels) + (Level 0 would mean not usable in this AutoSpell level) + SpiritBoost: Use max level when under Sage Spirit? (boolean, defaults to false) +}, +**************************************************************************/ + +{ + SkillId: "MG_NAPALMBEAT" + SkillLevel: 3 + SpiritBoost: false +}, +{ + SkillId: "MG_COLDBOLT" + SkillLevel: { + // Lv1: 0 + Lv2: 1 + Lv3: 2 + Lv4: 3 + Lv5: 3 + Lv6: 3 + Lv7: 3 + Lv8: 3 + Lv9: 3 + Lv10: 3 + } + SpiritBoost: true +}, +{ + SkillId: "MG_FIREBOLT" + SkillLevel: { + // Lv1: 0 + Lv2: 1 + Lv3: 2 + Lv4: 3 + Lv5: 3 + Lv6: 3 + Lv7: 3 + Lv8: 3 + Lv9: 3 + Lv10: 3 + } + SpiritBoost: true +}, +{ + SkillId: "MG_LIGHTNINGBOLT" + SkillLevel: { + // Lv1: 0 + Lv2: 1 + Lv3: 2 + Lv4: 3 + Lv5: 3 + Lv6: 3 + Lv7: 3 + Lv8: 3 + Lv9: 3 + Lv10: 3 + } + SpiritBoost: true +}, +{ + SkillId: "MG_SOULSTRIKE" + SkillLevel: { + // Lv1 .. Lv4: 0 + Lv5: 1 + Lv6: 2 + Lv7: 3 + Lv8: 3 + Lv9: 3 + Lv10: 3 + } + SpiritBoost: false +}, +{ + SkillId: "MG_FIREBALL" + SkillLevel: { + // Lv1 .. Lv7: 0 + Lv8: 1 + Lv9: 2 + Lv10: 2 + } + SpiritBoost: false +}, +{ + SkillId: "MG_FROSTDIVER" + SkillLevel: { + // Lv1 .. Lv9: 0 + Lv10: 1 + } + SpiritBoost: false +}, +) diff --git a/src/common/HPMDataCheck.h b/src/common/HPMDataCheck.h index a6c91dd1455..ad7debe4358 100644 --- a/src/common/HPMDataCheck.h +++ b/src/common/HPMDataCheck.h @@ -1185,6 +1185,7 @@ HPExport const struct s_HPMDataCheck HPMDataCheck[] = { #define MAP_SEARCHSTORE_H #endif // MAP_SEARCHSTORE_H #ifdef MAP_SKILL_H + { "s_autospell_db", sizeof(struct s_autospell_db), SERVER_TYPE_MAP }, { "s_skill_abra_db", sizeof(struct s_skill_abra_db), SERVER_TYPE_MAP }, { "s_skill_arrow_db", sizeof(struct s_skill_arrow_db), SERVER_TYPE_MAP }, { "s_skill_changematerial_db", sizeof(struct s_skill_changematerial_db), SERVER_TYPE_MAP }, diff --git a/src/map/clif.c b/src/map/clif.c index 8d63c6d0088..1355772a433 100644 --- a/src/map/clif.c +++ b/src/map/clif.c @@ -8007,46 +8007,43 @@ static void clif_pet_food(struct map_session_data *sd, int foodid, int fail) WFIFOSET(fd, sizeof(struct PACKET_ZC_FEED_PET)); } -/// Presents a list of skills that can be auto-spelled (ZC_AUTOSPELLLIST). -/// 01cd { .L }*7 -static void clif_autospell(struct map_session_data *sd, uint16 skill_lv) +/** + * Presents a list of skills that can be auto-spelled (ZC_AUTOSPELLLIST). + * + * 01cd { .L }*7 + * 0afb .W { .L }* + * + * @param sd player who will receive the list + * @param skill_lv autospell skill level + * @param skill_ids_list list of available skills to choose from + * @param list_len length of skill_ids_list + */ +static void clif_autospell(struct map_session_data *sd, uint16 skill_lv, int *skill_ids_list, int list_len) { #if PACKETVER_MAIN_NUM >= 20090406 || defined(PACKETVER_RE) || defined(PACKETVER_ZERO) || PACKETVER_SAK_NUM >= 20080618 nullpo_retv(sd); - int fd = sd->fd; #if PACKETVER_MAIN_NUM >= 20181128 || PACKETVER_RE_NUM >= 20181031 - // reserve space for 7 skills - WFIFOHEAD(fd, sizeof(struct PACKET_ZC_AUTOSPELLLIST) + 4 * 7); + const int len = sizeof(struct PACKET_ZC_AUTOSPELLLIST) + sizeof(int) * list_len; #else - WFIFOHEAD(fd, sizeof(struct PACKET_ZC_AUTOSPELLLIST)); + const int len = sizeof(struct PACKET_ZC_AUTOSPELLLIST); + if (list_len > 7) { + ShowError("%s: AutoSpell list too big for current client. Limit: %d, received: %d. Truncating list...\n", __func__, 7, list_len); + list_len = 7; + } #endif + + int fd = sd->fd; + WFIFOHEAD(fd, len); + struct PACKET_ZC_AUTOSPELLLIST *p = WFIFOP(fd, 0); memset(p, 0, sizeof(struct PACKET_ZC_AUTOSPELLLIST)); - p->packetType = HEADER_ZC_AUTOSPELLLIST; - int index = 0; - - if (skill_lv > 0 && pc->checkskill(sd, MG_NAPALMBEAT) > 0) - p->skills[index++] = MG_NAPALMBEAT; - if (skill_lv > 1 && pc->checkskill(sd, MG_COLDBOLT) > 0) - p->skills[index++] = MG_COLDBOLT; - if (skill_lv > 1 && pc->checkskill(sd, MG_FIREBOLT) > 0) - p->skills[index++] = MG_FIREBOLT; - if (skill_lv > 1 && pc->checkskill(sd, MG_LIGHTNINGBOLT) > 0) - p->skills[index++] = MG_LIGHTNINGBOLT; - if (skill_lv > 4 && pc->checkskill(sd, MG_SOULSTRIKE) > 0) - p->skills[index++] = MG_SOULSTRIKE; - if (skill_lv > 7 && pc->checkskill(sd, MG_FIREBALL) > 0) - p->skills[index++] = MG_FIREBALL; - if (skill_lv > 9 && pc->checkskill(sd, MG_FROSTDIVER) > 0) - p->skills[index++] = MG_FROSTDIVER; + p->packetType = HEADER_ZC_AUTOSPELLLIST; #if PACKETVER_MAIN_NUM >= 20181128 || PACKETVER_RE_NUM >= 20181031 - const int len = sizeof(struct PACKET_ZC_AUTOSPELLLIST) + index * 4; p->packetLength = len; -#else - const int len = sizeof(struct PACKET_ZC_AUTOSPELLLIST); #endif + memcpy(p->skills, skill_ids_list, sizeof(int) * list_len); WFIFOSET(fd, len); sd->menuskill_id = SA_AUTOSPELL; @@ -14199,7 +14196,7 @@ static void clif_parse_AutoSpell(int fd, struct map_session_data *sd) if( !skill_id ) return; - skill->autospell(sd, skill_id); + skill->autospell_spell_selected(sd, skill_id); clif_menuskill_clear(sd); } diff --git a/src/map/clif.h b/src/map/clif.h index f1c9e826603..01213081c44 100644 --- a/src/map/clif.h +++ b/src/map/clif.h @@ -1091,7 +1091,7 @@ struct clif_interface { void (*skill_mapinfomessage) (struct map_session_data *sd, int type); void (*skill_produce_mix_list) (struct map_session_data *sd, int skill_id, int trigger); void (*cooking_list) (struct map_session_data *sd, int trigger, uint16 skill_id, int qty, int list_type); - void (*autospell) (struct map_session_data *sd,uint16 skill_lv); + void (*autospell) (struct map_session_data *sd, uint16 skill_lv, int *skill_ids_list, int list_len); void (*combo_delay) (struct block_list *bl,int wait); void (*status_change) (struct block_list *bl, int relevant_bl, int type, int flag, int total_tick, int val1, int val2, int val3); void (*status_change_sub) (struct block_list *bl, int type, int relevant_bl, int flag, int tick, int total_tick, int val1, int val2, int val3); diff --git a/src/map/skill.c b/src/map/skill.c index e0c8fe751b7..83f362ef1fa 100644 --- a/src/map/skill.c +++ b/src/map/skill.c @@ -8776,43 +8776,8 @@ static int skill_castend_nodamage_id(struct block_list *src, struct block_list * sc_start(src, bl, type, 100, skill_lv, skill->get_time(skill_id, skill_lv), skill_id); break; case SA_AUTOSPELL: - clif->skill_nodamage(src,bl,skill_id,skill_lv,1); - if(sd){ - sd->state.workinprogress = 3; - clif->autospell(sd,skill_lv); - }else { - int maxlv=1,spellid=0; - static const int spellarray[3] = { MG_COLDBOLT,MG_FIREBOLT,MG_LIGHTNINGBOLT }; - if(skill_lv >= 10) { - spellid = MG_FROSTDIVER; -#if 0 - if (tsc && tsc->data[SC_SOULLINK] && tsc->data[SC_SOULLINK]->val2 == SA_SAGE) - maxlv = 10; - else -#endif // 0 - maxlv = skill_lv - 9; - } - else if(skill_lv >=8) { - spellid = MG_FIREBALL; - maxlv = skill_lv - 7; - } - else if(skill_lv >=5) { - spellid = MG_SOULSTRIKE; - maxlv = skill_lv - 4; - } - else if(skill_lv >=2) { - int i = rnd() % ARRAYLENGTH(spellarray); - spellid = spellarray[i]; - maxlv = skill_lv - 1; - } - else if(skill_lv > 0) { - spellid = MG_NAPALMBEAT; - maxlv = 3; - } - if(spellid > 0) - sc_start4(src,src,SC_AUTOSPELL,100,skill_lv,spellid,maxlv,0, - skill->get_time(SA_AUTOSPELL, skill_lv), SA_AUTOSPELL); - } + clif->skill_nodamage(src, bl, skill_id, skill_lv, 1); + skill->autospell_select_spell(src, skill_lv); break; case BS_GREED: @@ -16489,7 +16454,7 @@ static bool skill_items_required(struct map_session_data *sd, int skill_id, int /** * Checks conditions for a skill to be executed. This check happens after the cast time was completed. - * + * * @param sd The character who cast the skill. * @param skill_id The skill's ID. * @param skill_lv The skill's level. @@ -16692,7 +16657,7 @@ static int skill_check_condition_castend(struct map_session_data *sd, uint16 ski /** * Checks conditions for a skill to be executed. This check happens after the cast time was completed. - * + * * @param sd The character who cast the skill. * @param skill_id The skill's ID. * @param skill_lv The skill's level. @@ -17834,44 +17799,115 @@ static void skill_weaponrefine(struct map_session_data *sd, int idx) } /*========================================== - * + * Auto Spell / Hindsight *------------------------------------------*/ -static int skill_autospell(struct map_session_data *sd, uint16 skill_id) + +/** + * Prepares list and request player to choose the spell they want to use (Auto Spell skill) + * @param sd player casting the skill + * @param skill_lv Auto Spell level + */ +static void skill_autospell_select_spell_pc(struct map_session_data *sd, int skill_lv) { - uint16 skill_lv; - int maxlv=1,lv; + nullpo_retv(sd); - nullpo_ret(sd); + int *skill_ids; + CREATE(skill_ids, int, MAX_AUTOSPELL_DB); - skill_lv = sd->menuskill_val; - lv=pc->checkskill(sd,skill_id); + int valid_len = 0; - if(!skill_lv || !lv) return 0; // Player must learn the skill before doing auto-spell [Lance] + for (int i = 0; i < MAX_AUTOSPELL_DB; ++i) { + const struct s_autospell_db *sk = &skill->dbs->autospell_db[i]; + if (sk->autospell_level == 0) + break; - if(skill_id==MG_NAPALMBEAT) maxlv=3; - else if(skill_id==MG_COLDBOLT || skill_id==MG_FIREBOLT || skill_id==MG_LIGHTNINGBOLT){ - if (sd->sc.data[SC_SOULLINK] && sd->sc.data[SC_SOULLINK]->val2 == SL_SAGE) - maxlv =10; //Soul Linker bonus. [Skotlex] - else if(skill_lv==2) maxlv=1; - else if(skill_lv==3) maxlv=2; - else if(skill_lv>=4) maxlv=3; + if (skill_lv >= sk->autospell_level && pc->checkskill(sd, sk->skill_id) > 0) { + skill_ids[valid_len] = sk->skill_id; + valid_len++; + } } - else if(skill_id==MG_SOULSTRIKE){ - if(skill_lv==5) maxlv=1; - else if(skill_lv==6) maxlv=2; - else if(skill_lv>=7) maxlv=3; + + sd->state.workinprogress = 3; + clif->autospell(sd, skill_lv, skill_ids, valid_len); + + aFree(skill_ids); +} + +/** + * Auto Spell skill spell selection step. + * @param bl unit casting the skill + * @param skill_lv skill level + */ +static void skill_autospell_select_spell(struct block_list *bl, int skill_lv) +{ + nullpo_retv(bl); + + if (bl->type == BL_PC) { + skill->autospell_select_spell_pc(BL_CAST(BL_PC, bl), skill_lv); + return; } - else if(skill_id==MG_FIREBALL){ - if(skill_lv==8) maxlv=1; - else if(skill_lv>=9) maxlv=2; + + int lower_idx = -1; + int upper_idx = 0; + int highest_autospell_tier = 0; + while (upper_idx < MAX_AUTOSPELL_DB + && skill->dbs->autospell_db[upper_idx].autospell_level > 0 + && skill->dbs->autospell_db[upper_idx].autospell_level <= skill_lv) { + if (highest_autospell_tier != skill->dbs->autospell_db[upper_idx].autospell_level) { + lower_idx = upper_idx; + highest_autospell_tier = skill->dbs->autospell_db[upper_idx].autospell_level; + } + + upper_idx++; } - else if(skill_id==MG_FROSTDIVER) maxlv=1; - else return 0; - if(maxlv > lv) - maxlv = lv; + if (lower_idx == -1) + return; // No skill available + + int skill_idx = lower_idx; + if ((upper_idx - lower_idx) > 1) + skill_idx += rnd() % (upper_idx - lower_idx); + + const struct s_autospell_db *sk = &skill->dbs->autospell_db[skill_idx]; + sc_start4(bl, bl, SC_AUTOSPELL, 100, skill_lv, sk->skill_id, sk->skill_lv[skill_lv - 1], 0, + skill->get_time(SA_AUTOSPELL, skill_lv), SA_AUTOSPELL); +} + +/** + * Initiates AutoSpell effect on player based on the skill they chose. + * + * // @FIXME: Why this always returns 0? Does it even make sense? + * @param sd player casting the skill + * @param skill_id selected skill + * @returns always returns 0 + */ +static int skill_autospell_spell_selected(struct map_session_data *sd, uint16 skill_id) +{ + nullpo_ret(sd); + + uint16 autospell_lv = sd->menuskill_val; + int skill_lv = pc->checkskill(sd, skill_id); + + if(autospell_lv == 0 || skill_lv == 0) + return 0; // Player must learn the skill before doing auto-spell [Lance] + + int skill_idx; + ARR_FIND(0, MAX_AUTOSPELL_DB, skill_idx, skill->dbs->autospell_db[skill_idx].skill_id == skill_id); + if (skill_idx == MAX_AUTOSPELL_DB) + return 0; // Not an AutoSpell skill (exploit attempt?) + + const struct s_autospell_db *sk = &skill->dbs->autospell_db[skill_idx]; + if (sk->autospell_level > autospell_lv) + return 0; // Don't have enough level to use - sc_start4(&sd->bl,&sd->bl,SC_AUTOSPELL,100,skill_lv,skill_id,maxlv,0, + int max_lv = sk->skill_lv[autospell_lv - 1]; + if (sk->spirit_boost && sd->sc.data[SC_SOULLINK] != NULL && sd->sc.data[SC_SOULLINK]->val2 == SL_SAGE) + max_lv = skill->dbs->db[skill->get_index(skill_id)].max; // Soul Linker bonus. [Skotlex] + + if (max_lv > skill_lv) + max_lv = skill_lv; + + sc_start4(&sd->bl, &sd->bl, SC_AUTOSPELL, 100, skill_lv, skill_id, max_lv, 0, skill->get_time(SA_AUTOSPELL, skill_lv), SA_AUTOSPELL); return 0; } @@ -21609,7 +21645,7 @@ static void skill_config_set_level(struct config_setting_t *conf, int *arr) if (config_setting_is_group(conf)) { for (i=0; isetting_lookup_int(conf, level, &arr[i]); } } else if (config_setting_is_array(conf)) { @@ -21769,7 +21805,7 @@ static void skill_validate_max_level(struct config_setting_t *conf, struct s_ski * @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. * @param inherited Whether this record is an inherited entry (thus sk already has a valid value) - * + * **/ static void skill_validate_description(struct config_setting_t *conf, struct s_skill_db *sk, bool inherited) { @@ -22016,7 +22052,7 @@ static void skill_validate_skillinfo(struct config_setting_t *conf, struct s_ski { "RangeModByResearchTrap", INF2_RANGE_RESEARCHTRAP }, { "AllowPlagiarism", INF2_ALLOW_PLAGIARIZE }, }; - + 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); @@ -24724,7 +24760,7 @@ static bool skill_read_skilldb(const char *filename) idb_iput(loaded_ids_db, tmp_db.nameid, true); count++; } - + db_destroy(loaded_ids_db); libconfig->destroy(&skilldb); @@ -24732,6 +24768,223 @@ static bool skill_read_skilldb(const char *filename) return true; } +/** + * Reads a AutoSpell skill's Id (SkillId) when reading the autospell DB. + * + * @param conf The libconfig settings block which contains the skill's data. + * @param sk The autospell_skill struct where the id should be set. + */ +static void skill_read_autospell_skill_id(struct config_setting_t *conf, struct s_autospell_db *sk, int index) +{ + nullpo_retv(conf); + nullpo_retv(sk); + + sk->skill_id = 0; + + const char *skill_name = NULL; + int skill_id = 0; + + if (libconfig->setting_lookup_int(conf, "SkillId", &skill_id) == CONFIG_FALSE) { + if (libconfig->setting_lookup_string(conf, "SkillId", &skill_name) == CONFIG_FALSE) { + ShowError("%s: Invalid AutoSpell db entry \"%d\". SkillId is required. Skipping...\n", __func__, index); + return; + } + + skill_id = skill->name2id(skill_name); + } + + if (skill_id == 0) { + if (skill_name != NULL) + ShowError("%s: Invalid AutoSpell db entry \"%d\". SkillId \"%s\" doesn't exists. Skipping...\n", __func__, index, skill_name); + else + ShowError("%s: Invalid AutoSpell db entry \"%d\". SkillId \"%d\" doesn't exists. Skipping...\n", __func__, index, skill_id); + return; + } + + sk->skill_id = skill_id; +} + +/** + * Reads a AutoSpell skill's usable levels (SkillLevel) when reading the autospell DB. + * + * @param conf The libconfig settings block which contains the skill's data. + * @param sk The autospell_skill struct where the level should be set. + */ +static void skill_read_autospell_skill_level(struct config_setting_t *conf, struct s_autospell_db *sk) +{ + nullpo_retv(conf); + nullpo_retv(sk); + + skill->level_set_value(sk->skill_lv, 0); + + struct config_setting_t *t = libconfig->setting_get_member(conf, "SkillLevel"); + + 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. + snprintf(lv, sizeof(lv), "Lv%d", i + 1); + + int level; + if (libconfig->setting_lookup_int(t, lv, &level) == CONFIG_TRUE) { + if (level >= 0 && level <= MAX_SKILL_LEVEL) + sk->skill_lv[i] = level; + else + ShowWarning("%s: Invalid SkillLevel %d specified in level %d for skill ID %d in %s! Minimum is 0, maximum is %d. Defaulting to 0...\n", + __func__, level, i + 1, sk->skill_id, conf->file, MAX_SKILL_LEVEL); + } + } + + return; + } + + int level; + if (libconfig->setting_lookup_int(conf, "SkillLevel", &level) == CONFIG_TRUE) { + if (level >= 0 && level <= MAX_SKILL_LEVEL) + skill->level_set_value(sk->skill_lv, level); + else + ShowWarning("%s: Invalid SkillLevel %d specified for skill ID %d in %s! Minimum is 0, maximum is %d. Defaulting to 0...\n", + __func__, level, sk->skill_id, conf->file, MAX_SKILL_LEVEL); + } +} + +/** + * Reads additional field settings via plugins when parsing autospell_db.conf + * @param conf struct, pointer to the entry configuration + * @param sk struct, struct, pointer to s_autospell_db + */ +static void skill_read_autospell_additional_fields(struct config_setting_t *conf, struct s_autospell_db *sk) +{ + // Does nothing like a boss. *cough* plugins *cough* +} + +/** + * Compares two autospell db entries and sort it as the database expects: + * 1. autospell_level = 0 (blank entries) goes to the end + * 2. entries are sorted by autospell_level in ascending order + * + * @param entry1 first entry to compare + * @param entry2 second entry to compare + * @returns + * - < 0 if entry1 should go before entry2 ; + * - 0 if entry1 and entry2 are equivalent in sorting ; + * - > 0 if entry2 should go before entry1 + */ +static int skill_autospell_db_entry_compare(const void *entry1, const void *entry2) +{ + nullpo_ret(entry1); + nullpo_ret(entry2); + + struct s_autospell_db *entry1_ = (struct s_autospell_db *) entry1; + struct s_autospell_db *entry2_ = (struct s_autospell_db *) entry2; + + // autospell_level == 0 is always at the end of the list, because they are unused entries + if (entry1_->autospell_level == 0 && entry2_->autospell_level != 0) + return 1; + + if (entry2_->autospell_level == 0 && entry1_->autospell_level != 0) + return -1; + + return entry1_->autospell_level - entry2_->autospell_level; +} + +/** + * Reads autospell_db.conf into skill->dbs->autospell_db + * @param filename file to be loaded + * @returns true if the configuration was read (even if with some errors), false otherwise + */ +static bool skill_read_autospell_db(const char *filename) +{ + nullpo_retr(false, filename); + + char filepath[256]; + + libconfig->format_db_path(filename, filepath, sizeof(filepath)); + + if (!exists(filepath)) { + ShowError("%s: Can't find file %s! Abort reading AutoSpell skills...\n", __func__, filepath); + return false; + } + + struct config_t autospelldb; + + if (libconfig->load_file(&autospelldb, filepath) == 0) + return false; // Libconfig error report. + + struct config_setting_t *sk = libconfig->setting_get_member(autospelldb.root, "autospell_db"); + if (sk == NULL) { + ShowError("%s: AutoSpell DB could not be loaded! Please check %s.\n", __func__, filepath); + libconfig->destroy(&autospelldb); + return false; + } + + struct config_setting_t *conf; + int index = 0; + int count = 0; + + // Map int -> bool + struct DBMap *loaded_skills_db = idb_alloc(DB_OPT_BASE); + + while ((conf = libconfig->setting_get_elem(sk, index++)) != NULL) { + struct s_autospell_db tmp_db = {0}; + + skill->read_autospell_skill_id(conf, &tmp_db, index); + if (tmp_db.skill_id == 0) + continue; + + const char *skill_name = skill->dbs->db[skill->get_index(tmp_db.skill_id)].name; + + if (idb_exists(loaded_skills_db, tmp_db.skill_id)) { + ShowError("%s: Invalid AutoSpell db entry \"%d\". Skill \"%s\" (id: %d) duplicated. Skipping...\n", __func__, index, skill_name, tmp_db.skill_id); + continue; + } + + skill->read_autospell_skill_level(conf, &tmp_db); + + tmp_db.spirit_boost = false; + if (libconfig->setting_lookup_bool_real(conf, "SpiritBoost", &tmp_db.spirit_boost) == CONFIG_FALSE) + tmp_db.spirit_boost = false; + + skill->read_autospell_additional_fields(conf, &tmp_db); + + int min_level = 0; + ARR_FIND(0, MAX_SKILL_LEVEL, min_level, (tmp_db.skill_lv[min_level] != 0)); + if (min_level == MAX_SKILL_LEVEL) { + ShowError("%s: Invalid AutoSpell db entry \"%d\". Skill \"%s\" (id: %d) is never usable (SkillLevel is always 0). Skipping...\n", + __func__, index, skill_name, tmp_db.skill_id); + continue; + } + + tmp_db.autospell_level = min_level + 1; + + if (count >= MAX_AUTOSPELL_DB) { + ShowWarning("%s: Could not add skill \"%s\" (Id: \"%d\") to AutoSpell DB. Limit reached (see MAX_AUTOSPELL_DB). Skipping...\n", + __func__, skill_name, tmp_db.skill_id); + continue; + } + + skill->dbs->autospell_db[count] = tmp_db; + idb_iput(loaded_skills_db, tmp_db.skill_id, true); + + count++; + } + +#if PACKETVER_MAIN_NUM < 20181128 && PACKETVER_RE_NUM < 20181031 + if (count > 7) { + ShowWarning("%s: Your current packet version only supports up to 7 autospell skills, but your autospell db contains \"%d\" skills. Some skills may not be shown.\n", __func__, count); + ShowWarning("%s: Update your packet version or reduce the number of skills to fix this warning.\n", __func__); + } +#endif + + db_destroy(loaded_skills_db); + libconfig->destroy(&autospelldb); + + qsort(skill->dbs->autospell_db, MAX_AUTOSPELL_DB, sizeof(struct s_autospell_db), skill->autospell_db_entry_compare); + + ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", count, filepath); + + return true; +} + /*=============================== * DB reading. * produce_db.txt @@ -24774,7 +25027,7 @@ static void skill_readdb(bool minimal) struct s_skill_db *db = &skill->dbs->db[i]; if (db->nameid == 0) continue; - + if (skill->name2id(db->name) != 0) { ShowError("%s: Duplicated skill name %s found for Skill ID %d (Other Skill ID: %d). Skipping name...", __func__, db->name, db->nameid, skill->name2id(db->name)); @@ -24798,6 +25051,7 @@ static void skill_readdb(bool minimal) sv->readdb(map->db_path, "magicmushroom_db.txt", ',', 1, 1, MAX_SKILL_MAGICMUSHROOM_DB, skill->parse_row_magicmushroomdb); sv->readdb(map->db_path, "skill_improvise_db.txt", ',', 2, 2, MAX_SKILL_IMPROVISE_DB, skill->parse_row_improvisedb); sv->readdb(map->db_path, "skill_changematerial_db.txt", ',', 4, 4+2*5, MAX_SKILL_PRODUCE_DB, skill->parse_row_changematerialdb); + skill->read_autospell_db(DBPATH "autospell_db.conf"); } static void skill_reload(void) @@ -25024,7 +25278,9 @@ void skill_defaults(void) skill->repairweapon = skill_repairweapon; skill->identify = skill_identify; skill->weaponrefine = skill_weaponrefine; - skill->autospell = skill_autospell; + skill->autospell_select_spell = skill_autospell_select_spell; + skill->autospell_select_spell_pc = skill_autospell_select_spell_pc; + skill->autospell_spell_selected = skill_autospell_spell_selected; skill->calc_heal = skill_calc_heal; skill->check_cloaking = skill_check_cloaking; skill->check_cloaking_end = skill_check_cloaking_end; @@ -25161,6 +25417,13 @@ void skill_defaults(void) skill->validate_status_change = skill_validate_status_change; skill->validate_additional_fields = skill_validate_additional_fields; skill->read_skilldb = skill_read_skilldb; + /* AutoSpell DB Libconfig */ + skill->read_autospell_skill_id = skill_read_autospell_skill_id; + skill->read_autospell_skill_level = skill_read_autospell_skill_level; + skill->read_autospell_additional_fields = skill_read_autospell_additional_fields; + skill->autospell_db_entry_compare = skill_autospell_db_entry_compare; + skill->read_autospell_db = skill_read_autospell_db; + /* db reading helpers */ 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 6725a43c849..5002617d1b1 100644 --- a/src/map/skill.h +++ b/src/map/skill.h @@ -77,6 +77,7 @@ struct status_change_entry; #define MAX_SKILL_SPELLBOOK_DB 17 #define MAX_SKILL_MAGICMUSHROOM_DB 23 +#define MAX_AUTOSPELL_DB 7 //Walk intervals at which chase-skills are attempted to be triggered. #define WALK_SKILL_INTERVAL 5 @@ -1810,6 +1811,14 @@ enum skill_enabled_npc_flags { * Structures **/ +/** Information about a possible skill for AutoSpell */ +struct s_autospell_db { + int autospell_level; //< Minimum AutoSpell level to show this skill + int skill_id; //< Skill Id + int skill_lv[MAX_SKILL_LEVEL]; //< Maximum usable skill level at each AutoSpell level + bool spirit_boost; //< Whether Sage's Spirit boosts this skill to maximum level +}; + /** A container holding all required items. **/ struct skill_required_item_data { struct { @@ -2016,6 +2025,10 @@ BEGIN_ZEROED_BLOCK; // This block will be zeroed in skill_defaults() as well as struct s_skill_improvise_db improvise_db[MAX_SKILL_IMPROVISE_DB]; struct s_skill_changematerial_db changematerial_db[MAX_SKILL_PRODUCE_DB]; struct s_skill_spellbook_db spellbook_db[MAX_SKILL_SPELLBOOK_DB]; + /** + * Skills for AutoSpell. entries with autospell_level = 0 are unused (and always at the end) + */ + struct s_autospell_db autospell_db[MAX_AUTOSPELL_DB]; END_ZEROED_BLOCK; struct s_skill_unit_layout unit_layout[MAX_SKILL_UNIT_LAYOUT]; }; @@ -2155,7 +2168,9 @@ struct skill_interface { void (*repairweapon) (struct map_session_data *sd, int idx); void (*identify) (struct map_session_data *sd,int idx); void (*weaponrefine) (struct map_session_data *sd,int idx); - int (*autospell) (struct map_session_data *md,uint16 skill_id); + void (*autospell_select_spell) (struct block_list *bl, int skill_lv); + void (*autospell_select_spell_pc) (struct map_session_data *sd, int skill_lv); + int (*autospell_spell_selected) (struct map_session_data *md, uint16 skill_id); int (*calc_heal) (struct block_list *src, struct block_list *target, uint16 skill_id, uint16 skill_lv, bool heal); bool (*check_cloaking) (struct block_list *bl, struct status_change_entry *sce); int (*check_cloaking_end) (struct block_list *bl, va_list ap); @@ -2292,6 +2307,11 @@ struct skill_interface { void (*validate_status_change) (struct config_setting_t *conf, struct s_skill_db *sk, bool inherited); void (*validate_additional_fields) (struct config_setting_t *conf, struct s_skill_db *sk, bool inherited); bool (*read_skilldb) (const char *filename); + void (*read_autospell_skill_id) (struct config_setting_t *conf, struct s_autospell_db *sk, int index); + void (*read_autospell_skill_level) (struct config_setting_t *conf, struct s_autospell_db *sk); + void (*read_autospell_additional_fields) (struct config_setting_t *conf, struct s_autospell_db *sk); + int (*autospell_db_entry_compare) (const void *entry1, const void *entry2); + bool (*read_autospell_db) (const char *filename); void (*config_set_level) (struct config_setting_t *conf, int *arr); void (*level_set_value) (int *arr, int value); bool (*parse_row_producedb) (char* split[], int columns, int current); diff --git a/src/plugins/HPMHooking/HPMHooking.Defs.inc b/src/plugins/HPMHooking/HPMHooking.Defs.inc index 29fb7952d5e..4fc5b5ac6ef 100644 --- a/src/plugins/HPMHooking/HPMHooking.Defs.inc +++ b/src/plugins/HPMHooking/HPMHooking.Defs.inc @@ -1572,8 +1572,8 @@ typedef void (*HPMHOOK_pre_clif_skill_produce_mix_list) (struct map_session_data typedef void (*HPMHOOK_post_clif_skill_produce_mix_list) (struct map_session_data *sd, int skill_id, int trigger); typedef void (*HPMHOOK_pre_clif_cooking_list) (struct map_session_data **sd, int *trigger, uint16 *skill_id, int *qty, int *list_type); typedef void (*HPMHOOK_post_clif_cooking_list) (struct map_session_data *sd, int trigger, uint16 skill_id, int qty, int list_type); -typedef void (*HPMHOOK_pre_clif_autospell) (struct map_session_data **sd, uint16 *skill_lv); -typedef void (*HPMHOOK_post_clif_autospell) (struct map_session_data *sd, uint16 skill_lv); +typedef void (*HPMHOOK_pre_clif_autospell) (struct map_session_data **sd, uint16 *skill_lv, int **skill_ids_list, int *list_len); +typedef void (*HPMHOOK_post_clif_autospell) (struct map_session_data *sd, uint16 skill_lv, int *skill_ids_list, int list_len); typedef void (*HPMHOOK_pre_clif_combo_delay) (struct block_list **bl, int *wait); typedef void (*HPMHOOK_post_clif_combo_delay) (struct block_list *bl, int wait); typedef void (*HPMHOOK_pre_clif_status_change) (struct block_list **bl, int *relevant_bl, int *type, int *flag, int *total_tick, int *val1, int *val2, int *val3); @@ -8474,8 +8474,12 @@ typedef void (*HPMHOOK_pre_skill_identify) (struct map_session_data **sd, int *i typedef void (*HPMHOOK_post_skill_identify) (struct map_session_data *sd, int idx); typedef void (*HPMHOOK_pre_skill_weaponrefine) (struct map_session_data **sd, int *idx); typedef void (*HPMHOOK_post_skill_weaponrefine) (struct map_session_data *sd, int idx); -typedef int (*HPMHOOK_pre_skill_autospell) (struct map_session_data **md, uint16 *skill_id); -typedef int (*HPMHOOK_post_skill_autospell) (int retVal___, struct map_session_data *md, uint16 skill_id); +typedef void (*HPMHOOK_pre_skill_autospell_select_spell) (struct block_list **bl, int *skill_lv); +typedef void (*HPMHOOK_post_skill_autospell_select_spell) (struct block_list *bl, int skill_lv); +typedef void (*HPMHOOK_pre_skill_autospell_select_spell_pc) (struct map_session_data **sd, int *skill_lv); +typedef void (*HPMHOOK_post_skill_autospell_select_spell_pc) (struct map_session_data *sd, int skill_lv); +typedef int (*HPMHOOK_pre_skill_autospell_spell_selected) (struct map_session_data **md, uint16 *skill_id); +typedef int (*HPMHOOK_post_skill_autospell_spell_selected) (int retVal___, struct map_session_data *md, uint16 skill_id); typedef int (*HPMHOOK_pre_skill_calc_heal) (struct block_list **src, struct block_list **target, uint16 *skill_id, uint16 *skill_lv, bool *heal); typedef int (*HPMHOOK_post_skill_calc_heal) (int retVal___, struct block_list *src, struct block_list *target, uint16 skill_id, uint16 skill_lv, bool heal); typedef bool (*HPMHOOK_pre_skill_check_cloaking) (struct block_list **bl, struct status_change_entry **sce); @@ -8748,6 +8752,16 @@ typedef void (*HPMHOOK_pre_skill_validate_additional_fields) (struct config_sett typedef void (*HPMHOOK_post_skill_validate_additional_fields) (struct config_setting_t *conf, struct s_skill_db *sk, bool inherited); typedef bool (*HPMHOOK_pre_skill_read_skilldb) (const char **filename); typedef bool (*HPMHOOK_post_skill_read_skilldb) (bool retVal___, const char *filename); +typedef void (*HPMHOOK_pre_skill_read_autospell_skill_id) (struct config_setting_t **conf, struct s_autospell_db **sk, int *index); +typedef void (*HPMHOOK_post_skill_read_autospell_skill_id) (struct config_setting_t *conf, struct s_autospell_db *sk, int index); +typedef void (*HPMHOOK_pre_skill_read_autospell_skill_level) (struct config_setting_t **conf, struct s_autospell_db **sk); +typedef void (*HPMHOOK_post_skill_read_autospell_skill_level) (struct config_setting_t *conf, struct s_autospell_db *sk); +typedef void (*HPMHOOK_pre_skill_read_autospell_additional_fields) (struct config_setting_t **conf, struct s_autospell_db **sk); +typedef void (*HPMHOOK_post_skill_read_autospell_additional_fields) (struct config_setting_t *conf, struct s_autospell_db *sk); +typedef int (*HPMHOOK_pre_skill_autospell_db_entry_compare) (const void **entry1, const void **entry2); +typedef int (*HPMHOOK_post_skill_autospell_db_entry_compare) (int retVal___, const void *entry1, const void *entry2); +typedef bool (*HPMHOOK_pre_skill_read_autospell_db) (const char **filename); +typedef bool (*HPMHOOK_post_skill_read_autospell_db) (bool retVal___, const char *filename); typedef void (*HPMHOOK_pre_skill_config_set_level) (struct config_setting_t **conf, int **arr); typedef void (*HPMHOOK_post_skill_config_set_level) (struct config_setting_t *conf, int *arr); typedef void (*HPMHOOK_pre_skill_level_set_value) (int **arr, int *value); diff --git a/src/plugins/HPMHooking/HPMHooking_map.HPMHooksCore.inc b/src/plugins/HPMHooking/HPMHooking_map.HPMHooksCore.inc index 080837f4a02..fcbcb1af46d 100644 --- a/src/plugins/HPMHooking/HPMHooking_map.HPMHooksCore.inc +++ b/src/plugins/HPMHooking/HPMHooking_map.HPMHooksCore.inc @@ -6396,8 +6396,12 @@ struct { struct HPMHookPoint *HP_skill_identify_post; struct HPMHookPoint *HP_skill_weaponrefine_pre; struct HPMHookPoint *HP_skill_weaponrefine_post; - struct HPMHookPoint *HP_skill_autospell_pre; - struct HPMHookPoint *HP_skill_autospell_post; + struct HPMHookPoint *HP_skill_autospell_select_spell_pre; + struct HPMHookPoint *HP_skill_autospell_select_spell_post; + struct HPMHookPoint *HP_skill_autospell_select_spell_pc_pre; + struct HPMHookPoint *HP_skill_autospell_select_spell_pc_post; + struct HPMHookPoint *HP_skill_autospell_spell_selected_pre; + struct HPMHookPoint *HP_skill_autospell_spell_selected_post; struct HPMHookPoint *HP_skill_calc_heal_pre; struct HPMHookPoint *HP_skill_calc_heal_post; struct HPMHookPoint *HP_skill_check_cloaking_pre; @@ -6670,6 +6674,16 @@ struct { struct HPMHookPoint *HP_skill_validate_additional_fields_post; struct HPMHookPoint *HP_skill_read_skilldb_pre; struct HPMHookPoint *HP_skill_read_skilldb_post; + struct HPMHookPoint *HP_skill_read_autospell_skill_id_pre; + struct HPMHookPoint *HP_skill_read_autospell_skill_id_post; + struct HPMHookPoint *HP_skill_read_autospell_skill_level_pre; + struct HPMHookPoint *HP_skill_read_autospell_skill_level_post; + struct HPMHookPoint *HP_skill_read_autospell_additional_fields_pre; + struct HPMHookPoint *HP_skill_read_autospell_additional_fields_post; + struct HPMHookPoint *HP_skill_autospell_db_entry_compare_pre; + struct HPMHookPoint *HP_skill_autospell_db_entry_compare_post; + struct HPMHookPoint *HP_skill_read_autospell_db_pre; + struct HPMHookPoint *HP_skill_read_autospell_db_post; struct HPMHookPoint *HP_skill_config_set_level_pre; struct HPMHookPoint *HP_skill_config_set_level_post; struct HPMHookPoint *HP_skill_level_set_value_pre; @@ -13935,8 +13949,12 @@ struct { int HP_skill_identify_post; int HP_skill_weaponrefine_pre; int HP_skill_weaponrefine_post; - int HP_skill_autospell_pre; - int HP_skill_autospell_post; + int HP_skill_autospell_select_spell_pre; + int HP_skill_autospell_select_spell_post; + int HP_skill_autospell_select_spell_pc_pre; + int HP_skill_autospell_select_spell_pc_post; + int HP_skill_autospell_spell_selected_pre; + int HP_skill_autospell_spell_selected_post; int HP_skill_calc_heal_pre; int HP_skill_calc_heal_post; int HP_skill_check_cloaking_pre; @@ -14209,6 +14227,16 @@ struct { int HP_skill_validate_additional_fields_post; int HP_skill_read_skilldb_pre; int HP_skill_read_skilldb_post; + int HP_skill_read_autospell_skill_id_pre; + int HP_skill_read_autospell_skill_id_post; + int HP_skill_read_autospell_skill_level_pre; + int HP_skill_read_autospell_skill_level_post; + int HP_skill_read_autospell_additional_fields_pre; + int HP_skill_read_autospell_additional_fields_post; + int HP_skill_autospell_db_entry_compare_pre; + int HP_skill_autospell_db_entry_compare_post; + int HP_skill_read_autospell_db_pre; + int HP_skill_read_autospell_db_post; int HP_skill_config_set_level_pre; int HP_skill_config_set_level_post; int HP_skill_level_set_value_pre; diff --git a/src/plugins/HPMHooking/HPMHooking_map.HookingPoints.inc b/src/plugins/HPMHooking/HPMHooking_map.HookingPoints.inc index a126a441a61..f8374cbb049 100644 --- a/src/plugins/HPMHooking/HPMHooking_map.HookingPoints.inc +++ b/src/plugins/HPMHooking/HPMHooking_map.HookingPoints.inc @@ -3274,7 +3274,9 @@ struct HookingPointData HookingPoints[] = { { HP_POP(skill->repairweapon, HP_skill_repairweapon) }, { HP_POP(skill->identify, HP_skill_identify) }, { HP_POP(skill->weaponrefine, HP_skill_weaponrefine) }, - { HP_POP(skill->autospell, HP_skill_autospell) }, + { HP_POP(skill->autospell_select_spell, HP_skill_autospell_select_spell) }, + { HP_POP(skill->autospell_select_spell_pc, HP_skill_autospell_select_spell_pc) }, + { HP_POP(skill->autospell_spell_selected, HP_skill_autospell_spell_selected) }, { HP_POP(skill->calc_heal, HP_skill_calc_heal) }, { HP_POP(skill->check_cloaking, HP_skill_check_cloaking) }, { HP_POP(skill->check_cloaking_end, HP_skill_check_cloaking_end) }, @@ -3411,6 +3413,11 @@ struct HookingPointData HookingPoints[] = { { HP_POP(skill->validate_status_change, HP_skill_validate_status_change) }, { HP_POP(skill->validate_additional_fields, HP_skill_validate_additional_fields) }, { HP_POP(skill->read_skilldb, HP_skill_read_skilldb) }, + { HP_POP(skill->read_autospell_skill_id, HP_skill_read_autospell_skill_id) }, + { HP_POP(skill->read_autospell_skill_level, HP_skill_read_autospell_skill_level) }, + { HP_POP(skill->read_autospell_additional_fields, HP_skill_read_autospell_additional_fields) }, + { HP_POP(skill->autospell_db_entry_compare, HP_skill_autospell_db_entry_compare) }, + { HP_POP(skill->read_autospell_db, HP_skill_read_autospell_db) }, { HP_POP(skill->config_set_level, HP_skill_config_set_level) }, { HP_POP(skill->level_set_value, HP_skill_level_set_value) }, { HP_POP(skill->parse_row_producedb, HP_skill_parse_row_producedb) }, diff --git a/src/plugins/HPMHooking/HPMHooking_map.Hooks.inc b/src/plugins/HPMHooking/HPMHooking_map.Hooks.inc index 65a53b020ec..d0cbea1c529 100644 --- a/src/plugins/HPMHooking/HPMHooking_map.Hooks.inc +++ b/src/plugins/HPMHooking/HPMHooking_map.Hooks.inc @@ -12125,14 +12125,14 @@ void HP_clif_cooking_list(struct map_session_data *sd, int trigger, uint16 skill } return; } -void HP_clif_autospell(struct map_session_data *sd, uint16 skill_lv) { +void HP_clif_autospell(struct map_session_data *sd, uint16 skill_lv, int *skill_ids_list, int list_len) { int hIndex = 0; if (HPMHooks.count.HP_clif_autospell_pre > 0) { - void (*preHookFunc) (struct map_session_data **sd, uint16 *skill_lv); + void (*preHookFunc) (struct map_session_data **sd, uint16 *skill_lv, int **skill_ids_list, int *list_len); *HPMforce_return = false; for (hIndex = 0; hIndex < HPMHooks.count.HP_clif_autospell_pre; hIndex++) { preHookFunc = HPMHooks.list.HP_clif_autospell_pre[hIndex].func; - preHookFunc(&sd, &skill_lv); + preHookFunc(&sd, &skill_lv, &skill_ids_list, &list_len); } if (*HPMforce_return) { *HPMforce_return = false; @@ -12140,13 +12140,13 @@ void HP_clif_autospell(struct map_session_data *sd, uint16 skill_lv) { } } { - HPMHooks.source.clif.autospell(sd, skill_lv); + HPMHooks.source.clif.autospell(sd, skill_lv, skill_ids_list, list_len); } if (HPMHooks.count.HP_clif_autospell_post > 0) { - void (*postHookFunc) (struct map_session_data *sd, uint16 skill_lv); + void (*postHookFunc) (struct map_session_data *sd, uint16 skill_lv, int *skill_ids_list, int list_len); for (hIndex = 0; hIndex < HPMHooks.count.HP_clif_autospell_post; hIndex++) { postHookFunc = HPMHooks.list.HP_clif_autospell_post[hIndex].func; - postHookFunc(sd, skill_lv); + postHookFunc(sd, skill_lv, skill_ids_list, list_len); } } return; @@ -85337,14 +85337,66 @@ void HP_skill_weaponrefine(struct map_session_data *sd, int idx) { } return; } -int HP_skill_autospell(struct map_session_data *md, uint16 skill_id) { +void HP_skill_autospell_select_spell(struct block_list *bl, int skill_lv) { + int hIndex = 0; + if (HPMHooks.count.HP_skill_autospell_select_spell_pre > 0) { + void (*preHookFunc) (struct block_list **bl, int *skill_lv); + *HPMforce_return = false; + for (hIndex = 0; hIndex < HPMHooks.count.HP_skill_autospell_select_spell_pre; hIndex++) { + preHookFunc = HPMHooks.list.HP_skill_autospell_select_spell_pre[hIndex].func; + preHookFunc(&bl, &skill_lv); + } + if (*HPMforce_return) { + *HPMforce_return = false; + return; + } + } + { + HPMHooks.source.skill.autospell_select_spell(bl, skill_lv); + } + if (HPMHooks.count.HP_skill_autospell_select_spell_post > 0) { + void (*postHookFunc) (struct block_list *bl, int skill_lv); + for (hIndex = 0; hIndex < HPMHooks.count.HP_skill_autospell_select_spell_post; hIndex++) { + postHookFunc = HPMHooks.list.HP_skill_autospell_select_spell_post[hIndex].func; + postHookFunc(bl, skill_lv); + } + } + return; +} +void HP_skill_autospell_select_spell_pc(struct map_session_data *sd, int skill_lv) { + int hIndex = 0; + if (HPMHooks.count.HP_skill_autospell_select_spell_pc_pre > 0) { + void (*preHookFunc) (struct map_session_data **sd, int *skill_lv); + *HPMforce_return = false; + for (hIndex = 0; hIndex < HPMHooks.count.HP_skill_autospell_select_spell_pc_pre; hIndex++) { + preHookFunc = HPMHooks.list.HP_skill_autospell_select_spell_pc_pre[hIndex].func; + preHookFunc(&sd, &skill_lv); + } + if (*HPMforce_return) { + *HPMforce_return = false; + return; + } + } + { + HPMHooks.source.skill.autospell_select_spell_pc(sd, skill_lv); + } + if (HPMHooks.count.HP_skill_autospell_select_spell_pc_post > 0) { + void (*postHookFunc) (struct map_session_data *sd, int skill_lv); + for (hIndex = 0; hIndex < HPMHooks.count.HP_skill_autospell_select_spell_pc_post; hIndex++) { + postHookFunc = HPMHooks.list.HP_skill_autospell_select_spell_pc_post[hIndex].func; + postHookFunc(sd, skill_lv); + } + } + return; +} +int HP_skill_autospell_spell_selected(struct map_session_data *md, uint16 skill_id) { int hIndex = 0; int retVal___ = 0; - if (HPMHooks.count.HP_skill_autospell_pre > 0) { + if (HPMHooks.count.HP_skill_autospell_spell_selected_pre > 0) { int (*preHookFunc) (struct map_session_data **md, uint16 *skill_id); *HPMforce_return = false; - for (hIndex = 0; hIndex < HPMHooks.count.HP_skill_autospell_pre; hIndex++) { - preHookFunc = HPMHooks.list.HP_skill_autospell_pre[hIndex].func; + for (hIndex = 0; hIndex < HPMHooks.count.HP_skill_autospell_spell_selected_pre; hIndex++) { + preHookFunc = HPMHooks.list.HP_skill_autospell_spell_selected_pre[hIndex].func; retVal___ = preHookFunc(&md, &skill_id); } if (*HPMforce_return) { @@ -85353,12 +85405,12 @@ int HP_skill_autospell(struct map_session_data *md, uint16 skill_id) { } } { - retVal___ = HPMHooks.source.skill.autospell(md, skill_id); + retVal___ = HPMHooks.source.skill.autospell_spell_selected(md, skill_id); } - if (HPMHooks.count.HP_skill_autospell_post > 0) { + if (HPMHooks.count.HP_skill_autospell_spell_selected_post > 0) { int (*postHookFunc) (int retVal___, struct map_session_data *md, uint16 skill_id); - for (hIndex = 0; hIndex < HPMHooks.count.HP_skill_autospell_post; hIndex++) { - postHookFunc = HPMHooks.list.HP_skill_autospell_post[hIndex].func; + for (hIndex = 0; hIndex < HPMHooks.count.HP_skill_autospell_spell_selected_post; hIndex++) { + postHookFunc = HPMHooks.list.HP_skill_autospell_spell_selected_post[hIndex].func; retVal___ = postHookFunc(retVal___, md, skill_id); } } @@ -89115,6 +89167,138 @@ bool HP_skill_read_skilldb(const char *filename) { } return retVal___; } +void HP_skill_read_autospell_skill_id(struct config_setting_t *conf, struct s_autospell_db *sk, int index) { + int hIndex = 0; + if (HPMHooks.count.HP_skill_read_autospell_skill_id_pre > 0) { + void (*preHookFunc) (struct config_setting_t **conf, struct s_autospell_db **sk, int *index); + *HPMforce_return = false; + for (hIndex = 0; hIndex < HPMHooks.count.HP_skill_read_autospell_skill_id_pre; hIndex++) { + preHookFunc = HPMHooks.list.HP_skill_read_autospell_skill_id_pre[hIndex].func; + preHookFunc(&conf, &sk, &index); + } + if (*HPMforce_return) { + *HPMforce_return = false; + return; + } + } + { + HPMHooks.source.skill.read_autospell_skill_id(conf, sk, index); + } + if (HPMHooks.count.HP_skill_read_autospell_skill_id_post > 0) { + void (*postHookFunc) (struct config_setting_t *conf, struct s_autospell_db *sk, int index); + for (hIndex = 0; hIndex < HPMHooks.count.HP_skill_read_autospell_skill_id_post; hIndex++) { + postHookFunc = HPMHooks.list.HP_skill_read_autospell_skill_id_post[hIndex].func; + postHookFunc(conf, sk, index); + } + } + return; +} +void HP_skill_read_autospell_skill_level(struct config_setting_t *conf, struct s_autospell_db *sk) { + int hIndex = 0; + if (HPMHooks.count.HP_skill_read_autospell_skill_level_pre > 0) { + void (*preHookFunc) (struct config_setting_t **conf, struct s_autospell_db **sk); + *HPMforce_return = false; + for (hIndex = 0; hIndex < HPMHooks.count.HP_skill_read_autospell_skill_level_pre; hIndex++) { + preHookFunc = HPMHooks.list.HP_skill_read_autospell_skill_level_pre[hIndex].func; + preHookFunc(&conf, &sk); + } + if (*HPMforce_return) { + *HPMforce_return = false; + return; + } + } + { + HPMHooks.source.skill.read_autospell_skill_level(conf, sk); + } + if (HPMHooks.count.HP_skill_read_autospell_skill_level_post > 0) { + void (*postHookFunc) (struct config_setting_t *conf, struct s_autospell_db *sk); + for (hIndex = 0; hIndex < HPMHooks.count.HP_skill_read_autospell_skill_level_post; hIndex++) { + postHookFunc = HPMHooks.list.HP_skill_read_autospell_skill_level_post[hIndex].func; + postHookFunc(conf, sk); + } + } + return; +} +void HP_skill_read_autospell_additional_fields(struct config_setting_t *conf, struct s_autospell_db *sk) { + int hIndex = 0; + if (HPMHooks.count.HP_skill_read_autospell_additional_fields_pre > 0) { + void (*preHookFunc) (struct config_setting_t **conf, struct s_autospell_db **sk); + *HPMforce_return = false; + for (hIndex = 0; hIndex < HPMHooks.count.HP_skill_read_autospell_additional_fields_pre; hIndex++) { + preHookFunc = HPMHooks.list.HP_skill_read_autospell_additional_fields_pre[hIndex].func; + preHookFunc(&conf, &sk); + } + if (*HPMforce_return) { + *HPMforce_return = false; + return; + } + } + { + HPMHooks.source.skill.read_autospell_additional_fields(conf, sk); + } + if (HPMHooks.count.HP_skill_read_autospell_additional_fields_post > 0) { + void (*postHookFunc) (struct config_setting_t *conf, struct s_autospell_db *sk); + for (hIndex = 0; hIndex < HPMHooks.count.HP_skill_read_autospell_additional_fields_post; hIndex++) { + postHookFunc = HPMHooks.list.HP_skill_read_autospell_additional_fields_post[hIndex].func; + postHookFunc(conf, sk); + } + } + return; +} +int HP_skill_autospell_db_entry_compare(const void *entry1, const void *entry2) { + int hIndex = 0; + int retVal___ = 0; + if (HPMHooks.count.HP_skill_autospell_db_entry_compare_pre > 0) { + int (*preHookFunc) (const void **entry1, const void **entry2); + *HPMforce_return = false; + for (hIndex = 0; hIndex < HPMHooks.count.HP_skill_autospell_db_entry_compare_pre; hIndex++) { + preHookFunc = HPMHooks.list.HP_skill_autospell_db_entry_compare_pre[hIndex].func; + retVal___ = preHookFunc(&entry1, &entry2); + } + if (*HPMforce_return) { + *HPMforce_return = false; + return retVal___; + } + } + { + retVal___ = HPMHooks.source.skill.autospell_db_entry_compare(entry1, entry2); + } + if (HPMHooks.count.HP_skill_autospell_db_entry_compare_post > 0) { + int (*postHookFunc) (int retVal___, const void *entry1, const void *entry2); + for (hIndex = 0; hIndex < HPMHooks.count.HP_skill_autospell_db_entry_compare_post; hIndex++) { + postHookFunc = HPMHooks.list.HP_skill_autospell_db_entry_compare_post[hIndex].func; + retVal___ = postHookFunc(retVal___, entry1, entry2); + } + } + return retVal___; +} +bool HP_skill_read_autospell_db(const char *filename) { + int hIndex = 0; + bool retVal___ = false; + if (HPMHooks.count.HP_skill_read_autospell_db_pre > 0) { + bool (*preHookFunc) (const char **filename); + *HPMforce_return = false; + for (hIndex = 0; hIndex < HPMHooks.count.HP_skill_read_autospell_db_pre; hIndex++) { + preHookFunc = HPMHooks.list.HP_skill_read_autospell_db_pre[hIndex].func; + retVal___ = preHookFunc(&filename); + } + if (*HPMforce_return) { + *HPMforce_return = false; + return retVal___; + } + } + { + retVal___ = HPMHooks.source.skill.read_autospell_db(filename); + } + if (HPMHooks.count.HP_skill_read_autospell_db_post > 0) { + bool (*postHookFunc) (bool retVal___, const char *filename); + for (hIndex = 0; hIndex < HPMHooks.count.HP_skill_read_autospell_db_post; hIndex++) { + postHookFunc = HPMHooks.list.HP_skill_read_autospell_db_post[hIndex].func; + retVal___ = postHookFunc(retVal___, filename); + } + } + return retVal___; +} void HP_skill_config_set_level(struct config_setting_t *conf, int *arr) { int hIndex = 0; if (HPMHooks.count.HP_skill_config_set_level_pre > 0) {