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) {