From 451c0804fb72d5c4131c191f2f98dd9d7d9efbd7 Mon Sep 17 00:00:00 2001
From: Kenpachi Developer <Kenpachi.Developer@gmx.de>
Date: Sun, 17 May 2020 07:05:01 +0200
Subject: Make Unit->Target can be grouped by levels

---
 src/map/mob.c                                   |   2 +-
 src/map/skill.c                                 | 192 ++++++++++++++++--------
 src/map/skill.h                                 |   7 +-
 src/plugins/HPMHooking/HPMHooking.Defs.inc      |   8 +-
 src/plugins/HPMHooking/HPMHooking_map.Hooks.inc |  24 +--
 5 files changed, 152 insertions(+), 81 deletions(-)

diff --git a/src/map/mob.c b/src/map/mob.c
index df6deb866..056a1a8b1 100644
--- a/src/map/mob.c
+++ b/src/map/mob.c
@@ -3904,7 +3904,7 @@ static int mob_clone_spawn(struct map_session_data *sd, int16 m, int16 x, int16
 				mob_skills[i].state = MSS_IDLE;
 				mob_skills[i].target = MST_AROUND2;
 				mob_skills[i].delay = 60000;
-			} else if (skill->get_unit_target(skill_id) == BCT_ENEMY) { /// Target Enemy.
+			} else if (skill->get_unit_target(skill_id, sd->status.skill[idx].lv) == BCT_ENEMY) { /// Target Enemy.
 				mob_skills[i].state = MSS_ANYTARGET;
 				mob_skills[i].target = MST_TARGET;
 				mob_skills[i].cond1 = MSC_ALWAYS;
diff --git a/src/map/skill.c b/src/map/skill.c
index d57931b54..9b9c17b21 100644
--- a/src/map/skill.c
+++ b/src/map/skill.c
@@ -743,24 +743,48 @@ static int skill_get_unit_range(int skill_id, int skill_lv)
 	return skill->dbs->db[idx].unit_range[skill_get_lvl_idx(skill_lv)];
 }
 
-static int skill_get_unit_target(int skill_id)
+/**
+ * Gets a skill's unit target by its ID and level.
+ *
+ * @param skill_id The skill's ID.
+ * @param skill_lv The skill's level.
+ * @return The skill's unit target corresponding to the passed level. Defaults to BCT_NOONE (0) in case of error.
+ *
+ **/
+static int skill_get_unit_target(int skill_id, int skill_lv)
 {
-	int idx;
 	if (skill_id == 0)
 		return BCT_NOONE;
-	idx = skill->get_index(skill_id);
+
+	Assert_retr(BCT_NOONE, skill_lv > 0);
+
+	int idx = skill->get_index(skill_id);
+
 	Assert_retr(BCT_NOONE, idx != 0);
-	return skill->dbs->db[idx].unit_target & BCT_ALL;
+
+	return (skill->dbs->db[idx].unit_target[skill_get_lvl_idx(skill_lv)] & BCT_ALL);
 }
 
-static int skill_get_unit_bl_target(int skill_id)
+/**
+ * Gets a skill's unit target as bl type by its ID and level.
+ *
+ * @param skill_id The skill's ID.
+ * @param skill_lv The skill's level.
+ * @return The skill's unit target as bl type corresponding to the passed level. Defaults to BL_NUL (0) in case of error.
+ *
+ **/
+static int skill_get_unit_bl_target(int skill_id, int skill_lv)
 {
-	int idx;
 	if (skill_id == 0)
 		return BL_NUL;
-	idx = skill->get_index(skill_id);
-	Assert_retr(BL_NUL, idx != 0);
-	return skill->dbs->db[idx].unit_target & BL_ALL;
+
+	Assert_retr(BCT_NOONE, skill_lv > 0);
+
+	int idx = skill->get_index(skill_id);
+
+	Assert_retr(BCT_NOONE, idx != 0);
+
+	return (skill->dbs->db[idx].unit_target[skill_get_lvl_idx(skill_lv)] & BL_ALL);
 }
 
 static int skill_get_unit_flag(int skill_id)
@@ -12115,8 +12139,8 @@ static bool skill_dance_switch(struct skill_unit *su, int flag)
 		group->skill_id    = skill_id;
 		group->skill_lv    = 1;
 		group->unit_id     = skill->get_unit_id(skill_id, 1, 0);
-		group->target_flag = skill->get_unit_target(skill_id);
-		group->bl_flag     = skill->get_unit_bl_target(skill_id);
+		group->target_flag = skill->get_unit_target(skill_id, 1);
+		group->bl_flag     = skill->get_unit_bl_target(skill_id, 1);
 		group->interval    = skill->get_unit_interval(skill_id, 1);
 	} else {
 		//Restore
@@ -12151,7 +12175,7 @@ static struct skill_unit_group *skill_unitsetting(struct block_list *src, uint16
 	limit = skill->get_time(skill_id,skill_lv);
 	range = skill->get_unit_range(skill_id,skill_lv);
 	interval = skill->get_unit_interval(skill_id, skill_lv);
-	target = skill->get_unit_target(skill_id);
+	target = skill->get_unit_target(skill_id, skill_lv);
 	unit_flag = skill->get_unit_flag(skill_id);
 	layout = skill->get_unit_layout(skill_id,skill_lv,src,x,y);
 
@@ -12520,7 +12544,7 @@ static struct skill_unit_group *skill_unitsetting(struct block_list *src, uint16
 	group->val2=val2;
 	group->val3=val3;
 	group->target_flag=target;
-	group->bl_flag= skill->get_unit_bl_target(skill_id);
+	group->bl_flag= skill->get_unit_bl_target(skill_id, skill_lv);
 	group->state.ammo_consume = (sd && sd->state.arrow_atk && skill_id != GS_GROUNDDRIFT); //Store if this skill needs to consume ammo.
 	group->state.song_dance = ((unit_flag&(UF_DANCE|UF_SONG)) ? 1 : 0)|((unit_flag&UF_ENSEMBLE) ? 2 : 0); //Signals if this is a song/dance/duet
 	group->state.guildaura = ( skill_id >= GD_LEADERSHIP && skill_id <= GD_HAWKEYES )?1:0;
@@ -22842,10 +22866,51 @@ static void skill_validate_unit_flag(struct config_setting_t *conf, struct s_ski
 }
 
 /**
- * Validates a skill's unit target when reading the skill DB.
+ * Validates a single unit target when reading the skill DB.
+ *
+ * @param target The unit target to validate.
+ * @return A number greater than or equal to 0 if the passed unit target is valid, otherwise -1.
+ *
+ **/
+static int skill_validate_unit_target_sub(const char *target)
+{
+	nullpo_retr(-1, target);
+
+	int ret_val = BCT_NOONE;
+
+	if (strcmpi(target, "NotEnemy") == 0)
+		ret_val = BCT_NOENEMY;
+	else if (strcmpi(target, "NotParty") == 0)
+		ret_val = BCT_NOPARTY;
+	else if (strcmpi(target, "NotGuild") == 0)
+		ret_val = BCT_NOGUILD;
+	else if (strcmpi(target, "Friend") == 0)
+		ret_val = BCT_NOENEMY;
+	else if (strcmpi(target, "Party") == 0)
+		ret_val = BCT_PARTY;
+	else if (strcmpi(target, "Ally") == 0)
+		ret_val = BCT_PARTY|BCT_GUILD;
+	else if (strcmpi(target, "Guild") == 0)
+		ret_val = BCT_GUILD;
+	else if (strcmpi(target, "All") == 0)
+		ret_val = BCT_ALL;
+	else if (strcmpi(target, "Enemy") == 0)
+		ret_val = BCT_ENEMY;
+	else if (strcmpi(target, "Self") == 0)
+		ret_val = BCT_SELF;
+	else if (strcmpi(target, "SameGuild") == 0)
+		ret_val = BCT_SAMEGUILD;
+	else if (strcmpi(target, "None") != 0)
+		ret_val = -1;
+
+	return ret_val;
+}
+
+/**
+ * Validates a skill's unit targets when reading the skill DB.
  *
  * @param conf The libconfig settings block which contains the skill's data.
- * @param sk The s_skill_db struct where the unit target should be set it.
+ * @param sk The s_skill_db struct where the unit targets should be set it.
  *
  **/
 static void skill_validate_unit_target(struct config_setting_t *conf, struct s_skill_db *sk)
@@ -22853,52 +22918,56 @@ static void skill_validate_unit_target(struct config_setting_t *conf, struct s_s
 	nullpo_retv(conf);
 	nullpo_retv(sk);
 
-	sk->unit_target = BCT_NOONE;
-
-	const char *unit_target;
-
-	if (libconfig->setting_lookup_string(conf, "Target", &unit_target) == CONFIG_TRUE) {
-		if (strcmpi(unit_target, "NotEnemy") == 0)
-			sk->unit_target = BCT_NOENEMY;
-		else if (strcmpi(unit_target, "NotParty") == 0)
-			sk->unit_target = BCT_NOPARTY;
-		else if (strcmpi(unit_target, "NotGuild") == 0)
-			sk->unit_target = BCT_NOGUILD;
-		else if (strcmpi(unit_target, "Friend") == 0)
-			sk->unit_target = BCT_NOENEMY;
-		else if (strcmpi(unit_target, "Party") == 0)
-			sk->unit_target = BCT_PARTY;
-		else if (strcmpi(unit_target, "Ally") == 0)
-			sk->unit_target = BCT_PARTY|BCT_GUILD;
-		else if (strcmpi(unit_target, "Guild") == 0)
-			sk->unit_target = BCT_GUILD;
-		else if (strcmpi(unit_target, "All") == 0)
-			sk->unit_target = BCT_ALL;
-		else if (strcmpi(unit_target, "Enemy") == 0)
-			sk->unit_target = BCT_ENEMY;
-		else if (strcmpi(unit_target, "Self") == 0)
-			sk->unit_target = BCT_SELF;
-		else if (strcmpi(unit_target, "SameGuild") == 0)
-			sk->unit_target = BCT_SAMEGUILD;
-		else if (strcmpi(unit_target, "None") != 0)
-			ShowWarning("%s: Invalid unit target %s specified for skill ID %d in %s! Defaulting to None...\n",
-				    __func__, unit_target, sk->nameid, conf->file);
-	}
-
-	if ((sk->unit_flag & UF_DEFNOTENEMY) != 0 && battle_config.defnotenemy != 0)
-		sk->unit_target = BCT_NOENEMY;
-
-	// By default target just characters.
-	sk->unit_target |= BL_CHAR;
-
-	if ((sk->unit_flag & UF_NOPC) != 0)
-		sk->unit_target &= ~BL_PC;
-
-	if ((sk->unit_flag & UF_NOMOB) != 0)
-		sk->unit_target &= ~BL_MOB;
-
-	if ((sk->unit_flag & UF_SKILL) != 0)
-		sk->unit_target |= BL_SKILL;
+	skill->level_set_value(sk->unit_target, BCT_NOONE);
+
+	struct config_setting_t *t = libconfig->setting_get_member(conf, "Target");
+
+	if (t != NULL && config_setting_is_group(t)) {
+		for (int i = 0; i < MAX_SKILL_LEVEL; i++) {
+			char lv[6]; // Big enough to contain "Lv999" in case of custom MAX_SKILL_LEVEL.
+			safesnprintf(lv, sizeof(lv), "Lv%d", i + 1);
+			const char *unit_target;
+
+			if (libconfig->setting_lookup_string(t, lv, &unit_target) == CONFIG_TRUE) {
+				int target = skill->validate_unit_target_sub(unit_target);
+
+				if (target > BCT_NOONE)
+					sk->unit_target[i] = target;
+				else if (target == -1)
+					ShowWarning("%s: Invalid unit target %s specified in level %d for skill ID %d in %s! Defaulting to None...\n",
+						    __func__, unit_target, i + 1, sk->nameid, conf->file);
+			}
+		}
+	} else {
+		const char *unit_target;
+
+		if (libconfig->setting_lookup_string(conf, "Target", &unit_target) == CONFIG_TRUE) {
+			int target = skill->validate_unit_target_sub(unit_target);
+
+			if (target > BCT_NOONE)
+				skill->level_set_value(sk->unit_target, target);
+			else if (target == -1)
+				ShowWarning("%s: Invalid unit target %s specified for skill ID %d in %s! Defaulting to None...\n",
+					    __func__, unit_target, sk->nameid, conf->file);
+		}
+	}
+
+	for (int i = 0; i < MAX_SKILL_LEVEL; i++) {
+		if ((sk->unit_flag & UF_DEFNOTENEMY) != 0 && battle_config.defnotenemy != 0)
+			sk->unit_target[i] = BCT_NOENEMY;
+
+		// By default target just characters.
+		sk->unit_target[i] |= BL_CHAR;
+
+		if ((sk->unit_flag & UF_NOPC) != 0)
+			sk->unit_target[i] &= ~BL_PC;
+
+		if ((sk->unit_flag & UF_NOMOB) != 0)
+			sk->unit_target[i] &= ~BL_MOB;
+
+		if ((sk->unit_flag & UF_SKILL) != 0)
+			sk->unit_target[i] |= BL_SKILL;
+	}
 }
 
 /**
@@ -23410,6 +23479,7 @@ void skill_defaults(void)
 	skill->validate_unit_interval = skill_validate_unit_interval;
 	skill->validate_unit_flag_sub = skill_validate_unit_flag_sub;
 	skill->validate_unit_flag = skill_validate_unit_flag;
+	skill->validate_unit_target_sub = skill_validate_unit_target_sub;
 	skill->validate_unit_target = skill_validate_unit_target;
 	skill->validate_unit = skill_validate_unit;
 	skill->validate_additional_fields = skill_validate_additional_fields;
diff --git a/src/map/skill.h b/src/map/skill.h
index c32a8b639..2d68e695d 100644
--- a/src/map/skill.h
+++ b/src/map/skill.h
@@ -1782,7 +1782,7 @@ struct s_skill_db {
 	int unit_layout_type[MAX_SKILL_LEVEL];
 	int unit_range[MAX_SKILL_LEVEL];
 	int unit_interval[MAX_SKILL_LEVEL];
-	int unit_target;
+	int unit_target[MAX_SKILL_LEVEL];
 	int unit_flag;
 };
 
@@ -2006,9 +2006,9 @@ struct skill_interface {
 	int (*get_maxcount) (int skill_id, int skill_lv);
 	int (*get_blewcount) (int skill_id, int skill_lv);
 	int (*get_unit_flag) (int skill_id);
-	int (*get_unit_target) (int skill_id);
+	int (*get_unit_target) (int skill_id, int skill_lv);
 	int (*get_unit_interval) (int skill_id, int skill_lv);
-	int (*get_unit_bl_target) (int skill_id);
+	int (*get_unit_bl_target) (int skill_id, int skill_lv);
 	int (*get_unit_layout_type) (int skill_id, int skill_lv);
 	int (*get_unit_range) (int skill_id, int skill_lv);
 	int (*get_cooldown) (int skill_id, int skill_lv);
@@ -2180,6 +2180,7 @@ struct skill_interface {
 	void (*validate_unit_interval) (struct config_setting_t *conf, struct s_skill_db *sk);
 	int (*validate_unit_flag_sub) (const char *type, bool on, struct s_skill_db *sk);
 	void (*validate_unit_flag) (struct config_setting_t *conf,  struct s_skill_db *sk);
+	int (*validate_unit_target_sub) (const char *target);
 	void (*validate_unit_target) (struct config_setting_t *conf, struct s_skill_db *sk);
 	void (*validate_unit) (struct config_setting_t *conf, struct s_skill_db *sk);
 	void (*validate_additional_fields) (struct config_setting_t *conf, struct s_skill_db *sk);
diff --git a/src/plugins/HPMHooking/HPMHooking.Defs.inc b/src/plugins/HPMHooking/HPMHooking.Defs.inc
index e75dbaa97..645afa240 100644
--- a/src/plugins/HPMHooking/HPMHooking.Defs.inc
+++ b/src/plugins/HPMHooking/HPMHooking.Defs.inc
@@ -7304,12 +7304,12 @@ typedef int (*HPMHOOK_pre_skill_get_blewcount) (int *skill_id, int *skill_lv);
 typedef int (*HPMHOOK_post_skill_get_blewcount) (int retVal___, int skill_id, int skill_lv);
 typedef int (*HPMHOOK_pre_skill_get_unit_flag) (int *skill_id);
 typedef int (*HPMHOOK_post_skill_get_unit_flag) (int retVal___, int skill_id);
-typedef int (*HPMHOOK_pre_skill_get_unit_target) (int *skill_id);
-typedef int (*HPMHOOK_post_skill_get_unit_target) (int retVal___, int skill_id);
+typedef int (*HPMHOOK_pre_skill_get_unit_target) (int *skill_id, int *skill_lv);
+typedef int (*HPMHOOK_post_skill_get_unit_target) (int retVal___, int skill_id, int skill_lv);
 typedef int (*HPMHOOK_pre_skill_get_unit_interval) (int *skill_id, int *skill_lv);
 typedef int (*HPMHOOK_post_skill_get_unit_interval) (int retVal___, int skill_id, int skill_lv);
-typedef int (*HPMHOOK_pre_skill_get_unit_bl_target) (int *skill_id);
-typedef int (*HPMHOOK_post_skill_get_unit_bl_target) (int retVal___, int skill_id);
+typedef int (*HPMHOOK_pre_skill_get_unit_bl_target) (int *skill_id, int *skill_lv);
+typedef int (*HPMHOOK_post_skill_get_unit_bl_target) (int retVal___, int skill_id, int skill_lv);
 typedef int (*HPMHOOK_pre_skill_get_unit_layout_type) (int *skill_id, int *skill_lv);
 typedef int (*HPMHOOK_post_skill_get_unit_layout_type) (int retVal___, int skill_id, int skill_lv);
 typedef int (*HPMHOOK_pre_skill_get_unit_range) (int *skill_id, int *skill_lv);
diff --git a/src/plugins/HPMHooking/HPMHooking_map.Hooks.inc b/src/plugins/HPMHooking/HPMHooking_map.Hooks.inc
index 80c309a8c..1e29af351 100644
--- a/src/plugins/HPMHooking/HPMHooking_map.Hooks.inc
+++ b/src/plugins/HPMHooking/HPMHooking_map.Hooks.inc
@@ -77768,15 +77768,15 @@ int HP_skill_get_unit_flag(int skill_id) {
 	}
 	return retVal___;
 }
-int HP_skill_get_unit_target(int skill_id) {
+int HP_skill_get_unit_target(int skill_id, int skill_lv) {
 	int hIndex = 0;
 	int retVal___ = 0;
 	if (HPMHooks.count.HP_skill_get_unit_target_pre > 0) {
-		int (*preHookFunc) (int *skill_id);
+		int (*preHookFunc) (int *skill_id, int *skill_lv);
 		*HPMforce_return = false;
 		for (hIndex = 0; hIndex < HPMHooks.count.HP_skill_get_unit_target_pre; hIndex++) {
 			preHookFunc = HPMHooks.list.HP_skill_get_unit_target_pre[hIndex].func;
-			retVal___ = preHookFunc(&skill_id);
+			retVal___ = preHookFunc(&skill_id, &skill_lv);
 		}
 		if (*HPMforce_return) {
 			*HPMforce_return = false;
@@ -77784,13 +77784,13 @@ int HP_skill_get_unit_target(int skill_id) {
 		}
 	}
 	{
-		retVal___ = HPMHooks.source.skill.get_unit_target(skill_id);
+		retVal___ = HPMHooks.source.skill.get_unit_target(skill_id, skill_lv);
 	}
 	if (HPMHooks.count.HP_skill_get_unit_target_post > 0) {
-		int (*postHookFunc) (int retVal___, int skill_id);
+		int (*postHookFunc) (int retVal___, int skill_id, int skill_lv);
 		for (hIndex = 0; hIndex < HPMHooks.count.HP_skill_get_unit_target_post; hIndex++) {
 			postHookFunc = HPMHooks.list.HP_skill_get_unit_target_post[hIndex].func;
-			retVal___ = postHookFunc(retVal___, skill_id);
+			retVal___ = postHookFunc(retVal___, skill_id, skill_lv);
 		}
 	}
 	return retVal___;
@@ -77822,15 +77822,15 @@ int HP_skill_get_unit_interval(int skill_id, int skill_lv) {
 	}
 	return retVal___;
 }
-int HP_skill_get_unit_bl_target(int skill_id) {
+int HP_skill_get_unit_bl_target(int skill_id, int skill_lv) {
 	int hIndex = 0;
 	int retVal___ = 0;
 	if (HPMHooks.count.HP_skill_get_unit_bl_target_pre > 0) {
-		int (*preHookFunc) (int *skill_id);
+		int (*preHookFunc) (int *skill_id, int *skill_lv);
 		*HPMforce_return = false;
 		for (hIndex = 0; hIndex < HPMHooks.count.HP_skill_get_unit_bl_target_pre; hIndex++) {
 			preHookFunc = HPMHooks.list.HP_skill_get_unit_bl_target_pre[hIndex].func;
-			retVal___ = preHookFunc(&skill_id);
+			retVal___ = preHookFunc(&skill_id, &skill_lv);
 		}
 		if (*HPMforce_return) {
 			*HPMforce_return = false;
@@ -77838,13 +77838,13 @@ int HP_skill_get_unit_bl_target(int skill_id) {
 		}
 	}
 	{
-		retVal___ = HPMHooks.source.skill.get_unit_bl_target(skill_id);
+		retVal___ = HPMHooks.source.skill.get_unit_bl_target(skill_id, skill_lv);
 	}
 	if (HPMHooks.count.HP_skill_get_unit_bl_target_post > 0) {
-		int (*postHookFunc) (int retVal___, int skill_id);
+		int (*postHookFunc) (int retVal___, int skill_id, int skill_lv);
 		for (hIndex = 0; hIndex < HPMHooks.count.HP_skill_get_unit_bl_target_post; hIndex++) {
 			postHookFunc = HPMHooks.list.HP_skill_get_unit_bl_target_post[hIndex].func;
-			retVal___ = postHookFunc(retVal___, skill_id);
+			retVal___ = postHookFunc(retVal___, skill_id, skill_lv);
 		}
 	}
 	return retVal___;
-- 
cgit v1.2.3-70-g09d2