Explorar o código

Apply suggestions from code review

Co-authored-by: Aleos <aleos89@users.noreply.github.com>
Atemo %!s(int64=3) %!d(string=hai) anos
pai
achega
df115b730a

+ 6 - 10
db/import-tmpl/mob_skill_db.yml

@@ -1,5 +1,5 @@
 # This file is a part of rAthena.
-#   Copyright(C) 2021 rAthena Development Team
+#   Copyright(C) 2022 rAthena Development Team
 #   https://rathena.org - https://github.com/rathena
 #
 # This program is free software: you can redistribute it and/or modify
@@ -24,31 +24,27 @@
 ###########################################################################
 # - Mob                     Monster AegisName.
 #   Skills:                 List of Skills casted by the monster.
-#    - Index                Unique Index in the list to edit the skill in import.
+#    - Index                Unique Index in the list to edit the skill on import.
 #      Name                 Skill AegisName.
 #      Level                Skill Level.
-#      State                State of the monster to cast the skill (Default: ATTACK).
+#      State                State of the monster to cast the skill. (Default: ATTACK)
 #      CastRate             Rate to cast the skill (from 1-10000). (Default: 10000)
 #      CastTime             Time in millisecond to cast the skill. (Default: 0)
 #      CastDelay            Delay in millisecond to recast the skill. (Default: 5000)
 #      CastCancel           Whether the skill is cancelable or not. (Default: true)
 #      Target               Target of the skill. (Default: TARGET)
 #      Condition            Type of condition. (Default: ALWAYS)
-#      ConditionValue1      Values given to certain conditions.
-#      ConditionValue2      Values given to certain conditions.
+#      ConditionValue1      Value given at certain conditions.
+#      ConditionValue2      Value given at certain conditions.
 #      Ai                   Change the monster mode using the given Aegis monster type AI. (Default: 00)
 #      Summon:              List of monsters to summon, required for certain skills. (Default: null) 
-#        - Index            Unique Index in the list to edit the monster in import. Range 0-6.
+#        - Index            Unique Index in the list to edit the monster on import. Range of 0-5.
 #          Mob              Monster AegisName.
 #          Clear            True to remove the given monster at this index. (Optional)
 #      Emotion              Emotion displayed on the monster after the cast. (Default: ET_NONE)
 #      Chat                 Chat ID to display the associated sentence defined in mob_chat_db.yml. (Default: 0)
 #      Clear                True to remove the skill. (Optional)
 ###########################################################################
-#
-# Read doc/mob_skill_db.txt for more information.
-#
-###########################################################################
 
 Header:
   Type: MOB_SKILL_DB

+ 6 - 10
db/mob_skill_db.yml

@@ -1,5 +1,5 @@
 # This file is a part of rAthena.
-#   Copyright(C) 2021 rAthena Development Team
+#   Copyright(C) 2022 rAthena Development Team
 #   https://rathena.org - https://github.com/rathena
 #
 # This program is free software: you can redistribute it and/or modify
@@ -24,31 +24,27 @@
 ###########################################################################
 # - Mob                     Monster AegisName.
 #   Skills:                 List of Skills casted by the monster.
-#    - Index                Unique Index in the list to edit the skill in import.
+#    - Index                Unique Index in the list to edit the skill on import.
 #      Name                 Skill AegisName.
 #      Level                Skill Level.
-#      State                State of the monster to cast the skill (Default: ATTACK).
+#      State                State of the monster to cast the skill. (Default: ATTACK)
 #      CastRate             Rate to cast the skill (from 1-10000). (Default: 10000)
 #      CastTime             Time in millisecond to cast the skill. (Default: 0)
 #      CastDelay            Delay in millisecond to recast the skill. (Default: 5000)
 #      CastCancel           Whether the skill is cancelable or not. (Default: true)
 #      Target               Target of the skill. (Default: TARGET)
 #      Condition            Type of condition. (Default: ALWAYS)
-#      ConditionValue1      Values given to certain conditions.
-#      ConditionValue2      Values given to certain conditions.
+#      ConditionValue1      Value given at certain conditions.
+#      ConditionValue2      Value given at certain conditions.
 #      Ai                   Change the monster mode using the given Aegis monster type AI. (Default: 00)
 #      Summon:              List of monsters to summon, required for certain skills. (Default: null) 
-#        - Index            Unique Index in the list to edit the monster in import. Range 0-6.
+#        - Index            Unique Index in the list to edit the monster on import. Range of 0-5.
 #          Mob              Monster AegisName.
 #          Clear            True to remove the given monster at this index. (Optional)
 #      Emotion              Emotion displayed on the monster after the cast. (Default: ET_NONE)
 #      Chat                 Chat ID to display the associated sentence defined in mob_chat_db.yml. (Default: 0)
 #      Clear                True to remove the skill. (Optional)
 ###########################################################################
-#
-# Read doc/mob_skill_db.txt for more information.
-#
-###########################################################################
 
 Header:
   Type: MOB_SKILL_DB

+ 6 - 10
db/pre-re/mob_skill_db.yml

@@ -1,5 +1,5 @@
 # This file is a part of rAthena.
-#   Copyright(C) 2021 rAthena Development Team
+#   Copyright(C) 2022 rAthena Development Team
 #   https://rathena.org - https://github.com/rathena
 #
 # This program is free software: you can redistribute it and/or modify
@@ -24,31 +24,27 @@
 ###########################################################################
 # - Mob                     Monster AegisName.
 #   Skills:                 List of Skills casted by the monster.
-#    - Index                Unique Index in the list to edit the skill in import.
+#    - Index                Unique Index in the list to edit the skill on import.
 #      Name                 Skill AegisName.
 #      Level                Skill Level.
-#      State                State of the monster to cast the skill (Default: ATTACK).
+#      State                State of the monster to cast the skill. (Default: ATTACK)
 #      CastRate             Rate to cast the skill (from 1-10000). (Default: 10000)
 #      CastTime             Time in millisecond to cast the skill. (Default: 0)
 #      CastDelay            Delay in millisecond to recast the skill. (Default: 5000)
 #      CastCancel           Whether the skill is cancelable or not. (Default: true)
 #      Target               Target of the skill. (Default: TARGET)
 #      Condition            Type of condition. (Default: ALWAYS)
-#      ConditionValue1      Values given to certain conditions.
-#      ConditionValue2      Values given to certain conditions.
+#      ConditionValue1      Value given at certain conditions.
+#      ConditionValue2      Value given at certain conditions.
 #      Ai                   Change the monster mode using the given Aegis monster type AI. (Default: 00)
 #      Summon:              List of monsters to summon, required for certain skills. (Default: null) 
-#        - Index            Unique Index in the list to edit the monster in import. Range 0-6.
+#        - Index            Unique Index in the list to edit the monster on import. Range of 0-5.
 #          Mob              Monster AegisName.
 #          Clear            True to remove the given monster at this index. (Optional)
 #      Emotion              Emotion displayed on the monster after the cast. (Default: ET_NONE)
 #      Chat                 Chat ID to display the associated sentence defined in mob_chat_db.yml. (Default: 0)
 #      Clear                True to remove the skill. (Optional)
 ###########################################################################
-#
-# Read doc/mob_skill_db.txt for more information.
-#
-###########################################################################
 
 Header:
   Type: MOB_SKILL_DB

+ 6 - 10
db/re/mob_skill_db.yml

@@ -1,5 +1,5 @@
 # This file is a part of rAthena.
-#   Copyright(C) 2021 rAthena Development Team
+#   Copyright(C) 2022 rAthena Development Team
 #   https://rathena.org - https://github.com/rathena
 #
 # This program is free software: you can redistribute it and/or modify
@@ -24,31 +24,27 @@
 ###########################################################################
 # - Mob                     Monster AegisName.
 #   Skills:                 List of Skills casted by the monster.
-#    - Index                Unique Index in the list to edit the skill in import.
+#    - Index                Unique Index in the list to edit the skill on import.
 #      Name                 Skill AegisName.
 #      Level                Skill Level.
-#      State                State of the monster to cast the skill (Default: ATTACK).
+#      State                State of the monster to cast the skill. (Default: ATTACK)
 #      CastRate             Rate to cast the skill (from 1-10000). (Default: 10000)
 #      CastTime             Time in millisecond to cast the skill. (Default: 0)
 #      CastDelay            Delay in millisecond to recast the skill. (Default: 5000)
 #      CastCancel           Whether the skill is cancelable or not. (Default: true)
 #      Target               Target of the skill. (Default: TARGET)
 #      Condition            Type of condition. (Default: ALWAYS)
-#      ConditionValue1      Values given to certain conditions.
-#      ConditionValue2      Values given to certain conditions.
+#      ConditionValue1      Value given at certain conditions.
+#      ConditionValue2      Value given at certain conditions.
 #      Ai                   Change the monster mode using the given Aegis monster type AI. (Default: 00)
 #      Summon:              List of monsters to summon, required for certain skills. (Default: null) 
-#        - Index            Unique Index in the list to edit the monster in import. Range 0-6.
+#        - Index            Unique Index in the list to edit the monster on import. Range of 0-5.
 #          Mob              Monster AegisName.
 #          Clear            True to remove the given monster at this index. (Optional)
 #      Emotion              Emotion displayed on the monster after the cast. (Default: ET_NONE)
 #      Chat                 Chat ID to display the associated sentence defined in mob_chat_db.yml. (Default: 0)
 #      Clear                True to remove the skill. (Optional)
 ###########################################################################
-#
-# Read doc/mob_skill_db.txt for more information.
-#
-###########################################################################
 
 Header:
   Type: MOB_SKILL_DB

+ 15 - 9
doc/mob_skill_db.txt

@@ -12,7 +12,7 @@
 
 Mob: The Aegis name of the monster (SpriteName).
 
-Note: if the following name are provided, the skills will be treated as 'global':
+Note: If one of the following names is provided, the skills will be treated as 'global':
 ALL_BOSS		Added for all boss types.
 ALL_NORMAL		Added for all normal types.
 ALL				Added for all mobs.
@@ -24,7 +24,7 @@ Skills: List of Skills casted by the monster. The max number of skills a monster
 
 ---------------------------------------
 
-Index: Unique Index in the list to edit the skill in import. Range 0-65535.
+Index: Unique Index in the list to edit the skill on import. Range 0-65535.
 
 ---------------------------------------
 
@@ -36,7 +36,9 @@ Level: The monster will cast the skill at the given Level.
 
 ---------------------------------------
 
-State: State of the monster to cast the skill. Valids input are :
+State: State of the monster to cast the skill.
+
+Valid options:
 
 ANY (except dead)
 IDLE (in standby)
@@ -67,7 +69,9 @@ CastCancel: Whether the skill can be canceled when the monster is hit. (true/fal
 
 ---------------------------------------
 
-Target: Target of the skill. Valids input are :
+Target: Target of the skill.
+
+Valid options:
 
 TARGET			(current target)
 SELF
@@ -88,7 +92,9 @@ AROUND			= AROUND4
 
 ---------------------------------------
 
-Condition: Type of condition. Valids input are :
+Condition: Type of condition.
+
+Valid options:
 
 ALWAYS				(no condition value)			Unconditional.
 ONSPAWN				(no condition value)			When mob spawns/respawns.
@@ -116,7 +122,7 @@ RUDEATTACKED		(no condition value)			When mob is rude attacked.
 
 ---------------------------------------
 
-ConditionValue1: Values required to certain conditions.
+ConditionValue1: Value required at certain conditions.
 
 Status abnormalities accepted through MYSTATUSON / MYSTATUSOFF / FRIENDSTATUSON / FRIENDSTATUSOFF for ConditionValue1:
 SC_ANYBAD (any type of state change)
@@ -132,7 +138,7 @@ SC_BLIND
 SC_HIDING
 SC_SIGHT (unhidden)
 
-When the condition are SKILLUSED or AFTERSKILL, ConditionValue1 requires an Aegis skill name.
+When the Condition is SKILLUSED or AFTERSKILL, ConditionValue1 requires an Aegis skill name.
 ConditionValue1 requires a number for all other conditions.
 
 ---------------------------------------
@@ -146,8 +152,8 @@ Ai: Change the monster mode using the given Aegis monster type AI.
 
 ---------------------------------------
 
-Summon: List of monsters to summon, currently required for NPC_METAMORPHOSIS, NPC_SUMMONSLAVE, NPC_SUMMONMONSTER and NPC_DEATHSUMMON skills.
-    - Index            Unique Index in the list to edit the monster in import. Range 0-6.
+Summon: List of monsters to summon. Required for NPC_METAMORPHOSIS, NPC_SUMMONSLAVE, NPC_SUMMONMONSTER, and NPC_DEATHSUMMON skills.
+    - Index            Unique Index in the list to edit the monster on import. Range of 0-5.
       Mob              Monster AegisName.
       Clear            True to remove the given monster at this index. (Optional)
 

+ 5 - 9
doc/yaml/db/mob_skill_db.yml

@@ -7,28 +7,24 @@
 ###########################################################################
 # - Mob                     Monster AegisName.
 #   Skills:                 List of Skills casted by the monster.
-#    - Index                Unique Index in the list to edit the skill in import.
+#    - Index                Unique Index in the list to edit the skill on import.
 #      Name                 Skill AegisName.
 #      Level                Skill Level.
-#      State                State of the monster to cast the skill (Default: ATTACK).
+#      State                State of the monster to cast the skill. (Default: ATTACK)
 #      CastRate             Rate to cast the skill (from 1-10000). (Default: 10000)
 #      CastTime             Time in millisecond to cast the skill. (Default: 0)
 #      CastDelay            Delay in millisecond to recast the skill. (Default: 5000)
 #      CastCancel           Whether the skill is cancelable or not. (Default: true)
 #      Target               Target of the skill. (Default: TARGET)
 #      Condition            Type of condition. (Default: ALWAYS)
-#      ConditionValue1      Values given to certain conditions.
-#      ConditionValue2      Values given to certain conditions.
+#      ConditionValue1      Value given at certain conditions.
+#      ConditionValue2      Value given at certain conditions.
 #      Ai                   Change the monster mode using the given Aegis monster type AI. (Default: 00)
 #      Summon:              List of monsters to summon, required for certain skills. (Default: null) 
-#        - Index            Unique Index in the list to edit the monster in import. Range 0-6.
+#        - Index            Unique Index in the list to edit the monster on import. Range of 0-5.
 #          Mob              Monster AegisName.
 #          Clear            True to remove the given monster at this index. (Optional)
 #      Emotion              Emotion displayed on the monster after the cast. (Default: ET_NONE)
 #      Chat                 Chat ID to display the associated sentence defined in mob_chat_db.yml. (Default: 0)
 #      Clear                True to remove the skill. (Optional)
 ###########################################################################
-#
-# Read doc/mob_skill_db.txt for more information.
-#
-###########################################################################

+ 63 - 62
src/map/mob.cpp

@@ -3188,23 +3188,22 @@ int mob_guardian_guildchange(struct mob_data *md)
 /*==========================================
  * Pick a random class for the mob
  *------------------------------------------*/
-int mob_random_class(int *value, size_t count)
+int mob_random_class(std::unordered_map<uint16, int> summons)
 {
-	nullpo_ret(value);
+	if (summons.empty())
+		return 0;
 
-	// no count specified, look into the array manually, but take only max 5 elements
-	if (count < 1) {
-		count = 0;
-		while(count < 5 && mobdb_checkid(value[count])) count++;
-		if(count < 1)	// nothing found
-			return 0;
-	} else {
-		// check if at least the first value is valid
-		if(mobdb_checkid(value[0]) == 0)
-			return 0;
+	std::vector<int> summons_available;
+
+	for (const auto &it : summons) {
+		if (mobdb_checkid(it.second) > 0)
+			summons_available.push_back(it.second);
 	}
-	//Pick a random value, hoping it exists. [Skotlex]
-	return mobdb_checkid(value[rnd()%count]);
+
+	if (summons_available.empty())
+		return 0;
+
+	return util::vector_random(summons_available);
 }
 
 /**
@@ -3418,14 +3417,26 @@ int mob_countslave(struct block_list *bl)
 /*==========================================
  * Summons amount slaves contained in the value[5] array using round-robin. [adapted by Skotlex]
  *------------------------------------------*/
-int mob_summonslave(struct mob_data *md2,int *value,int amount,uint16 skill_id)
+int mob_summonslave(mob_data *md2, std::unordered_map<uint16, int> summons, int amount, uint16 skill_id)
 {
-	struct mob_data *md;
-	struct spawn_data data;
-	int count = 0,k=0,hp_rate=0;
-
 	nullpo_ret(md2);
-	nullpo_ret(value);
+
+	if (summons.empty())
+		return 0;
+
+	std::vector<int> summons_available;
+
+	for (const auto &it : summons) {
+		if (mobdb_checkid(it.second) > 0)
+			summons_available.push_back(it.second);
+	}
+
+	if (summons_available.empty())
+		return 0;
+
+	int k = 0, hp_rate = 0;
+	int count = summons_available.size();
+	spawn_data data;
 
 	memset(&data, 0, sizeof(struct spawn_data));
 	data.m = md2->bl.m;
@@ -3435,30 +3446,28 @@ int mob_summonslave(struct mob_data *md2,int *value,int amount,uint16 skill_id)
 	data.state.size = md2->special_state.size;
 	data.state.ai = md2->special_state.ai;
 
-	if(mobdb_checkid(value[0]) == 0)
-		return 0;
 	/**
 	 * Flags this monster is able to summon; saves a worth amount of memory upon deletion
 	 **/
 	md2->can_summon = 1;
 
-	while(count < 5 && mobdb_checkid(value[count])) count++;
-	if(count < 1) return 0;
 	if (amount > 0 && amount < count) { //Do not start on 0, pick some random sub subset [Skotlex]
 		k = rnd()%count;
 		amount+=k; //Increase final value by same amount to preserve total number to summon.
 	}
 
-	if (!battle_config.monster_class_change_recover &&
-		(skill_id == NPC_TRANSFORMATION || skill_id == NPC_METAMORPHOSIS))
+	if (!battle_config.monster_class_change_recover && (skill_id == NPC_TRANSFORMATION || skill_id == NPC_METAMORPHOSIS))
 		hp_rate = get_percentage(md2->status.hp, md2->status.max_hp);
 
+	mob_data *md;
+
 	for(;k<amount;k++) {
-		short x,y;
-		data.id = value[k%count]; //Summon slaves in round-robin fashion. [Skotlex]
+		data.id = summons_available[k%count]; //Summon slaves in round-robin fashion. [Skotlex]
 		if (mobdb_checkid(data.id) == 0)
 			continue;
 
+		short x,y;
+
 		if (skill_id != NPC_DEATHSUMMON && map_search_freecell(&md2->bl, 0, &x, &y, MOB_SLAVEDISTANCE, MOB_SLAVEDISTANCE, 0)) {
 			data.x = x;
 			data.y = y;
@@ -3583,11 +3592,10 @@ struct block_list *mob_getmasterhpltmaxrate(struct mob_data *md,int rate)
  *------------------------------------------*/
 int mob_getfriendstatus_sub(struct block_list *bl,va_list ap)
 {
-	int cond2;
-	struct mob_data **fr, *md, *mmd;
-	int flag=0;
-
 	nullpo_ret(bl);
+
+	struct mob_data *md, *mmd;
+
 	nullpo_ret(md=(struct mob_data *)bl);
 	nullpo_ret(mmd=va_arg(ap,struct mob_data *));
 
@@ -3596,9 +3604,12 @@ int mob_getfriendstatus_sub(struct block_list *bl,va_list ap)
 
 	if (battle_check_target(&mmd->bl,bl,BCT_ENEMY)>0)
 		return 0;
+
 	int cond1 = va_arg(ap, int);
-	cond2=va_arg(ap,int);
-	fr=va_arg(ap,struct mob_data **);
+	int cond2 = va_arg(ap,int);
+	struct mob_data **fr = va_arg(ap,struct mob_data **);
+	int flag = 0;
+
 	if( cond2==-1 ){
 		int j;
 		for(j=SC_COMMON_MIN;j<=SC_COMMON_MAX && !flag;j++){
@@ -3616,7 +3627,7 @@ int mob_getfriendstatus_sub(struct block_list *bl,va_list ap)
 struct mob_data *mob_getfriendstatus(struct mob_data *md,int cond1,int cond2)
 {
 	nullpo_ret(md);
-	struct mob_data* fr = NULL;
+	struct mob_data* fr = nullptr;
 
 	map_foreachinallrange(mob_getfriendstatus_sub, &md->bl, 8,BL_MOB, md,cond1,cond2,&fr);
 	return fr;
@@ -5684,11 +5695,11 @@ uint64 MobSkillDatabase::parseBodyNode(const YAML::Node &node) {
 	std::shared_ptr<s_mob_db> mob = nullptr;
 
 	if (mob_name == "ALL")	// special behaviour
-		mob_id = -3;
+		mob_id = MOBID_ALL;
 	else if (mob_name == "ALL_NORMAL")
-		mob_id = -2;
+		mob_id = MOBID_NORMAL;
 	else if (mob_name == "ALL_BOSS")
-		mob_id = -1;
+		mob_id = MOBID_BOSS;
 	else {
 		mob = mobdb_search_aegisname( mob_name.c_str() );
 
@@ -5720,7 +5731,7 @@ uint64 MobSkillDatabase::parseBodyNode(const YAML::Node &node) {
 					return 0;
 
 				if (mob_skill->skills.erase(index) == 0)
-					this->invalidWarning(it["Clear"], "Failed to remove non-existing skill Index %hu for monster %s.\n", index, mob_name.c_str());
+					this->invalidWarning(it["Clear"], "Failed to remove nonexistent skill Index %hu for monster %s.\n", index, mob_name.c_str());
 
 				continue;
 			}
@@ -5736,7 +5747,7 @@ uint64 MobSkillDatabase::parseBodyNode(const YAML::Node &node) {
 					return 0;
 
 				if (mob_skill->skills.size() >= MAX_MOBSKILL) {
-					this->invalidWarning(it["Index"], "Too many skills for monster %s (max: %d).\n", mob_name.c_str(), MAX_MOBSKILL);
+					this->invalidWarning(it["Index"], "Too many skills have been defined for monster %s (max: %d).\n", mob_name.c_str(), MAX_MOBSKILL);
 					return 0;
 				}
 				skill = std::make_shared<s_mob_skill>();
@@ -5751,7 +5762,7 @@ uint64 MobSkillDatabase::parseBodyNode(const YAML::Node &node) {
 				uint16 skill_id = skill_name2id(name.c_str());
 
 				if (skill_id == 0) {
-					this->invalidWarning(it["Name"], "Invalid aegis skill name \"%s\".\n", name.c_str());
+					this->invalidWarning(it["Name"], "Invalid Aegis skill name \"%s\".\n", name.c_str());
 					return 0;
 				}
 
@@ -5887,7 +5898,7 @@ uint64 MobSkillDatabase::parseBodyNode(const YAML::Node &node) {
 					case MSC_LONGRANGEATTACKED:
 					case MSC_CASTTARGETED:
 					case MSC_RUDEATTACKED:
-						this->invalidWarning(it["ConditionValue1"], "The Condition doesn't support a value.\n");
+						this->invalidWarning(it["ConditionValue1"], "Condition %s does not need a Condition Value.\n", script_get_constant_str("MSC_", skill->cond1));
 						return 0;
 					case MSC_SKILLUSED:	// aegis skill name required
 					case MSC_AFTERSKILL: {
@@ -5899,7 +5910,7 @@ uint64 MobSkillDatabase::parseBodyNode(const YAML::Node &node) {
 						uint16 skill_id_cond2 = skill_name2id(condition_value.c_str());
 
 						if (skill_id_cond2 == 0) {
-							this->invalidWarning(it["ConditionValue1"], "Invalid aegis skill name \"%s\".\n", condition_value.c_str());
+							this->invalidWarning(it["ConditionValue1"], "Invalid Aegis skill name \"%s\".\n", condition_value.c_str());
 							return 0;
 						}
 						skill->cond2 = static_cast<int16>(skill_id_cond2);
@@ -5962,7 +5973,7 @@ uint64 MobSkillDatabase::parseBodyNode(const YAML::Node &node) {
 					case MSC_FRIENDHPINRATE:
 						break;
 					default:	// no condition value required
-						this->invalidWarning(it["ConditionValue2"], "The Condition doesn't support a value.\n");
+						this->invalidWarning(it["ConditionValue2"], "Condition %s does not need a Condition Value.\n", script_get_constant_str("MSC_", skill->cond1));
 						return 0;
 				}
 
@@ -6030,7 +6041,10 @@ uint64 MobSkillDatabase::parseBodyNode(const YAML::Node &node) {
 						if (!this->asBool(summonit, "Clear", active))
 							return 0;
 
-						skill->val[summon_index] = 0;
+						if (active && skill->summons.erase(summon_index) == 0) {
+							this->invalidWarning(summonit["Clear"], "Failed to remove data in index %s.\n", summon_index);
+							continue;
+						}
 
 						continue;
 					}
@@ -6047,16 +6061,7 @@ uint64 MobSkillDatabase::parseBodyNode(const YAML::Node &node) {
 						return 0;
 					}
 
-					skill->val[summon_index] = mob_summon->id;
-				}
-			} else {
-				if (!skill_exists) {
-					skill->val[0] = 0;
-					skill->val[1] = 0;
-					skill->val[2] = 0;
-					skill->val[3] = 0;
-					skill->val[4] = 0;
-					skill->val[5] = 0;
+					skill->summons[summon_index] = mob_summon->id;
 				}
 			}
 
@@ -6125,16 +6130,12 @@ void MobSkillDatabase::loadingFinished() {
 
 			// Apply battle_config modifiers to rate (permillage) and delay [Skotlex]
 			if (battle_config.mob_skill_rate != 100)
-				skill->permillage = skill->permillage * battle_config.mob_skill_rate / 100;
-			if (skill->permillage > 10000)
-				skill->permillage = 10000;
+				skill->permillage = min(skill->permillage * battle_config.mob_skill_rate / 100, 10000);
 			else if (skill->permillage == 0 && battle_config.mob_skill_rate)
 				skill->permillage = 1;
 
 			if (battle_config.mob_skill_delay != 100)
-				skill->delay = skill->delay * battle_config.mob_skill_delay / 100;
-			if (skill->delay < 0 || skill->delay > MOB_MAX_DELAY) // time overflow?
-				skill->delay = MOB_MAX_DELAY;
+				skill->delay = cap_value(skill->delay * battle_config.mob_skill_delay / 100, 0, MOB_MAX_DELAY);
 
 			// Check that the target condition is right for the skill type. [Skotlex]
 			if (skill_get_casttype(skill->skill_id) == CAST_GROUND) {
@@ -6579,7 +6580,7 @@ static void mob_skill_db_set(void) {
 		}
 		// Global skill
 		else {
-			uint16 id = skill->mob_id;
+			int32 id = skill->mob_id;
 			id *= -1;
 			for( auto &pair : mob_db ){
 				if ( mob_is_clone(pair.first) ){

+ 9 - 6
src/map/mob.hpp

@@ -47,6 +47,9 @@ const t_tick MIN_RANDOMWALKTIME = 4000;
  * Added definitions for WoE:SE objects and other [L0ne_W0lf], [aleos]
  */
 enum MOBID {
+	MOBID_ALL				= -3,
+	MOBID_NORMAL			= -2,
+	MOBID_BOSS				= -1,
 	MOBID_PORING			= 1002,
 	MOBID_RED_PLANT			= 1078,
 	MOBID_BLUE_PLANT,
@@ -186,7 +189,7 @@ enum e_aegis_monsterclass : int8 {
 };
 
 enum e_mob_skill_target {
-	MST_TARGET	=	0,
+	MST_TARGET = 0,
 	MST_RANDOM,	//Random Target!
 	MST_SELF,
 	MST_FRIEND,
@@ -203,7 +206,7 @@ enum e_mob_skill_target {
 };
 
 enum e_mob_skill_condition {
-	MSC_ALWAYS	=	0x0000,
+	MSC_ALWAYS = 0,
 	MSC_MYHPLTMAXRATE,
 	MSC_MYHPINRATE,
 	MSC_FRIENDHPLTMAXRATE,
@@ -237,14 +240,14 @@ struct s_mob_skill {
 	e_mob_skill_target target;
 	short cond1, cond2, cond3;
 	int mob_mode;
-	int val[6];
+	std::unordered_map<uint16, int> summons;	// index, mob ID
 	short emotion;
 	unsigned short msg_id;
 };
 
 /// Mob skill struct for temporary storage
 struct s_mob_skill_db {
-	int32 mob_id; ///< Monster ID. -1 boss types, -2 normal types, -3 all monsters
+	int32 mob_id; ///< Monster ID. MOBID_BOSS boss types, MOBID_NORMAL normal types, MOBID_ALL all monsters
 	uint16 index_num;	/// index for unordered_map
 	std::unordered_map<uint16, std::shared_ptr<s_mob_skill>> skills; ///< index, Skills
 };
@@ -509,7 +512,7 @@ void do_final_mob(bool is_reload);
 TIMER_FUNC(mob_timer_delete);
 int mob_deleteslave(struct mob_data *md);
 
-int mob_random_class (int *value, size_t count);
+int mob_random_class (std::unordered_map<uint16, int> summons);
 int mob_get_random_id(int type, enum e_random_monster_flags flag, int lv);
 int mob_class_change(struct mob_data *md,int mob_id);
 int mob_warpslave(struct block_list *bl, int range);
@@ -518,7 +521,7 @@ int mob_linksearch(struct block_list *bl,va_list ap);
 bool mob_chat_display_message (mob_data &md, uint16 msg_id);
 int mobskill_use(struct mob_data *md,t_tick tick,int event);
 int mobskill_event(struct mob_data *md,struct block_list *src,t_tick tick, int flag);
-int mob_summonslave(struct mob_data *md2,int *value,int amount,uint16 skill_id);
+int mob_summonslave(mob_data *md2, std::unordered_map<uint16, int> summons, int amount, uint16 skill_id);
 int mob_countslave(struct block_list *bl);
 int mob_count_sub(struct block_list *bl, va_list ap);
 

+ 3 - 3
src/map/skill.cpp

@@ -9448,7 +9448,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui
 	case NPC_SUMMONMONSTER:
 	case NPC_DEATHSUMMON:
 		if(md && md->skill_idx >= 0)
-			mob_summonslave(md,md->db->skill[md->skill_idx]->val,skill_lv,skill_id);
+			mob_summonslave(md,md->db->skill[md->skill_idx]->summons,skill_lv,skill_id);
 		break;
 
 	case NPC_CALLSLAVE:
@@ -9499,9 +9499,9 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui
 	case NPC_TRANSFORMATION:
 	case NPC_METAMORPHOSIS:
 		if(md && md->skill_idx >= 0) {
-			int class_ = mob_random_class (md->db->skill[md->skill_idx]->val,0);
+			int class_ = mob_random_class (md->db->skill[md->skill_idx]->summons);
 			if (skill_lv > 1) //Multiply the rest of mobs. [Skotlex]
-				mob_summonslave(md,md->db->skill[md->skill_idx]->val,skill_lv-1,skill_id);
+				mob_summonslave(md,md->db->skill[md->skill_idx]->summons,skill_lv-1,skill_id);
 			if (class_) mob_class_change(md, class_);
 		}
 		break;