瀏覽代碼

Update and corrections

Atemo 3 年之前
父節點
當前提交
a8e35c0408

+ 23 - 18
db/import-tmpl/mob_skill_db.yml

@@ -24,25 +24,30 @@
 ###########################################################################
 ###########################################################################
 # - Mob                     Monster AegisName.
 # - Mob                     Monster AegisName.
 #   Skills:                 List of Skills casted by the monster.
 #   Skills:                 List of Skills casted by the monster.
-#    - Id                   Unique Index in the list.
-#      Name                 Skill name.
+#    - Index                Unique Index in the list to edit the skill in import.
+#      Name                 Skill AegisName.
 #      Level                Skill Level.
 #      Level                Skill Level.
-#      State                State of the monster to cast the skill (Default: attack).
-#      Rate                 Rate to cast the skill (from 1-10000).
-#      CastTime             Time in millisecond to cast the skill.
-#      Delay                Delay in millisecond to recast the skill.
-#      Cancelable           True whether the skill is cancelable or not (false).
-#      Target               Target of the skill.
-#      Condition:           Condition to cast the skill.
-#         Type              Type of condition.
-#         Value             Value of the condition.
-#      Val0                 Val0.
-#      Val1                 Val1.
-#      Val2                 Val2.
-#      Val3                 Val3.
-#      Val4                 Val4.
-#      Emotion              Emotion displayed on the monster after (? while casting) the cast.
-#      Chat                 Chat ID to display the associated sentence.
+#      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.
+#      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.
+#          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:
 Header:

+ 23 - 18
db/mob_skill_db.yml

@@ -24,25 +24,30 @@
 ###########################################################################
 ###########################################################################
 # - Mob                     Monster AegisName.
 # - Mob                     Monster AegisName.
 #   Skills:                 List of Skills casted by the monster.
 #   Skills:                 List of Skills casted by the monster.
-#    - Id                   Unique Index in the list.
-#      Name                 Skill name.
+#    - Index                Unique Index in the list to edit the skill in import.
+#      Name                 Skill AegisName.
 #      Level                Skill Level.
 #      Level                Skill Level.
-#      State                State of the monster to cast the skill (Default: attack).
-#      Rate                 Rate to cast the skill (from 1-10000).
-#      CastTime             Time in millisecond to cast the skill.
-#      Delay                Delay in millisecond to recast the skill.
-#      Cancelable           True whether the skill is cancelable or not (false).
-#      Target               Target of the skill.
-#      Condition:           Condition to cast the skill.
-#         Type              Type of condition.
-#         Value             Value of the condition.
-#      Val0                 Val0.
-#      Val1                 Val1.
-#      Val2                 Val2.
-#      Val3                 Val3.
-#      Val4                 Val4.
-#      Emotion              Emotion displayed on the monster after (? while casting) the cast.
-#      Chat                 Chat ID to display the associated sentence.
+#      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.
+#      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.
+#          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:
 Header:

+ 1 - 1
db/pre-re/mob_skill_db.txt

@@ -5418,7 +5418,7 @@
 1991,Tendrilion@AS_SONICBLOW,attack,136,10,2000,0,5000,yes,target,always,0,,,,,,,
 1991,Tendrilion@AS_SONICBLOW,attack,136,10,2000,0,5000,yes,target,always,0,,,,,,,
 1991,Tendrilion@NPC_DRAGONFEAR,attack,659,2,500,0,10000,yes,self,always,0,,,,,,,
 1991,Tendrilion@NPC_DRAGONFEAR,attack,659,2,500,0,10000,yes,self,always,0,,,,,,,
 1991,Tendrilion@NPC_GROUNDATTACK,attack,185,4,1000,0,5000,yes,target,always,0,,,,,,21,
 1991,Tendrilion@NPC_GROUNDATTACK,attack,185,4,1000,0,5000,yes,target,always,0,,,,,,21,
-1991,Tendrilion@PF_SPIDERWEB,attack,405,1,3000,0,10000,yes,target,always,0,,0x39A5,,,,7,
+1991,Tendrilion@PF_SPIDERWEB,attack,405,1,3000,0,10000,yes,target,always,0,,0x3885,,,,7,
 1991,Tendrilion@NPC_ALLHEAL,idle,687,1,10000,30000,30000,yes,self,myhpltmaxrate,80,,,,,,,
 1991,Tendrilion@NPC_ALLHEAL,idle,687,1,10000,30000,30000,yes,self,myhpltmaxrate,80,,,,,,,
 1992,Cornus@AL_TELEPORT,idle,26,1,10000,0,0,yes,self,rudeattacked,,,,,,,,
 1992,Cornus@AL_TELEPORT,idle,26,1,10000,0,0,yes,self,rudeattacked,,,,,,,,
 1992,Cornus@AL_TELEPORT,walk,26,1,5000,0,5000,yes,self,rudeattacked,,,,,,,,
 1992,Cornus@AL_TELEPORT,walk,26,1,5000,0,5000,yes,self,rudeattacked,,,,,,,,

+ 35 - 35
db/pre-re/mob_skill_db.yml

@@ -15,41 +15,41 @@
 # You should have received a copy of the GNU General Public License
 # You should have received a copy of the GNU General Public License
 # along with this program. If not, see <http://www.gnu.org/licenses/>.
 # along with this program. If not, see <http://www.gnu.org/licenses/>.
 #
 #
-###########################################################################
-# Monster Skill Database
-###########################################################################
-#
-# Monster Skill Settings
-#
-###########################################################################
-# - Mob                     Monster AegisName.
-#   Skills:                 List of Skills casted by the monster.
-#    - Id                   Unique Index in the list to edit the skill in import.
-#      Name                 Skill AegisName.
-#      Level                Skill Level.
-#      State                State of the monster to cast the skill (Default: ATTACK).
-#      Cast:                About the skill Cast.
-#        Rate               Rate to cast the skill (from 1-10000) (Default: 10000).
-#        Time               Time in millisecond to cast the skill (Default: 0).
-#        Delay              Delay in millisecond to recast the skill (Default: 5000).
-#        Cancelable         Whether the skill is cancelable (true) or not (false) (Default: true).
-#      Target               Target of the skill (Default: Target).
-#      Condition:
-#        Cond1              Type of condition (Default: Always).
-#        Cond2Sring         String value (no value by default).
-#        Cond2Value         Integer value (default 0).
-#        Val0               Special value 0 according to Condition/Name (default 0).
-#        Val1               Special value 1 according to Condition/Name (default 0).
-#        Val2               Special value 2 according to Condition/Name (default 0).
-#        Val3               Special value 3 according to Condition/Name (default 0).
-#        Val4               Special value 4 according to Condition/Name (default 0).
-#      Emotion              Emotion displayed on the monster after (? while casting) the cast (no emotion by default).
-#      Chat                 Chat ID to display the associated sentence defined in mob_chat_db.yml (default 0).
-###########################################################################
-#
-# Read doc/mob_skill_db.txt for more information.
-#
-###########################################################################
+###########################################################################
+# Monster Skill Database
+###########################################################################
+#
+# Monster Skill Settings
+#
+###########################################################################
+# - Mob                     Monster AegisName.
+#   Skills:                 List of Skills casted by the monster.
+#    - Id                   Unique Index in the list to edit the skill in import.
+#      Name                 Skill AegisName.
+#      Level                Skill Level.
+#      State                State of the monster to cast the skill (Default: ATTACK).
+#      Cast:                About the skill Cast.
+#        Rate               Rate to cast the skill (from 1-10000) (Default: 10000).
+#        Time               Time in millisecond to cast the skill (Default: 0).
+#        Delay              Delay in millisecond to recast the skill (Default: 5000).
+#        Cancelable         Whether the skill is cancelable (true) or not (false) (Default: true).
+#      Target               Target of the skill (Default: Target).
+#      Condition:
+#        Cond1              Type of condition (Default: Always).
+#        Cond2Sring         String value (no value by default).
+#        Cond2Value         Integer value (default 0).
+#        Val0               Special value 0 according to Condition/Name (default 0).
+#        Val1               Special value 1 according to Condition/Name (default 0).
+#        Val2               Special value 2 according to Condition/Name (default 0).
+#        Val3               Special value 3 according to Condition/Name (default 0).
+#        Val4               Special value 4 according to Condition/Name (default 0).
+#      Emotion              Emotion displayed on the monster after (? while casting) the cast (no emotion by default).
+#      Chat                 Chat ID to display the associated sentence defined in mob_chat_db.yml (default 0).
+###########################################################################
+#
+# Read doc/mob_skill_db.txt for more information.
+#
+###########################################################################
 
 
 Header:
 Header:
   Type: MOB_SKILL_DB
   Type: MOB_SKILL_DB

+ 1 - 1
db/re/mob_skill_db.txt

@@ -5419,7 +5419,7 @@
 1991,Tendrilion@AS_SONICBLOW,attack,136,10,2000,0,5000,yes,target,always,0,,,,,,,
 1991,Tendrilion@AS_SONICBLOW,attack,136,10,2000,0,5000,yes,target,always,0,,,,,,,
 1991,Tendrilion@NPC_DRAGONFEAR,attack,659,2,500,0,10000,yes,self,always,0,,,,,,,
 1991,Tendrilion@NPC_DRAGONFEAR,attack,659,2,500,0,10000,yes,self,always,0,,,,,,,
 1991,Tendrilion@NPC_GROUNDATTACK,attack,185,4,1000,0,5000,yes,target,always,0,,,,,,21,
 1991,Tendrilion@NPC_GROUNDATTACK,attack,185,4,1000,0,5000,yes,target,always,0,,,,,,21,
-1991,Tendrilion@PF_SPIDERWEB,attack,405,1,3000,0,10000,yes,target,always,0,,0x38A5,,,,7,
+1991,Tendrilion@PF_SPIDERWEB,attack,405,1,3000,0,10000,yes,target,always,0,,0x3885,,,,7,
 1991,Tendrilion@NPC_ALLHEAL,idle,687,1,10000,30000,30000,yes,self,myhpltmaxrate,80,,,,,,,
 1991,Tendrilion@NPC_ALLHEAL,idle,687,1,10000,30000,30000,yes,self,myhpltmaxrate,80,,,,,,,
 1992,Cornus@AL_TELEPORT,idle,26,1,10000,0,0,yes,self,rudeattacked,,,,,,,,
 1992,Cornus@AL_TELEPORT,idle,26,1,10000,0,0,yes,self,rudeattacked,,,,,,,,
 1992,Cornus@AL_TELEPORT,walk,26,1,5000,0,5000,yes,self,rudeattacked,,,,,,,,
 1992,Cornus@AL_TELEPORT,walk,26,1,5000,0,5000,yes,self,rudeattacked,,,,,,,,

+ 155 - 49
doc/mob_skill_db.txt

@@ -8,52 +8,158 @@
 //= Reference for monster skills Database.
 //= Reference for monster skills Database.
 //============================================================
 //============================================================
 
 
-RATE: the chance of the skill being casted when the condition is fulfilled (10000 = 100%).
-DELAY: the time (in milliseconds) before attempting to recast the same skill.
-
-STATE:
-any (except dead) / idle (in standby) / walk (in movement) / dead (on killed) /
-loot /attack / angry (like attack, except player has not attacked mob yet) /
-chase (following target, after being attacked) / follow (following target,
-without being attacked) / anytarget (attack+angry+chase+follow)
-
-TARGET:
-target (current target) / self / friend / master / randomtarget (any enemy within skill's range)
-
-The following are for ground-skills, a random target tile is selected from the specified area:
-    around1 (3x3 area around self) / around2 (5x5 area around self) /
-    around3 (7x7 area around self) / around4 (9x9 area around self) /
-    around5 (3x3 area around target) / around6 (5x5 area around target) /
-    around7 (7x7 area around target) / around8 (9x9 area around target) /
-    around = around4
-
-CONDITION:
-always			Unconditional (no condition value).
-onspawn			When mob spawns/respawns (no condition value).
-myhpltmaxrate		When mob's HP drops to the specified %.
-myhpinrate		When mob's HP is in a certain % range (condition value = lower bound, val1 = upper bound).
-mystatuson		If mob has the specified abnormality in status.
-mystatusoff		If mob has ended the specified abnormality in status.
-friendhpltmaxrate	When mob's friend's HP drops to the specified %.
-friendhpinrate		When mob's friend's HP is in a certain % range (condition value = lower bound, val1 = upper bound).
-friendstatuson		If friend has the specified abnormality in status.
-friendstatusoff		If friend has ended the specified abnormality in status.
-attackpcgt		When attack PCs become greater than specified number.
-attackpcge		When attack PCs become greater than or equal to the specified number.
-slavelt			When number of slaves is less than the original specified number.
-slavele			When number of slaves is less than or equal to the original specified number.
-closedattacked		When close range melee attacked (no condition value).
-longrangeattacked	When long range attacked, ex. bows, guns, ranged skills (no condition value).
-skillused		When the specified skill is used on the mob.
-afterskill		After mob casts the specified skill.
-casttargeted		When a target is in cast range (no condition value).
-rudeattacked		When mob is rude attacked (no condition value).
-
-Status abnormalities specified through the statuson/statusoff system:
-    anybad (any type of state change) / stone / freeze / stun / sleep /
-    poison / curse / silence / confusion / blind / hiding / sight (unhidden)
-
-Note: if a negative MobID is provided, the skill will be treated as 'global':
--1: added for all boss types.
--2: added for all normal types.
--3: added for all mobs.
+---------------------------------------
+
+Mob: The Aegis name of the monster (SpriteName).
+
+Note: if the following name are 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.
+
+---------------------------------------
+
+Skills: List of Skills casted by the monster. The max number of skills a monster can hold is limited by MAX_MOBSKILL (50 by default).
+(starting here the definitions below are the one defined in Skills).
+
+---------------------------------------
+
+Index: Unique Index in the list to edit the skill in import. Range 0-65535.
+
+---------------------------------------
+
+Name: Skill Aegis name the monster will cast.
+
+---------------------------------------
+
+Level: The monster will cast the skill at the given Level.
+
+---------------------------------------
+
+State: State of the monster to cast the skill. Valids input are :
+
+ANY (except dead)
+IDLE (in standby)
+WALK (in movement)
+DEAD (on killed)
+LOOT
+ATTACK
+ANGRY (like ATTACK, except player has not attacked mob yet)
+CHASE (following target, after being attacked)
+FOLLOW (following target, without being attacked)
+ANYTARGET (ATTACK+ANGRY+CHASE+FOLLOW)
+
+---------------------------------------
+
+CastRate: The chance of the skill being casted when the condition is fulfilled (from 1-10000, 10000 = 100%). 
+
+---------------------------------------
+
+CastTime: Time in millisecond to cast the skill.
+
+---------------------------------------
+
+CastDelay: The time (in milliseconds) before attempting to recast the same skill.
+
+---------------------------------------
+
+CastCancel: Whether the skill can be canceled when the monster is hit. (true/false)
+
+---------------------------------------
+
+Target: Target of the skill. Valids input are :
+
+TARGET			(current target)
+SELF
+FRIEND
+MASTER
+RANDOMTARGET	(any enemy within skill's range)
+
+(The following are for ground-skills, a random target tile is selected from the specified area)
+AROUND1			(3x3 area around self)
+AROUND2			(5x5 area around self)
+AROUND3			(7x7 area around self)
+AROUND4			(9x9 area around self)
+AROUND5			(3x3 area around target)
+AROUND6			(5x5 area around target)
+AROUND7			(7x7 area around target)
+AROUND8			(9x9 area around target)
+AROUND			= AROUND4
+
+---------------------------------------
+
+Condition: Type of condition. Valids input are :
+
+ALWAYS				(no condition value)			Unconditional.
+ONSPAWN				(no condition value)			When mob spawns/respawns.
+SPAWN = ONSPAWN
+MYHPLTMAXRATE		(ConditionValue1)				When mob's HP drops to the specified %.
+MYHPINRATE			(ConditionValue1 = lower bound
+					ConditionValue2 = upper bound)	When mob's HP is in a certain % range (ConditionValue1 = lower bound, ConditionValue2 = upper bound).
+MYSTATUSON			(ConditionValue1)				If mob has the specified abnormality in status.
+MYSTATUSOFF			(ConditionValue1)				If mob has ended the specified abnormality in status.
+FRIENDHPLTMAXRATE	(ConditionValue1)				When mob's friend's HP drops to the specified %.
+FRIENDHPINRATE		(ConditionValue1 = lower bound
+					ConditionValue2 = upper bound)	When mob's friend's HP is in a certain % range (ConditionValue1 = lower bound, ConditionValue2 = upper bound).
+FRIENDSTATUSON		(ConditionValue1)				If friend has the specified abnormality in status.
+FRIENDSTATUSOFF		(ConditionValue1)				If friend has ended the specified abnormality in status.
+ATTACKPCGT			(ConditionValue1)				When attack PCs become greater than specified number.
+ATTACKPCGE			(ConditionValue1)				When attack PCs become greater than or equal to the specified number.
+SLAVELT				(ConditionValue1)				When number of slaves is less than the original specified number.
+SLAVELE				(ConditionValue1)				When number of slaves is less than or equal to the original specified number.
+CLOSEDATTACKED		(no condition value)			When close range melee attacked.
+LONGRANGEATTACKED	(no condition value)			When long range attacked, ex. bows, guns, ranged skills.
+SKILLUSED			(ConditionValue1)				When the specified skill is used on the mob.
+AFTERSKILL			(ConditionValue1)				After mob casts the specified skill.
+CASTTARGETED		(no condition value)			When a target is in cast range.
+RUDEATTACKED		(no condition value)			When mob is rude attacked.
+
+---------------------------------------
+
+ConditionValue1: Values required to certain conditions.
+
+Status abnormalities accepted through MYSTATUSON / MYSTATUSOFF / FRIENDSTATUSON / FRIENDSTATUSOFF for ConditionValue1:
+SC_ANYBAD (any type of state change)
+SC_STONE
+SC_FREEZE
+SC_STUN
+SC_SLEEP
+SC_POISON
+SC_CURSE
+SC_SILENCE
+SC_CONFUSION
+SC_BLIND
+SC_HIDING
+SC_SIGHT (unhidden)
+
+When the condition are SKILLUSED or AFTERSKILL, ConditionValue1 requires an Aegis skill name.
+ConditionValue1 requires a number for all other conditions.
+
+---------------------------------------
+
+ConditionValue2: Number required for MYHPINRATE and FRIENDHPINRATE conditions.
+
+---------------------------------------
+
+Ai: Change the monster mode using the given Aegis monster type AI. This field is currently only supported for NPC_EMOTION and NPC_EMOTION_ON skills.
+
+---------------------------------------
+
+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.
+      Mob              Monster AegisName.
+      Clear            True to remove the given monster at this index. (Optional)
+
+---------------------------------------
+
+Emotion: Emotion displayed on the monster after the cast. See the constants starting to "ET_" in script_constant.hpp
+
+---------------------------------------
+
+Chat: Chat ID to display the associated sentence defined in mob_chat_db.yml
+
+---------------------------------------
+
+Clear: True to remove the skill.
+
+---------------------------------------

+ 17 - 18
doc/yaml/db/mob_skill_db.yml

@@ -7,27 +7,26 @@
 ###########################################################################
 ###########################################################################
 # - Mob                     Monster AegisName.
 # - Mob                     Monster AegisName.
 #   Skills:                 List of Skills casted by the monster.
 #   Skills:                 List of Skills casted by the monster.
-#    - Id                   Unique Index in the list to edit the skill in import.
+#    - Index                Unique Index in the list to edit the skill in import.
 #      Name                 Skill AegisName.
 #      Name                 Skill AegisName.
 #      Level                Skill Level.
 #      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).
-#      Cast:                About the skill Cast.
-#        Rate               Rate to cast the skill (from 1-10000) (Default: 10000).
-#        Time               Time in millisecond to cast the skill (Default: 0).
-#        Delay              Delay in millisecond to recast the skill (Default: 5000).
-#        Cancelable         Whether the skill is cancelable (true) or not (false) (Default: true).
-#      Target               Target of the skill (Default: Target).
-#      Condition:
-#        Cond1              Type of condition (Default: Always).
-#        Cond2Sring         String value (no value by default).
-#        Cond2Value         Integer value (default 0).
-#        Val0               Special value 0 according to Condition/Name (default 0).
-#        Val1               Special value 1 according to Condition/Name (default 0).
-#        Val2               Special value 2 according to Condition/Name (default 0).
-#        Val3               Special value 3 according to Condition/Name (default 0).
-#        Val4               Special value 4 according to Condition/Name (default 0).
-#      Emotion              Emotion displayed on the monster after (? while casting) the cast (no emotion by default).
-#      Chat                 Chat ID to display the associated sentence defined in mob_chat_db.yml (default 0).
+#      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.
+#      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.
+#          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.
 # Read doc/mob_skill_db.txt for more information.

+ 2 - 0
src/map/clif.hpp

@@ -271,6 +271,8 @@ enum broadcast_flags : uint8_t {
 };
 };
 
 
 enum emotion_type {
 enum emotion_type {
+	ET_NONE = -1,
+	//
 	ET_SURPRISE = 0,	// /!
 	ET_SURPRISE = 0,	// /!
 	ET_QUESTION,		// /?
 	ET_QUESTION,		// /?
 	ET_DELIGHT,
 	ET_DELIGHT,

+ 2 - 2
src/map/homunculus.cpp

@@ -331,7 +331,7 @@ int hom_delete(struct homun_data *hd, int emote)
 	if (!sd)
 	if (!sd)
 		return unit_free(&hd->bl,CLR_DEAD);
 		return unit_free(&hd->bl,CLR_DEAD);
 
 
-	if (emote >= 0)
+	if (emote > ET_NONE && emote < ET_MAX)
 		clif_emotion(&sd->bl, emote);
 		clif_emotion(&sd->bl, emote);
 
 
 	//This makes it be deleted right away.
 	//This makes it be deleted right away.
@@ -865,7 +865,7 @@ void hom_menu(struct map_session_data *sd, int type)
 			hom_food(sd, sd->hd);
 			hom_food(sd, sd->hd);
 			break;
 			break;
 		case 2:
 		case 2:
-			hom_delete(sd->hd, -1);
+			hom_delete(sd->hd, ET_NONE);
 			break;
 			break;
 		default:
 		default:
 			ShowError("hom_menu : unknown menu choice : %d\n", type);
 			ShowError("hom_menu : unknown menu choice : %d\n", type);

+ 307 - 202
src/map/mob.cpp

@@ -3583,7 +3583,7 @@ struct block_list *mob_getmasterhpltmaxrate(struct mob_data *md,int rate)
  *------------------------------------------*/
  *------------------------------------------*/
 int mob_getfriendstatus_sub(struct block_list *bl,va_list ap)
 int mob_getfriendstatus_sub(struct block_list *bl,va_list ap)
 {
 {
-	int cond1,cond2;
+	int cond2;
 	struct mob_data **fr, *md, *mmd;
 	struct mob_data **fr, *md, *mmd;
 	int flag=0;
 	int flag=0;
 
 
@@ -3596,7 +3596,7 @@ int mob_getfriendstatus_sub(struct block_list *bl,va_list ap)
 
 
 	if (battle_check_target(&mmd->bl,bl,BCT_ENEMY)>0)
 	if (battle_check_target(&mmd->bl,bl,BCT_ENEMY)>0)
 		return 0;
 		return 0;
-	cond1=va_arg(ap,int);
+	int cond1 = va_arg(ap, int);
 	cond2=va_arg(ap,int);
 	cond2=va_arg(ap,int);
 	fr=va_arg(ap,struct mob_data **);
 	fr=va_arg(ap,struct mob_data **);
 	if( cond2==-1 ){
 	if( cond2==-1 ){
@@ -3615,8 +3615,8 @@ int mob_getfriendstatus_sub(struct block_list *bl,va_list ap)
 
 
 struct mob_data *mob_getfriendstatus(struct mob_data *md,int cond1,int cond2)
 struct mob_data *mob_getfriendstatus(struct mob_data *md,int cond1,int cond2)
 {
 {
-	struct mob_data* fr = NULL;
 	nullpo_ret(md);
 	nullpo_ret(md);
+	struct mob_data* fr = NULL;
 
 
 	map_foreachinallrange(mob_getfriendstatus_sub, &md->bl, 8,BL_MOB, md,cond1,cond2,&fr);
 	map_foreachinallrange(mob_getfriendstatus_sub, &md->bl, 8,BL_MOB, md,cond1,cond2,&fr);
 	return fr;
 	return fr;
@@ -3664,15 +3664,14 @@ int mobskill_use(struct mob_data *md, t_tick tick, int event)
 	//Pick a starting position and loop from that.
 	//Pick a starting position and loop from that.
 	i = battle_config.mob_ai&0x100?rnd()%ms.size():0;
 	i = battle_config.mob_ai&0x100?rnd()%ms.size():0;
 	for (n = 0; n < ms.size(); i++, n++) {
 	for (n = 0; n < ms.size(); i++, n++) {
-		int c2, flag = 0;
-
 		if (i == ms.size())
 		if (i == ms.size())
 			i = 0;
 			i = 0;
 
 
 		if (DIFF_TICK(tick, md->skilldelay[i]) < ms[i]->delay)
 		if (DIFF_TICK(tick, md->skilldelay[i]) < ms[i]->delay)
 			continue;
 			continue;
 
 
-		c2 = ms[i]->cond2;
+		int c2 = ms[i]->cond2;
+		int c3 = ms[i]->cond3;
 
 
 		if (ms[i]->state != md->state.skillstate) {
 		if (ms[i]->state != md->state.skillstate) {
 			if (md->state.skillstate != MSS_DEAD && (ms[i]->state == MSS_ANY ||
 			if (md->state.skillstate != MSS_DEAD && (ms[i]->state == MSS_ANY ||
@@ -3685,6 +3684,8 @@ int mobskill_use(struct mob_data *md, t_tick tick, int event)
 		if (rnd() % 10000 > ms[i]->permillage) //Lupus (max value = 10000)
 		if (rnd() % 10000 > ms[i]->permillage) //Lupus (max value = 10000)
 			continue;
 			continue;
 
 
+		int flag = 0;
+
 		if (ms[i]->cond1 == event)
 		if (ms[i]->cond1 == event)
 			flag = 1; //Trigger skill.
 			flag = 1; //Trigger skill.
 		else if (ms[i]->cond1 == MSC_SKILLUSED)
 		else if (ms[i]->cond1 == MSC_SKILLUSED)
@@ -3701,33 +3702,33 @@ int mobskill_use(struct mob_data *md, t_tick tick, int event)
 					break;
 					break;
 				case MSC_MYHPINRATE:
 				case MSC_MYHPINRATE:
 					flag = get_percentage(md->status.hp, md->status.max_hp);
 					flag = get_percentage(md->status.hp, md->status.max_hp);
-					flag = (flag >= c2 && flag <= ms[i]->val[0]);
+					flag = (flag >= c2 && flag <= c3);
 					break;
 					break;
 				case MSC_MYSTATUSON:		// status[num] on
 				case MSC_MYSTATUSON:		// status[num] on
 				case MSC_MYSTATUSOFF:		// status[num] off
 				case MSC_MYSTATUSOFF:		// status[num] off
 					if (!md->sc.count) {
 					if (!md->sc.count) {
 						flag = 0;
 						flag = 0;
-					} else if (ms[i]->cond2 == -1) {
+					} else if (c2 == -1) {
 						for (j = SC_COMMON_MIN; j <= SC_COMMON_MAX; j++)
 						for (j = SC_COMMON_MIN; j <= SC_COMMON_MAX; j++)
 							if ((flag = (md->sc.data[j]!=NULL)) != 0)
 							if ((flag = (md->sc.data[j]!=NULL)) != 0)
 								break;
 								break;
 					} else {
 					} else {
-						flag = (md->sc.data[ms[i]->cond2]!=NULL);
+						flag = (md->sc.data[c2]!=NULL);
 					}
 					}
 					flag ^= (ms[i]->cond1 == MSC_MYSTATUSOFF); break;
 					flag ^= (ms[i]->cond1 == MSC_MYSTATUSOFF); break;
 				case MSC_FRIENDHPLTMAXRATE:	// friend HP < maxhp%
 				case MSC_FRIENDHPLTMAXRATE:	// friend HP < maxhp%
-					flag = ((fbl = mob_getfriendhprate(md, 0, ms[i]->cond2)) != NULL); break;
+					flag = ((fbl = mob_getfriendhprate(md, 0, c2)) != NULL); break;
 				case MSC_FRIENDHPINRATE	:
 				case MSC_FRIENDHPINRATE	:
-					flag = ((fbl = mob_getfriendhprate(md, ms[i]->cond2, ms[i]->val[0])) != NULL); break;
+					flag = ((fbl = mob_getfriendhprate(md, c2, c3)) != NULL); break;
 				case MSC_FRIENDSTATUSON:	// friend status[num] on
 				case MSC_FRIENDSTATUSON:	// friend status[num] on
 				case MSC_FRIENDSTATUSOFF:	// friend status[num] off
 				case MSC_FRIENDSTATUSOFF:	// friend status[num] off
-					flag = ((fmd = mob_getfriendstatus(md, ms[i]->cond1, ms[i]->cond2)) != NULL); break;
+					flag = ((fmd = mob_getfriendstatus(md, ms[i]->cond1, c2)) != NULL); break;
 				case MSC_SLAVELT:		// slave < num
 				case MSC_SLAVELT:		// slave < num
-					flag = (mob_countslave(&md->bl) < c2 ); break;
+					flag = (mob_countslave(&md->bl) < c2); break;
 				case MSC_ATTACKPCGT:	// attack pc > num
 				case MSC_ATTACKPCGT:	// attack pc > num
 					flag = (unit_counttargeted(&md->bl) > c2); break;
 					flag = (unit_counttargeted(&md->bl) > c2); break;
 				case MSC_SLAVELE:		// slave <= num
 				case MSC_SLAVELE:		// slave <= num
-					flag = (mob_countslave(&md->bl) <= c2 ); break;
+					flag = (mob_countslave(&md->bl) <= c2); break;
 				case MSC_ATTACKPCGE:	// attack pc >= num
 				case MSC_ATTACKPCGE:	// attack pc >= num
 					flag = (unit_counttargeted(&md->bl) >= c2); break;
 					flag = (unit_counttargeted(&md->bl) >= c2); break;
 				case MSC_AFTERSKILL:
 				case MSC_AFTERSKILL:
@@ -3737,7 +3738,7 @@ int mobskill_use(struct mob_data *md, t_tick tick, int event)
 					if (flag) md->state.attacked_count = 0;	//Rude attacked count should be reset after the skill condition is met. Thanks to Komurka [Skotlex]
 					if (flag) md->state.attacked_count = 0;	//Rude attacked count should be reset after the skill condition is met. Thanks to Komurka [Skotlex]
 					break;
 					break;
 				case MSC_MASTERHPLTMAXRATE:
 				case MSC_MASTERHPLTMAXRATE:
-					flag = ((fbl = mob_getmasterhpltmaxrate(md, ms[i]->cond2)) != NULL); break;
+					flag = ((fbl = mob_getmasterhpltmaxrate(md, c2)) != NULL); break;
 				case MSC_MASTERATTACKED:
 				case MSC_MASTERATTACKED:
 					flag = (md->master_id > 0 && (fbl=map_id2bl(md->master_id)) && unit_counttargeted(fbl) > 0); break;
 					flag = (md->master_id > 0 && (fbl=map_id2bl(md->master_id)) && unit_counttargeted(fbl) > 0); break;
 				case MSC_ALCHEMIST:
 				case MSC_ALCHEMIST:
@@ -3838,7 +3839,7 @@ int mobskill_use(struct mob_data *md, t_tick tick, int event)
 			}
 			}
 		}
 		}
 		//Skill used. Post-setups...
 		//Skill used. Post-setups...
-		if ( ms[i]->msg_id ){ //Display color message [SnakeDrak]
+		if ( ms[i]->msg_id > 0 ){ //Display color message [SnakeDrak]
 			mob_chat_display_message(*md, ms[i]->msg_id);
 			mob_chat_display_message(*md, ms[i]->msg_id);
 		}
 		}
 		if(!(battle_config.mob_ai&0x200)) { //pass on delay to same skill.
 		if(!(battle_config.mob_ai&0x200)) { //pass on delay to same skill.
@@ -4008,8 +4009,8 @@ int mob_clone_spawn(struct map_session_data *sd, int16 m, int16 x, int16 y, cons
 			ms->skill_lv = sd->status.skill[sk_idx].lv;
 			ms->skill_lv = sd->status.skill[sk_idx].lv;
 			ms->state = MSS_ANY;
 			ms->state = MSS_ANY;
 			ms->permillage = 500*battle_config.mob_skill_rate/100; //Default chance of all skills: 5%
 			ms->permillage = 500*battle_config.mob_skill_rate/100; //Default chance of all skills: 5%
-			ms->emotion = -1;
-			ms->cancel = 0;
+			ms->emotion = ET_NONE;
+			ms->cancel = false;
 			ms->casttime = skill_castfix(&sd->bl,skill_id, ms->skill_lv);
 			ms->casttime = skill_castfix(&sd->bl,skill_id, ms->skill_lv);
 			ms->delay = 5000+skill_delayfix(&sd->bl,skill_id, ms->skill_lv);
 			ms->delay = 5000+skill_delayfix(&sd->bl,skill_id, ms->skill_lv);
 			ms->msg_id = 0;
 			ms->msg_id = 0;
@@ -5696,7 +5697,7 @@ uint64 MobSkillDatabase::parseBodyNode(const YAML::Node &node) {
 			return 0;
 			return 0;
 		}
 		}
 
 
-		mob_id = static_cast<int32>(mob->vd.class_);
+		mob_id = static_cast<int32>(mob->id);
 	}
 	}
 
 
 	std::shared_ptr<s_mob_skill_db> mob_skill = this->find(mob_id);
 	std::shared_ptr<s_mob_skill_db> mob_skill = this->find(mob_id);
@@ -5712,22 +5713,22 @@ uint64 MobSkillDatabase::parseBodyNode(const YAML::Node &node) {
 
 
 	if (this->nodeExists(node, "Skills")) {
 	if (this->nodeExists(node, "Skills")) {
 		for (const YAML::Node &it : node["Skills"]) {
 		for (const YAML::Node &it : node["Skills"]) {
-			uint16 id;
+			uint16 index;
 
 
 			if (this->nodeExists(it, "Clear")) {
 			if (this->nodeExists(it, "Clear")) {
-				if (!this->asUInt16(it, "Clear", id))
+				if (!this->asUInt16(it, "Clear", index))
 					return 0;
 					return 0;
 
 
-				if (mob_skill->skills.erase(id) == 0)
-					this->invalidWarning(it["Clear"], "Failed to remove non-existing Id %hu for mob %s.\n", id, mob_name.c_str());
+				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());
 
 
 				continue;
 				continue;
 			}
 			}
 
 
-			if (!this->asUInt16(it, "Id", id))
+			if (!this->asUInt16(it, "Index", index))
 				return 0;
 				return 0;
 
 
-			std::shared_ptr<s_mob_skill> skill = util::umap_find(mob_skill->skills, id);
+			std::shared_ptr<s_mob_skill> skill = util::umap_find(mob_skill->skills, index);
 			bool skill_exists = skill != nullptr;
 			bool skill_exists = skill != nullptr;
 
 
 			if (!skill_exists) {
 			if (!skill_exists) {
@@ -5735,48 +5736,55 @@ uint64 MobSkillDatabase::parseBodyNode(const YAML::Node &node) {
 					return 0;
 					return 0;
 
 
 				if (mob_skill->skills.size() >= MAX_MOBSKILL) {
 				if (mob_skill->skills.size() >= MAX_MOBSKILL) {
-					this->invalidWarning(it["Id"], "Too many skills for monster %d[%s] (max: %d).\n", mob_id, mob_name.c_str(), MAX_MOBSKILL);
+					this->invalidWarning(it["Id"], "Too many skills for monster %s (max: %d).\n", mob_name.c_str(), MAX_MOBSKILL);
 					return 0;
 					return 0;
 				}
 				}
 				skill = std::make_shared<s_mob_skill>();
 				skill = std::make_shared<s_mob_skill>();
 			}
 			}
 
 
 			if (this->nodeExists(it, "Name")) {
 			if (this->nodeExists(it, "Name")) {
-				std::string skill_name;
+				std::string name;
 
 
-				if (!this->asString(it, "Name", skill_name))
+				if (!this->asString(it, "Name", name))
 					return 0;
 					return 0;
 
 
-				uint16 skill_id = skill_name2id(skill_name.c_str());
+				uint16 skill_id = skill_name2id(name.c_str());
 
 
 				if (skill_id == 0) {
 				if (skill_id == 0) {
-					this->invalidWarning(it["Name"], "Invalid skill name \"%s\", skipping.\n", skill_name.c_str());
+					this->invalidWarning(it["Name"], "Invalid aegis skill name \"%s\".\n", name.c_str());
 					return 0;
 					return 0;
 				}
 				}
 
 
 				skill->skill_id = skill_id;
 				skill->skill_id = skill_id;
 			}
 			}
 
 
+			const char *skill_name = skill_get_desc( skill->skill_id );
+
 			if (this->nodeExists(it, "Level")) {
 			if (this->nodeExists(it, "Level")) {
 				uint16 level;
 				uint16 level;
 
 
 				if (!this->asUInt16(it, "Level", level))
 				if (!this->asUInt16(it, "Level", level))
 					return 0;
 					return 0;
 
 
-				skill->skill_lv = cap_value(level, 1, battle_config.mob_max_skilllvl); //we strip max skill level
+				if (level == 0 || level > battle_config.mob_max_skilllvl) {
+					this->invalidWarning(it["Level"], "Invalid skill level %hu for \"%s\", capping to 1.\n", level, skill_name);
+					level = 1;
+				}
+
+				skill->skill_lv = level;
 			}
 			}
 
 
-			if (this->nodeExists(it, "SkillState")) {
+			if (this->nodeExists(it, "State")) {
 				std::string state_name;
 				std::string state_name;
 
 
-				if (!this->asString(it, "SkillState", state_name))
+				if (!this->asString(it, "State", state_name))
 					return 0;
 					return 0;
 
 
 				std::string state_constant = "MSS_" + state_name;
 				std::string state_constant = "MSS_" + state_name;
 				int64 constant;
 				int64 constant;
 
 
 				if (!script_get_constant(state_constant.c_str(), &constant) || constant < MSS_ANY || constant > MSS_ANYTARGET) {
 				if (!script_get_constant(state_constant.c_str(), &constant) || constant < MSS_ANY || constant > MSS_ANYTARGET) {
-					this->invalidWarning(it["SkillState"], "Invalid SkillState %s. Note: \"MSS_\" is appended before the name, MobSkillDatabase is checking the constant \"%s\".\n", state_name.c_str(), state_constant.c_str());
+					this->invalidWarning(it["State"], "Invalid State %s.\n", state_name.c_str());
 					return 0;
 					return 0;
 				}
 				}
 				skill->state = static_cast<e_MobSkillState>(constant);
 				skill->state = static_cast<e_MobSkillState>(constant);
@@ -5785,166 +5793,283 @@ uint64 MobSkillDatabase::parseBodyNode(const YAML::Node &node) {
 					skill->state = MSS_BERSERK;
 					skill->state = MSS_BERSERK;
 			}
 			}
 
 
-			if (this->nodeExists(it, "Cast")) {
-				const YAML::Node &castNode = it["Cast"];
+			if (this->nodeExists(it, "CastRate")) {
+				int16 rate;
 
 
-				if (this->nodeExists(castNode, "Rate")) {
-					int16 rate;
-
-					if (!this->asInt16(castNode, "Rate", rate))
-						return 0;
+				if (!this->asInt16(it, "CastRate", rate))
+					return 0;
 
 
-					skill->permillage = rate;
-				} else {
-					if (!skill_exists)	// note: default by number of occurences
-						skill->permillage = 10000;
-				}
+				skill->permillage = rate;
+			} else {
+				if (!skill_exists)	// note: default by number of occurrences
+					skill->permillage = 10000;
+			}
 
 
-				if (this->nodeExists(castNode, "Time")) {
-					int32 time;
+			if (this->nodeExists(it, "CastTime")) {
+				int32 time;
 
 
-					if (!this->asInt32(castNode, "Time", time))
-						return 0;
+				if (!this->asInt32(it, "CastTime", time))
+					return 0;
 
 
-					skill->casttime = time;
-				} else {
-					if (!skill_exists)
-						skill->casttime = 0;
-				}
+				skill->casttime = time;
+			} else {
+				if (!skill_exists)
+					skill->casttime = 0;
+			}
 
 
-				if (this->nodeExists(castNode, "Delay")) {
-					int32 delay;
+			if (this->nodeExists(it, "CastDelay")) {
+				int32 delay;
 
 
-					if (!this->asInt32(castNode, "Delay", delay))
-						return 0;
+				if (!this->asInt32(it, "CastDelay", delay))
+					return 0;
 
 
-					skill->delay = delay;
-				} else {
-					if (!skill_exists)
-						skill->delay = 5000;
-				}
+				skill->delay = delay;
+			} else {
+				if (!skill_exists)
+					skill->delay = 5000;
+			}
 
 
-				if (this->nodeExists(castNode, "Cancelable")) {
-					bool cancel;
+			if (this->nodeExists(it, "CastCancel")) {
+				bool cancel;
 
 
-					if (!this->asBool(castNode, "Cancelable", cancel))
-						return 0;
+				if (!this->asBool(it, "CastCancel", cancel))
+					return 0;
 
 
-					skill->cancel = cancel;
-				} else {
-					if (!skill_exists)
-						skill->cancel = true;
-				}
+				skill->cancel = cancel;
 			} else {
 			} else {
-				if (!skill_exists) {
-					skill->permillage = 10000;
-					skill->casttime = 0;
-					skill->delay = 5000;
+				if (!skill_exists)
 					skill->cancel = true;
 					skill->cancel = true;
-				}
 			}
 			}
 
 
-			if (this->nodeExists(it, "SkillTarget")) {
+			if (this->nodeExists(it, "Target")) {
 				std::string target_name;
 				std::string target_name;
 
 
-				if (!this->asString(it, "SkillTarget", target_name))
+				if (!this->asString(it, "Target", target_name))
 					return 0;
 					return 0;
 
 
 				std::string target_constant = "MST_" + target_name;
 				std::string target_constant = "MST_" + target_name;
 				int64 constant;
 				int64 constant;
 
 
 				if (!script_get_constant(target_constant.c_str(), &constant) || constant < MST_TARGET || constant > MST_AROUND) {
 				if (!script_get_constant(target_constant.c_str(), &constant) || constant < MST_TARGET || constant > MST_AROUND) {
-					this->invalidWarning(it["SkillTarget"], "Unrecognized SkillTarget %s. Don't forget that \"MST_\" is appended before the name: MobSkillDatabase is checking if the constant \"%s\" exists.\n", target_name.c_str(), target_constant.c_str());
+					this->invalidWarning(it["Target"], "Invalid Target %s.\n", target_name.c_str());
 					return 0;
 					return 0;
 				}
 				}
-				skill->target = static_cast<int16>(constant);
+				skill->target = static_cast<e_mob_skill_target>(constant);
 			} else {
 			} else {
 				if (!skill_exists)
 				if (!skill_exists)
 					skill->target = MST_TARGET;
 					skill->target = MST_TARGET;
 			}
 			}
 
 
 			if (this->nodeExists(it, "Condition")) {
 			if (this->nodeExists(it, "Condition")) {
-				const YAML::Node &condNode = it["Condition"];
+				std::string condition_name;
 
 
-				if (this->nodeExists(it, "Cond1")) {
-					std::string condition_name;
+				if (!this->asString(it, "Condition", condition_name))
+					return 0;
 
 
-					if (!this->asString(it, "Cond1", condition_name))
-						return 0;
+				std::string condition_constant = "MSC_" + condition_name;
+				int64 constant;
 
 
-					std::string condition_constant = "MSC_" + condition_name;
-					int64 constant;
+				if (!script_get_constant(condition_constant.c_str(), &constant) || constant < MSC_ALWAYS || constant > MSC_SPAWN) {
+					this->invalidWarning(it["Condition"], "Invalid Condition %s.\n", condition_name.c_str());
+					return 0;
+				}
+				skill->cond1 = static_cast<int>(constant);
+			} else {
+				if (!skill_exists)
+					skill->cond1 = MSC_ALWAYS;
+			}
 
 
-					if (!script_get_constant(condition_constant.c_str(), &constant) || constant < MSC_ALWAYS || constant > MSC_SPAWN) {
-						this->invalidWarning(it["Cond1"], "Unknown Cond1 %s. Don't forget that \"MSC_\" is appended before the name: MobSkillDatabase is checking if the constant \"%s\" exists.\n", condition_name.c_str(), condition_constant.c_str());
+			if (this->nodeExists(it, "ConditionValue1")) {
+				switch( skill->cond1 ) {
+					case MSC_ALWAYS:	// no condition value required
+					case MSC_SPAWN:
+					case MSC_CLOSEDATTACKED:
+					case MSC_LONGRANGEATTACKED:
+					case MSC_CASTTARGETED:
+					case MSC_RUDEATTACKED:
+						this->invalidWarning(it["ConditionValue1"], "The Condition doesn't support a value.\n");
 						return 0;
 						return 0;
+					case MSC_SKILLUSED:	// aegis skill name required
+					case MSC_AFTERSKILL: {
+						std::string condition_value;
+
+						if (!this->asString(it, "ConditionValue1", condition_value))
+							return 0;
+
+						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());
+							return 0;
+						}
+						skill->cond2 = skill_id_cond2;
+						break;
 					}
 					}
-					skill->cond1 = static_cast<int16>(constant);	// todo eventually change short to enum ?
-					
-				} else {
-					if (!skill_exists)
-						skill->cond1 = MSC_ALWAYS;
+					case MSC_MYSTATUSON:	// SC_ status constant required
+					case MSC_MYSTATUSOFF:
+					case MSC_FRIENDSTATUSON:
+					case MSC_FRIENDSTATUSOFF: {
+						std::string condition_value;
+
+						if (!this->asString(it, "ConditionValue1", condition_value))
+							return 0;
+
+						int64 constant;
+
+						if (!script_get_constant(condition_value.c_str(), &constant)) {
+							this->invalidWarning(it["ConditionValue1"], "Invalid ConditionValue1 %s.\n", condition_value.c_str());
+							return 0;
+						}
+						switch( constant ) {
+							case SC_NONE:	// = SC_ANYBAD
+							case SC_STONE:
+							case SC_FREEZE:
+							case SC_STUN:
+							case SC_SLEEP:
+							case SC_POISON:
+							case SC_CURSE:
+							case SC_SILENCE:
+							case SC_CONFUSION:
+							case SC_BLIND:
+							case SC_HIDING:
+							case SC_SIGHT:
+								break;
+							default:
+								this->invalidWarning(it["ConditionValue1"], "Unsupported ConditionValue1 %s.\n", condition_value.c_str());
+								return 0;
+						}
+						skill->cond2 = static_cast<uint16>(constant);
+						break;
+					}
+					default: {
+						uint16 cond2;
+
+						if (!this->asUInt16(it, "ConditionValue1", cond2))
+							return 0;
+
+						skill->cond2 = cond2;
+						break;
+					}
+				}
+			} else {
+				if (!skill_exists)
+					skill->cond2 = 0;
+			}
+
+			if (this->nodeExists(it, "ConditionValue2")) {
+				switch( skill->cond1 ) {
+					case MSC_MYHPINRATE:
+					case MSC_FRIENDHPINRATE:
+						break;
+					default:	// no condition value required
+						this->invalidWarning(it["ConditionValue2"], "The Condition doesn't support a value.\n");
+						return 0;
 				}
 				}
 
 
-				// Status and Value fill the same variable
-				if (this->nodeExists(it, "Cond2String")) {	// todo string constant 0 to remove the value on import
-					std::string status_name;
+				uint16 cond3;
+
+				if (!this->asUInt16(it, "ConditionValue2", cond3))
+					return 0;
+
+				skill->cond3 = cond3;
+			} else {
+				if (!skill_exists)
+					skill->cond3 = 0;
+			}
 
 
-					if (!this->asString(it, "Cond2String", status_name))
+			if (this->nodeExists(it, "Ai")) {
+				switch( skill->skill_id ) {
+					case NPC_EMOTION:
+					case NPC_EMOTION_ON:
+						break;
+					default:	// some skills have a value in the AI field on aegis data. Mistake? or the AI field should be supported for all skills ?
+						this->invalidWarning(it["Ai"], "Ai not supported for skill %s.\n", skill_name);
 						return 0;
 						return 0;
+				}
 
 
+				std::string ai;
+
+				if (!this->asString(it, "Ai", ai))
+					return 0;
+
+				if (ai == "00")
+					skill->mob_mode = -1;
+				else {
+					std::string ai_constant = "MONSTER_TYPE_" + ai;
 					int64 constant;
 					int64 constant;
 
 
-					if (!script_get_constant(status_name.c_str(), &constant)) {	// todo sc_ range
-						this->invalidWarning(it["Cond2String"], "Unknown Cond2String %s.\n", status_name.c_str());
+					if (!script_get_constant(ai_constant.c_str(), &constant) || constant < MD_NONE || constant > MD_MASK) {
+						this->invalidWarning(it["Ai"], "Unknown monster AI %s.\n", ai.c_str());
 						return 0;
 						return 0;
 					}
 					}
-					skill->cond2 = static_cast<int16>(constant);	// todo eventually change short to enum ?
-				} else {
-					if (!skill_exists)
-						skill->cond2 = 0;
-				}
 
 
-				if (this->nodeExists(it, "Cond2Value")) {
-					uint16 value;
+					skill->mob_mode = static_cast<e_mode>(constant);
+				}
+			} else {
+				if (!skill_exists)
+					skill->mob_mode = -1;
+			}
 
 
-					if (!this->asUInt16(it, "Cond2Value", value))
+			if (this->nodeExists(it, "Summon")) {
+				switch( skill->skill_id ) {
+					case NPC_METAMORPHOSIS:
+					case NPC_SUMMONSLAVE:
+					case NPC_SUMMONMONSTER:
+					case NPC_DEATHSUMMON:
+						break;
+					default:
+						this->invalidWarning(it["Summon"], "Summon not supported for skill %s.\n", skill_name);
 						return 0;
 						return 0;
-
-					skill->cond2 = static_cast<int16>(value);
-				} else {
-					if (!skill_exists)
-						skill->cond2 = 0;
 				}
 				}
 
 
-				for (int i = 0; i < 5; ++i) {	// todo the max number of slaves should be at least 6, Val6 is missing
-					std::string val_name = "Val" + std::to_string(i);
+				for (const YAML::Node &summonit : it["Summon"]) {
+					uint16 summon_index;
+
+					if (!this->asUInt16(summonit, "Index", summon_index))
+						return 0;
+
+					if (summon_index > 5) {
+						this->invalidWarning(summonit["Index"], "Index %hu out of range (min: 0, max: 5).\n", summon_index);
+						return 0;
+					}
 
 
-					if (this->nodeExists(it, val_name)) {
-						int32 val;
+					if (this->nodeExists(summonit, "Clear")) {
+						bool active;
 
 
-						if (!this->asInt32(it, val_name, val))
+						if (!this->asBool(summonit, "Clear", active))
 							return 0;
 							return 0;
 
 
-						skill->val[i] = val;
-					} else {
-						if (!skill_exists)
-							skill->val[i] = 0;
+						skill->val[summon_index] = 0;
+
+						continue;
 					}
 					}
+
+					std::string mob_name_summon;
+
+					if (!this->asString( summonit, "Mob", mob_name_summon ))
+						return 0;
+
+					std::shared_ptr<s_mob_db> mob_summon = mobdb_search_aegisname( mob_name_summon.c_str() );
+
+					if (mob_summon == nullptr) {
+						this->invalidWarning(summonit["Mob"], "Mob %s does not exist.\n", mob_name_summon.c_str());
+						return 0;
+					}
+
+					skill->val[summon_index] = mob_summon->id;
 				}
 				}
 			} else {
 			} else {
 				if (!skill_exists) {
 				if (!skill_exists) {
-					skill->cond1 = MSC_ALWAYS;
-					skill->cond2 = 0;
 					skill->val[0] = 0;
 					skill->val[0] = 0;
 					skill->val[1] = 0;
 					skill->val[1] = 0;
 					skill->val[2] = 0;
 					skill->val[2] = 0;
 					skill->val[3] = 0;
 					skill->val[3] = 0;
 					skill->val[4] = 0;
 					skill->val[4] = 0;
+					skill->val[5] = 0;
 				}
 				}
 			}
 			}
 
 
-			if (this->nodeExists(it, "Emotion")) {	// todo emotion constant -1 to remove the emotion on import
+			if (this->nodeExists(it, "Emotion")) {
 				std::string emotion_name;
 				std::string emotion_name;
 
 
 				if (!this->asString(it, "Emotion", emotion_name))
 				if (!this->asString(it, "Emotion", emotion_name))
@@ -5952,14 +6077,14 @@ uint64 MobSkillDatabase::parseBodyNode(const YAML::Node &node) {
 
 
 				int64 constant;
 				int64 constant;
 
 
-				if (!script_get_constant(emotion_name.c_str(), &constant) || constant < ET_SURPRISE || constant >= ET_MAX) {
-					this->invalidWarning(it["Emotion"], "Unknown Emotion %s.\n", emotion_name.c_str());
+				if (!script_get_constant(emotion_name.c_str(), &constant) || constant <= ET_NONE || constant >= ET_MAX) {
+					this->invalidWarning(it["Emotion"], "Invalid Emotion %s.\n", emotion_name.c_str());
 					return 0;
 					return 0;
 				}
 				}
 				skill->emotion = static_cast<int16>(constant);
 				skill->emotion = static_cast<int16>(constant);
 			} else {
 			} else {
 				if (!skill_exists)
 				if (!skill_exists)
-					skill->emotion = -1;
+					skill->emotion = ET_NONE;
 			}
 			}
 
 
 			if (this->nodeExists(it, "Chat")) {
 			if (this->nodeExists(it, "Chat")) {
@@ -5969,7 +6094,7 @@ uint64 MobSkillDatabase::parseBodyNode(const YAML::Node &node) {
 					return 0;
 					return 0;
 
 
 				if (mob_chat_db.find(msg_id) == nullptr) {
 				if (mob_chat_db.find(msg_id) == nullptr) {
-					this->invalidWarning(it["Chat"], "Unknown chat ID %hu.\n", msg_id);
+					this->invalidWarning(it["Chat"], "Unknown chat ID %hu, defaulting to 0.\n", msg_id);
 					msg_id = 0;
 					msg_id = 0;
 				}
 				}
 
 
@@ -6032,16 +6157,33 @@ void MobSkillDatabase::loadingFinished() {
 				skill->target = MST_TARGET;
 				skill->target = MST_TARGET;
 			}
 			}
 
 
-			if (skill->skill_id == NPC_EMOTION && mob != nullptr && skill->val[1] == mob->status.mode) {
-				skill->val[1] = 0;
-				skill->val[4] = 1; // request to return mode to normal.
-			}
-			if (skill->skill_id == NPC_EMOTION_ON && mob_id > 0 && skill->val[1]) {	// Adds a mode to the mob.
-				// Remove aggressive mode when the new mob type is passive.
-				if (!(skill->val[1]&MD_AGGRESSIVE))
-					skill->val[3] |= MD_AGGRESSIVE;
-				skill->val[2] |= skill->val[1]; //Add the new mode.
-				skill->val[1] = 0; //Do not "set" it.
+			// Remove the condition value not required
+			switch( skill->cond1 ) {
+				case MSC_ALWAYS:
+				case MSC_SPAWN:
+				case MSC_CLOSEDATTACKED:
+				case MSC_LONGRANGEATTACKED:
+				case MSC_CASTTARGETED:
+				case MSC_RUDEATTACKED:
+					skill->cond2 = 0;
+					skill->cond3 = 0;
+					break;
+				case MSC_MYHPLTMAXRATE:
+				case MSC_MYSTATUSON:
+				case MSC_MYSTATUSOFF:
+				case MSC_FRIENDSTATUSON:
+				case MSC_FRIENDSTATUSOFF:
+				case MSC_ATTACKPCGT:
+				case MSC_ATTACKPCGE:
+				case MSC_SLAVELT:
+				case MSC_SLAVELE:
+				case MSC_SKILLUSED:
+				case MSC_AFTERSKILL:
+					skill->cond3 = 0;
+					break;
+				case MSC_MYHPINRATE:
+				case MSC_FRIENDHPINRATE:
+					break;
 			}
 			}
 		}
 		}
 	}
 	}
@@ -6079,6 +6221,7 @@ static bool mob_read_sqlskilldb_sub(std::vector<std::string> str) {
 
 
 	YAML::Node skills;
 	YAML::Node skills;
 
 
+	// skills["Id"] = ;
 	skills["State"] = str[++index];
 	skills["State"] = str[++index];
 	uint16 skill_id = static_cast<uint16>(std::stoul(str[++index]));
 	uint16 skill_id = static_cast<uint16>(std::stoul(str[++index]));
 
 
@@ -6088,36 +6231,28 @@ static bool mob_read_sqlskilldb_sub(std::vector<std::string> str) {
 		return 0;
 		return 0;
 	}
 	}
 
 
-	skills["Name"] = skill->name;
-	skills["Level"] = std::stoi(str[++index]);
+	// skills["Name"] = skill->name;
+	// skills["Level"] = std::stoi(str[++index]);
 
 
-	YAML::Node cast;
-	cast["Rate"] = std::stoi(str[++index]);
-	cast["Time"] = std::stoi(str[++index]);
-	cast["Delay"] = std::stoi(str[++index]);
-	cast["Cancelable"] = str[++index];
+	// skills["CastRate"] = std::stoi(str[++index]);
+	// skills["CastTime"] = std::stoi(str[++index]);
+	// skills["CastDelay"] = std::stoi(str[++index]);
+	// skills["CastCancel"] = str[++index];
 
 
-	cast["Cast"] = cast;
+	// skills["Target"] = str[++index];
 
 
-	skills["Target"] = str[++index];
-
-	YAML::Node Condition;
-	Condition["Cond1"] = str[++index];
-	std::string cond2 = str[++index];
-	if (atoi(cond2.c_str()) > 0)
-		Condition["Cond2Value"] = str[++index];
-	else
-		Condition["Cond2String"] = str[++index];
-	Condition["Val0"] = str[++index];
-	Condition["Val1"] = str[++index];
-	Condition["Val2"] = str[++index];
-	Condition["Val3"] = str[++index];
-	Condition["Val4"] = str[++index];
+	// skills["Condition"] = str[++index];
+		// Condition["Cond2String"] = str[++index];
+	// Condition["Val0"] = str[++index];
+	// Condition["Val1"] = str[++index];
+	// Condition["Val2"] = str[++index];
+	// Condition["Val3"] = str[++index];
+	// Condition["Val4"] = str[++index];
 
 
-	skills["Condition"] = Condition;
 
 
-	skills["Emotion"] = str[++index];
-	skills["Chat"] = std::stoi(str[++index]);
+	// todo
+	// skills["Emotion"] = str[++index];
+	// skills["Chat"] = str[++index];
 
 
 	node["Skills"] = skills;
 	node["Skills"] = skills;
 
 
@@ -6161,8 +6296,8 @@ static int mob_read_sqlskilldb(void)
 					data.push_back(str);
 					data.push_back(str);
 			}
 			}
 
 
-			if (!mob_read_sqlskilldb_sub(data))
-				continue;
+			// if (!mob_read_sqlskilldb_sub(data))
+				// continue;
 
 
 			count++;
 			count++;
 		}
 		}
@@ -6434,16 +6569,14 @@ static void mob_drop_ratio_adjust(void){
  * @param skill Monster skill entries
  * @param skill Monster skill entries
  **/
  **/
 static void mob_skill_db_set_single(std::shared_ptr<s_mob_db> mob, std::shared_ptr<s_mob_skill_db> mob_skill) {
 static void mob_skill_db_set_single(std::shared_ptr<s_mob_db> mob, std::shared_ptr<s_mob_skill_db> mob_skill) {
-	nullpo_retv(mob_skill);
-
-	if (mob == nullptr)
+	if (mob == nullptr || mob_skill == nullptr)
 		return;
 		return;
 	if (mob_skill->skills.empty())
 	if (mob_skill->skills.empty())
 		return;
 		return;
 
 
 	for (const auto &it : mob_skill->skills) {
 	for (const auto &it : mob_skill->skills) {
 		if (mob->skill.size() >= MAX_MOBSKILL) {
 		if (mob->skill.size() >= MAX_MOBSKILL) {
-			ShowWarning("Monster '%s' (%d, src:%d) reaches max skill limit %d..\n", mob->sprite.c_str(), mob->vd.class_, mob_skill->mob_id, MAX_MOBSKILL);
+			ShowWarning("Monster '%s' (%d, src:%d) exceeds max skill limit %d..\n", mob->sprite.c_str(), mob->vd.class_, mob_skill->mob_id, MAX_MOBSKILL);
 			break;
 			break;
 		}
 		}
 		mob->skill.push_back(it.second);
 		mob->skill.push_back(it.second);
@@ -6456,7 +6589,8 @@ static void mob_skill_db_set_single(std::shared_ptr<s_mob_db> mob, std::shared_p
 static void mob_skill_db_set(void) {
 static void mob_skill_db_set(void) {
 	for (const auto &mob_skill : mob_skill_db) {
 	for (const auto &mob_skill : mob_skill_db) {
 		std::shared_ptr<s_mob_skill_db> skill = mob_skill.second;
 		std::shared_ptr<s_mob_skill_db> skill = mob_skill.second;
-		nullpo_retv(skill);
+		if (skill == nullptr)
+			continue;
 
 
 		// Specific monster
 		// Specific monster
 		if (skill->mob_id >= 0) {
 		if (skill->mob_id >= 0) {
@@ -6490,11 +6624,6 @@ static void mob_skill_db_set(void) {
  */
  */
 static void mob_load(void)
 static void mob_load(void)
 {
 {
-	const char* dbsubpath[] = {
-		"",
-		"/" DBIMPORT,
-	};
-
 	// First we parse all the possible monsters to add additional data in the second loop
 	// First we parse all the possible monsters to add additional data in the second loop
 	if( db_use_sqldbs )
 	if( db_use_sqldbs )
 		mob_read_sqldb();
 		mob_read_sqldb();
@@ -6502,33 +6631,9 @@ static void mob_load(void)
 		mob_db.load();
 		mob_db.load();
 
 
 	mob_chat_db.load();	// load before mob_skill_db
 	mob_chat_db.load();	// load before mob_skill_db
-
-	for(int i = 0; i < ARRAYLENGTH(dbsubpath); i++){	
-		int n1 = strlen(db_path)+strlen(dbsubpath[i])+1;
-		int n2 = strlen(db_path)+strlen(DBPATH)+strlen(dbsubpath[i])+1;
-
-		char* dbsubpath1 = (char*)aMalloc(n1+1);
-		char* dbsubpath2 = (char*)aMalloc(n2+1);
-		bool silent = i > 0;
-
-		if(i==0) {
-			safesnprintf(dbsubpath1,n1,"%s%s",db_path,dbsubpath[i]);
-			safesnprintf(dbsubpath2,n2,"%s/%s%s",db_path,DBPATH,dbsubpath[i]);
-		} else {
-			safesnprintf(dbsubpath1,n1,"%s%s",db_path,dbsubpath[i]);
-			safesnprintf(dbsubpath2,n1,"%s%s",db_path,dbsubpath[i]);
-		}
-
-
-
-		aFree(dbsubpath1);
-		aFree(dbsubpath2);
-	}
-
 	mob_item_drop_ratio.load();
 	mob_item_drop_ratio.load();
 	mob_avail_db.load();
 	mob_avail_db.load();
 	mob_summon_db.load();
 	mob_summon_db.load();
-
 	mob_drop_ratio_adjust();
 	mob_drop_ratio_adjust();
 
 
 	if (battle_config.mob_skill_rate == 0)
 	if (battle_config.mob_skill_rate == 0)

+ 48 - 46
src/map/mob.hpp

@@ -185,15 +185,60 @@ enum e_aegis_monsterclass : int8 {
 	CLASS_MAX,
 	CLASS_MAX,
 };
 };
 
 
+enum e_mob_skill_target {
+	MST_TARGET	=	0,
+	MST_RANDOM,	//Random Target!
+	MST_SELF,
+	MST_FRIEND,
+	MST_MASTER,
+	MST_AROUND5,
+	MST_AROUND6,
+	MST_AROUND7,
+	MST_AROUND8,
+	MST_AROUND1,
+	MST_AROUND2,
+	MST_AROUND3,
+	MST_AROUND4,
+	MST_AROUND	=	MST_AROUND4,
+};
+
+enum e_mob_skill_condition {
+	MSC_ALWAYS	=	0x0000,
+	MSC_MYHPLTMAXRATE,
+	MSC_MYHPINRATE,
+	MSC_FRIENDHPLTMAXRATE,
+	MSC_FRIENDHPINRATE,
+	MSC_MYSTATUSON,
+	MSC_MYSTATUSOFF,
+	MSC_FRIENDSTATUSON,
+	MSC_FRIENDSTATUSOFF,
+	MSC_ATTACKPCGT,
+	MSC_ATTACKPCGE,
+	MSC_SLAVELT,
+	MSC_SLAVELE,
+	MSC_CLOSEDATTACKED,
+	MSC_LONGRANGEATTACKED,
+	MSC_AFTERSKILL,
+	MSC_SKILLUSED,
+	MSC_CASTTARGETED,
+	MSC_RUDEATTACKED,
+	MSC_MASTERHPLTMAXRATE,
+	MSC_MASTERATTACKED,
+	MSC_ALCHEMIST,
+	MSC_SPAWN,
+};
+
 struct s_mob_skill {
 struct s_mob_skill {
 	e_MobSkillState state;
 	e_MobSkillState state;
 	uint16 skill_id,skill_lv;
 	uint16 skill_id,skill_lv;
 	short permillage;
 	short permillage;
 	int casttime,delay;
 	int casttime,delay;
 	bool cancel;
 	bool cancel;
-	short cond1,cond2;
-	short target;
-	int val[5];
+	e_mob_skill_target target;
+	int cond1;
+	uint16 cond2, cond3;
+	int mob_mode;
+	int val[6];
 	short emotion;
 	short emotion;
 	unsigned short msg_id;
 	unsigned short msg_id;
 };
 };
@@ -405,49 +450,6 @@ public:
 	uint64 parseBodyNode(const ryml::NodeRef node) override;
 	uint64 parseBodyNode(const ryml::NodeRef node) override;
 };
 };
 
 
-enum e_mob_skill_target {
-	MST_TARGET	=	0,
-	MST_RANDOM,	//Random Target!
-	MST_SELF,
-	MST_FRIEND,
-	MST_MASTER,
-	MST_AROUND5,
-	MST_AROUND6,
-	MST_AROUND7,
-	MST_AROUND8,
-	MST_AROUND1,
-	MST_AROUND2,
-	MST_AROUND3,
-	MST_AROUND4,
-	MST_AROUND	=	MST_AROUND4,
-};
-
-enum e_mob_skill_condition {
-	MSC_ALWAYS	=	0x0000,
-	MSC_MYHPLTMAXRATE,
-	MSC_MYHPINRATE,
-	MSC_FRIENDHPLTMAXRATE,
-	MSC_FRIENDHPINRATE,
-	MSC_MYSTATUSON,
-	MSC_MYSTATUSOFF,
-	MSC_FRIENDSTATUSON,
-	MSC_FRIENDSTATUSOFF,
-	MSC_ATTACKPCGT,
-	MSC_ATTACKPCGE,
-	MSC_SLAVELT,
-	MSC_SLAVELE,
-	MSC_CLOSEDATTACKED,
-	MSC_LONGRANGEATTACKED,
-	MSC_AFTERSKILL,
-	MSC_SKILLUSED,
-	MSC_CASTTARGETED,
-	MSC_RUDEATTACKED,
-	MSC_MASTERHPLTMAXRATE,
-	MSC_MASTERATTACKED,
-	MSC_ALCHEMIST,
-	MSC_SPAWN,
-};
-
 // The data structures for storing delayed item drops
 // The data structures for storing delayed item drops
 struct item_drop {
 struct item_drop {
 	struct item item_data;
 	struct item item_data;

+ 22 - 18
src/map/script.cpp

@@ -13260,8 +13260,8 @@ BUILDIN_FUNC(emotion)
 	struct block_list *bl = NULL;
 	struct block_list *bl = NULL;
 	int type = script_getnum(st,2);
 	int type = script_getnum(st,2);
 
 
-	if (type < ET_SURPRISE || type >= ET_MAX) {
-		ShowWarning("buildin_emotion: Unknown emotion %d (min=%d, max=%d).\n", type, ET_SURPRISE, (ET_MAX-1));
+	if (type <= ET_NONE || type >= ET_MAX) {
+		ShowWarning("buildin_emotion: Unknown emotion %d (min=%d, max=%d).\n", type, (ET_NONE+1), (ET_MAX-1));
 		return SCRIPT_CMD_FAILURE;
 		return SCRIPT_CMD_FAILURE;
 	}
 	}
 
 
@@ -19495,8 +19495,8 @@ BUILDIN_FUNC(unitemote)
 
 
 	emotion = script_getnum(st,3);
 	emotion = script_getnum(st,3);
 
 
-	if (emotion < ET_SURPRISE || emotion >= ET_MAX) {
-		ShowWarning("buildin_emotion: Unknown emotion %d (min=%d, max=%d).\n", emotion, ET_SURPRISE, (ET_MAX-1));
+	if (emotion <= ET_NONE || emotion >= ET_MAX) {
+		ShowWarning("buildin_emotion: Unknown emotion %d (min=%d, max=%d).\n", emotion, (ET_NONE+1), (ET_MAX-1));
 		return SCRIPT_CMD_FAILURE;
 		return SCRIPT_CMD_FAILURE;
 	}
 	}
 
 
@@ -21629,7 +21629,7 @@ static int buildin_mobuseskill_sub(struct block_list *bl,va_list ap)
 	uint16 skill_id	= va_arg(ap,int);
 	uint16 skill_id	= va_arg(ap,int);
 	uint16 skill_lv	= va_arg(ap,int);
 	uint16 skill_lv	= va_arg(ap,int);
 	int casttime	= va_arg(ap,int);
 	int casttime	= va_arg(ap,int);
-	int cancel		= va_arg(ap,int);
+	bool cancel		= va_arg(ap,int);
 	int emotion		= va_arg(ap,int);
 	int emotion		= va_arg(ap,int);
 	int target		= va_arg(ap,int);
 	int target		= va_arg(ap,int);
 
 
@@ -21665,20 +21665,21 @@ static int buildin_mobuseskill_sub(struct block_list *bl,va_list ap)
  *------------------------------------------*/
  *------------------------------------------*/
 BUILDIN_FUNC(areamobuseskill)
 BUILDIN_FUNC(areamobuseskill)
 {
 {
-	struct block_list center;
-	int16 m;
-	int range,mobid,skill_id,skill_lv,casttime,emotion,target,cancel;
+	int16 m = map_mapname2mapid(script_getstr(st,2));
 
 
-	if( (m = map_mapname2mapid(script_getstr(st,2))) < 0 ) {
+	if (m < 0) {
 		ShowError("areamobuseskill: invalid map name.\n");
 		ShowError("areamobuseskill: invalid map name.\n");
 		return SCRIPT_CMD_SUCCESS;
 		return SCRIPT_CMD_SUCCESS;
 	}
 	}
 
 
+	block_list center;
+	int skill_id;
+	
 	center.m = m;
 	center.m = m;
 	center.x = script_getnum(st,3);
 	center.x = script_getnum(st,3);
 	center.y = script_getnum(st,4);
 	center.y = script_getnum(st,4);
-	range = script_getnum(st,5);
-	mobid = script_getnum(st,6);
+	int range = script_getnum(st,5);
+	int mobid = script_getnum(st,6);
 	if (script_isstring(st, 7)) {
 	if (script_isstring(st, 7)) {
 		const char *name = script_getstr(st, 7);
 		const char *name = script_getstr(st, 7);
 
 
@@ -21694,13 +21695,16 @@ BUILDIN_FUNC(areamobuseskill)
 			return SCRIPT_CMD_FAILURE;
 			return SCRIPT_CMD_FAILURE;
 		}
 		}
 	}
 	}
-	if( (skill_lv = script_getnum(st,8)) > battle_config.mob_max_skilllvl )
+
+	int skill_lv = script_getnum(st,8);
+
+	if (skill_lv > battle_config.mob_max_skilllvl)
 		skill_lv = battle_config.mob_max_skilllvl;
 		skill_lv = battle_config.mob_max_skilllvl;
 
 
-	casttime = script_getnum(st,9);
-	cancel = script_getnum(st,10);
-	emotion = script_getnum(st,11);
-	target = script_getnum(st,12);
+	int casttime = script_getnum(st,9);
+	bool cancel = script_getnum(st,10);
+	int emotion = script_getnum(st,11);
+	int target = script_getnum(st,12);
 
 
 	map_foreachinallrange(buildin_mobuseskill_sub, &center, range, BL_MOB, mobid, skill_id, skill_lv, casttime, cancel, emotion, target);
 	map_foreachinallrange(buildin_mobuseskill_sub, &center, range, BL_MOB, mobid, skill_id, skill_lv, casttime, cancel, emotion, target);
 	return SCRIPT_CMD_SUCCESS;
 	return SCRIPT_CMD_SUCCESS;
@@ -22469,9 +22473,9 @@ BUILDIN_FUNC(npcskill)
 		status_calc_npc(nd, SCO_NONE);
 		status_calc_npc(nd, SCO_NONE);
 
 
 	if (skill_get_inf(skill_id)&INF_GROUND_SKILL)
 	if (skill_get_inf(skill_id)&INF_GROUND_SKILL)
-		unit_skilluse_pos2(&nd->bl, sd->bl.x, sd->bl.y, skill_id, skill_level,0,0);
+		unit_skilluse_pos2(&nd->bl, sd->bl.x, sd->bl.y, skill_id, skill_level,0,false);
 	else
 	else
-		unit_skilluse_id2(&nd->bl, sd->bl.id, skill_id, skill_level,0,0);
+		unit_skilluse_id2(&nd->bl, sd->bl.id, skill_id, skill_level,0,false);
 
 
 	return SCRIPT_CMD_SUCCESS;
 	return SCRIPT_CMD_SUCCESS;
 }
 }

+ 2 - 14
src/map/script_constants.hpp

@@ -956,6 +956,7 @@
 	export_constant2("Eff_Deepsleep",SC_DEEPSLEEP);
 	export_constant2("Eff_Deepsleep",SC_DEEPSLEEP);
 
 
 	export_constant2("SC_ALL",SC_NONE);
 	export_constant2("SC_ALL",SC_NONE);
+	export_constant2("SC_ANYBAD",SC_NONE);
 
 
 	export_constant(SC_NONE);
 	export_constant(SC_NONE);
 	export_constant(SC_STONE);
 	export_constant(SC_STONE);
@@ -4133,6 +4134,7 @@
 	export_constant2("ATF_SKILL",ATF_MAGIC|ATF_MISC);
 	export_constant2("ATF_SKILL",ATF_MAGIC|ATF_MISC);
 
 
 	/* emoticons */
 	/* emoticons */
+	export_constant(ET_NONE);
 	export_constant(ET_SURPRISE);
 	export_constant(ET_SURPRISE);
 	export_constant(ET_QUESTION);
 	export_constant(ET_QUESTION);
 	export_constant(ET_DELIGHT);
 	export_constant(ET_DELIGHT);
@@ -8960,20 +8962,6 @@
 	export_constant(MSC_MASTERATTACKED);
 	export_constant(MSC_MASTERATTACKED);
 	export_constant(MSC_ALCHEMIST);
 	export_constant(MSC_ALCHEMIST);
 	export_constant2("MSC_ONSPAWN",MSC_SPAWN);
 	export_constant2("MSC_ONSPAWN",MSC_SPAWN);
-
-	/* cond2 */
-	export_constant2("SC_ANYBAD",-1);
-	// {	"stone",		SC_STONE		},
-	// {	"freeze",		SC_FREEZE		},
-	// {	"stun",			SC_STUN			},
-	// {	"sleep",		SC_SLEEP		},
-	// {	"poison",		SC_POISON		},
-	// {	"curse",		SC_CURSE		},
-	// {	"silence",		SC_SILENCE		},
-	// {	"confusion",	SC_CONFUSION	},
-	// {	"blind",		SC_BLIND		},
-	// {	"hiding",		SC_HIDING		},
-	// {	"sight",		SC_SIGHT		},
 		
 		
 	#undef export_constant
 	#undef export_constant
 	#undef export_constant2
 	#undef export_constant2

+ 27 - 13
src/map/skill.cpp

@@ -184,7 +184,7 @@ int skill_get_walkdelay( uint16 skill_id ,uint16 skill_lv )        { skill_get_l
 int skill_get_time( uint16 skill_id ,uint16 skill_lv )             { skill_get_lv(skill_id, skill_lv, skill_db.find(skill_id)->upkeep_time); }
 int skill_get_time( uint16 skill_id ,uint16 skill_lv )             { skill_get_lv(skill_id, skill_lv, skill_db.find(skill_id)->upkeep_time); }
 int skill_get_time2( uint16 skill_id ,uint16 skill_lv )            { skill_get_lv(skill_id, skill_lv, skill_db.find(skill_id)->upkeep_time2); }
 int skill_get_time2( uint16 skill_id ,uint16 skill_lv )            { skill_get_lv(skill_id, skill_lv, skill_db.find(skill_id)->upkeep_time2); }
 int skill_get_castdef( uint16 skill_id )                           { skill_get(skill_id, skill_db.find(skill_id)->cast_def_rate); }
 int skill_get_castdef( uint16 skill_id )                           { skill_get(skill_id, skill_db.find(skill_id)->cast_def_rate); }
-int skill_get_castcancel( uint16 skill_id )                        { skill_get(skill_id, skill_db.find(skill_id)->castcancel); }
+bool skill_get_castcancel( uint16 skill_id )                       { return (skill_check(skill_id) && skill_db.find(skill_id)->castcancel); }
 int skill_get_maxcount( uint16 skill_id ,uint16 skill_lv )         { skill_get_lv(skill_id, skill_lv, skill_db.find(skill_id)->maxcount); }
 int skill_get_maxcount( uint16 skill_id ,uint16 skill_lv )         { skill_get_lv(skill_id, skill_lv, skill_db.find(skill_id)->maxcount); }
 int skill_get_blewcount( uint16 skill_id ,uint16 skill_lv )        { skill_get_lv(skill_id, skill_lv, skill_db.find(skill_id)->blewcount); }
 int skill_get_blewcount( uint16 skill_id ,uint16 skill_lv )        { skill_get_lv(skill_id, skill_lv, skill_db.find(skill_id)->blewcount); }
 int skill_get_castnodex( uint16 skill_id )                         { skill_get(skill_id, skill_db.find(skill_id)->castnodex); }
 int skill_get_castnodex( uint16 skill_id )                         { skill_get(skill_id, skill_db.find(skill_id)->castnodex); }
@@ -9507,28 +9507,42 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui
 		break;
 		break;
 
 
 	case NPC_EMOTION_ON:
 	case NPC_EMOTION_ON:
+		//NPC_EMOTION & NPC_EMOTION_ON can change a mob's mode 'permanently' [Skotlex]
+		if(md && md->skill_idx >= 0 && tsc)
+		{
+			int mode_passive = (md->db->skill[md->skill_idx]->mob_mode & MD_AGGRESSIVE) ? 0 : MD_AGGRESSIVE;	// Remove aggressive mode when the new mob type is passive.
+
+			if (md->db->skill[md->skill_idx]->mob_mode > -1)
+				sc_start4(src,src, type, 100, skill_lv,
+					0,	// 'sets' the mode
+					md->db->skill[md->skill_idx]->mob_mode,	// adds to the current mode
+					mode_passive,	// removes from the current mode
+					skill_get_time(skill_id, skill_lv));
+
+			//Reset aggressive state depending on resulting mode
+			if (!battle_config.npc_emotion_behavior)
+				md->state.aggressive = status_has_mode(&md->status,MD_ANGRY)?1:0;
+		}
+		break;
+
 	case NPC_EMOTION:
 	case NPC_EMOTION:
-		//val[0] is the emotion to use.
 		//NPC_EMOTION & NPC_EMOTION_ON can change a mob's mode 'permanently' [Skotlex]
 		//NPC_EMOTION & NPC_EMOTION_ON can change a mob's mode 'permanently' [Skotlex]
-		//val[1] 'sets' the mode
-		//val[2] adds to the current mode
-		//val[3] removes from the current mode
-		//val[4] if set, asks to delete the previous mode change.
 		if(md && md->skill_idx >= 0 && tsc)
 		if(md && md->skill_idx >= 0 && tsc)
 		{
 		{
-			clif_emotion(bl, md->db->skill[md->skill_idx]->val[0]);
-			if(md->db->skill[md->skill_idx]->val[4] && tsce)
+			std::shared_ptr<s_mob_skill> skilltmp = md->db->skill[md->skill_idx];
+
+			if (skilltmp->mob_mode > -1 && skilltmp->mob_mode == md->db->status.mode && tsce)	// asks to delete the previous mode change
 				status_change_end(bl, type, INVALID_TIMER);
 				status_change_end(bl, type, INVALID_TIMER);
 
 
 			//If mode gets set by NPC_EMOTION then the target should be reset [Playtester]
 			//If mode gets set by NPC_EMOTION then the target should be reset [Playtester]
-			if(!battle_config.npc_emotion_behavior && skill_id == NPC_EMOTION && md->db->skill[md->skill_idx]->val[1])
+			if (!battle_config.npc_emotion_behavior && skilltmp->mob_mode > -1 && skilltmp->mob_mode != md->db->status.mode)
 				mob_unlocktarget(md,tick);
 				mob_unlocktarget(md,tick);
 
 
-			if(md->db->skill[md->skill_idx]->val[1] || md->db->skill[md->skill_idx]->val[2])
+			if (skilltmp->mob_mode > -1 && skilltmp->mob_mode != md->db->status.mode)
 				sc_start4(src,src, type, 100, skill_lv,
 				sc_start4(src,src, type, 100, skill_lv,
-					md->db->skill[md->skill_idx]->val[1],
-					md->db->skill[md->skill_idx]->val[2],
-					md->db->skill[md->skill_idx]->val[3],
+					skilltmp->mob_mode,	// 'sets' the mode
+					0,	// adds to the current mode
+					0,	// removes from the current mode
 					skill_get_time(skill_id, skill_lv));
 					skill_get_time(skill_id, skill_lv));
 
 
 			//Reset aggressive state depending on resulting mode
 			//Reset aggressive state depending on resulting mode

+ 1 - 1
src/map/skill.hpp

@@ -529,7 +529,7 @@ int skill_get_castdef( uint16 skill_id );
 int skill_get_nocast( uint16 skill_id );
 int skill_get_nocast( uint16 skill_id );
 int skill_get_unit_id( uint16 skill_id );
 int skill_get_unit_id( uint16 skill_id );
 int skill_get_unit_id2( uint16 skill_id );
 int skill_get_unit_id2( uint16 skill_id );
-int skill_get_castcancel( uint16 skill_id );
+bool skill_get_castcancel( uint16 skill_id );
 int skill_get_maxcount( uint16 skill_id ,uint16 skill_lv );
 int skill_get_maxcount( uint16 skill_id ,uint16 skill_lv );
 int skill_get_blewcount( uint16 skill_id ,uint16 skill_lv );
 int skill_get_blewcount( uint16 skill_id ,uint16 skill_lv );
 int skill_get_cooldown( uint16 skill_id, uint16 skill_lv );
 int skill_get_cooldown( uint16 skill_id, uint16 skill_lv );

+ 2 - 2
src/map/unit.cpp

@@ -1585,7 +1585,7 @@ int unit_set_walkdelay(struct block_list *bl, t_tick tick, t_tick delay, int typ
  * @param castcancel: Whether or not the skill can be cancelled by interruption (hit)
  * @param castcancel: Whether or not the skill can be cancelled by interruption (hit)
  * @return Success(1); Fail(0);
  * @return Success(1); Fail(0);
  */
  */
-int unit_skilluse_id2(struct block_list *src, int target_id, uint16 skill_id, uint16 skill_lv, int casttime, int castcancel)
+int unit_skilluse_id2(struct block_list *src, int target_id, uint16 skill_id, uint16 skill_lv, int casttime, bool castcancel)
 {
 {
 	struct unit_data *ud;
 	struct unit_data *ud;
 	struct status_data *tstatus;
 	struct status_data *tstatus;
@@ -2103,7 +2103,7 @@ int unit_skilluse_pos(struct block_list *src, short skill_x, short skill_y, uint
  * @param castcancel: Whether or not the skill can be cancelled by interuption (hit)
  * @param castcancel: Whether or not the skill can be cancelled by interuption (hit)
  * @return Success(1); Fail(0);
  * @return Success(1); Fail(0);
  */
  */
-int unit_skilluse_pos2( struct block_list *src, short skill_x, short skill_y, uint16 skill_id, uint16 skill_lv, int casttime, int castcancel)
+int unit_skilluse_pos2( struct block_list *src, short skill_x, short skill_y, uint16 skill_id, uint16 skill_lv, int casttime, bool castcancel)
 {
 {
 	struct map_session_data *sd = NULL;
 	struct map_session_data *sd = NULL;
 	struct unit_data        *ud = NULL;
 	struct unit_data        *ud = NULL;

+ 2 - 2
src/map/unit.hpp

@@ -143,8 +143,8 @@ bool unit_can_attack(struct block_list *bl, int target_id);
 // Cast on a unit
 // Cast on a unit
 int unit_skilluse_id(struct block_list *src, int target_id, uint16 skill_id, uint16 skill_lv);
 int unit_skilluse_id(struct block_list *src, int target_id, uint16 skill_id, uint16 skill_lv);
 int unit_skilluse_pos(struct block_list *src, short skill_x, short skill_y, uint16 skill_id, uint16 skill_lv);
 int unit_skilluse_pos(struct block_list *src, short skill_x, short skill_y, uint16 skill_id, uint16 skill_lv);
-int unit_skilluse_id2(struct block_list *src, int target_id, uint16 skill_id, uint16 skill_lv, int casttime, int castcancel);
-int unit_skilluse_pos2( struct block_list *src, short skill_x, short skill_y, uint16 skill_id, uint16 skill_lv, int casttime, int castcancel);
+int unit_skilluse_id2(struct block_list *src, int target_id, uint16 skill_id, uint16 skill_lv, int casttime, bool castcancel);
+int unit_skilluse_pos2( struct block_list *src, short skill_x, short skill_y, uint16 skill_id, uint16 skill_lv, int casttime, bool castcancel);
 
 
 // Step timer used for delayed attack and skill use
 // Step timer used for delayed attack and skill use
 TIMER_FUNC(unit_step_timer);
 TIMER_FUNC(unit_step_timer);

+ 106 - 66
src/tool/csv2yaml.cpp

@@ -4997,42 +4997,95 @@ static bool mob_parse_row_mobskilldb(char** str, int columns, int current) {
 	else
 	else
 		entry.cancel = false;
 		entry.cancel = false;
 
 
-	//Target
+	// Target
 	std::string target_name = str[9];
 	std::string target_name = str[9];
 	std::transform(target_name.begin(), target_name.end(), target_name.begin(), ::toupper);
 	std::transform(target_name.begin(), target_name.end(), target_name.begin(), ::toupper);
 	entry.target_name = target_name;
 	entry.target_name = target_name;
 
 
-	//Cond1
+	// Cond1
 	std::string cond1 = str[10];
 	std::string cond1 = str[10];
 	std::transform(cond1.begin(), cond1.end(), cond1.begin(), ::toupper);
 	std::transform(cond1.begin(), cond1.end(), cond1.begin(), ::toupper);
 	entry.cond1_name = cond1;
 	entry.cond1_name = cond1;
 
 
-	//Cond2
+	// Cond2
 	std::string cond2(str[11]);
 	std::string cond2(str[11]);
-	std::string *name = util::umap_find(um_mob_skill_cond2, cond2);
-
-	// todo sc_ Status
-	std::string cond2_name;
-	if (name != nullptr)
-		entry.cond2_name = *name;
-	else {
-		if (atoi(str[11]) > 0)
+	if (cond1 == "MYSTATUSON" || cond1 == "MYSTATUSOFF" || cond1 == "FRIENDSTATUSON" || cond1 == "FRIENDSTATUSOFF") {	// SC_ status
+		std::transform(cond2.begin(), cond2.end(), cond2.begin(), ::toupper);
+		entry.cond2_name = "SC_" + cond2;
+	}
+	else if (atoi(str[11]) > 0) {
+		if (cond1 != "SKILLUSED" && cond1 != "AFTERSKILL")	// Number
 			entry.cond2_name = cond2;
 			entry.cond2_name = cond2;
+		else {	// Aegis skill name
+			uint16 tmp_skill_id = atoi(str[11]);
+			std::string* tmp_skill_name = util::umap_find( aegis_skillnames, tmp_skill_id );
+
+			if( tmp_skill_name == nullptr ){
+				ShowError( "Invalid specified skill %hu for %s (source: skill id %hu).\n", tmp_skill_id, cond1.c_str(), atoi(str[3]) );
+				return false;
+			}
+			entry.cond2_name = *tmp_skill_name;
+		}
 	}
 	}
-	
+
+	entry.cond3 = 0;
+
 	for (uint16 i = 0; i < 5; ++i) {
 	for (uint16 i = 0; i < 5; ++i) {
 		uint16 index = 12 + i;
 		uint16 index = 12 + i;
 		if (*str[index]) {
 		if (*str[index]) {
-			uint16 val = static_cast<uint16>(atoi(str[index]));
+			int val = (int)strtol(str[index],NULL,0);
+			if (val < 1)
+				continue;
+
 			std::string val_name = std::to_string(val);
 			std::string val_name = std::to_string(val);
-			// todo aegis mob name instead of ID ?
-			// if (val > 1000) {
-				// std::string* mob_name_val = util::umap_find( aegis_mobnames, val );
 
 
-				// if (mob_name_val != nullptr)
-					// val_name = *mob_name_val;
-			// }
-			entry.val.insert({ i, val_name });
+			if (i == 0 && (cond1 == "MYHPINRATE" || cond1 == "FRIENDHPINRATE")) {	// Upper bound now stored in cond3 instead of val 1
+				entry.cond3 = val;
+				continue;
+			}
+
+			switch( skill_id ) {
+				case NPC_METAMORPHOSIS:
+				case NPC_SUMMONSLAVE:
+				case NPC_SUMMONMONSTER:
+				case NPC_DEATHSUMMON: {
+					std::string* mob_name_val = util::umap_find( aegis_mobnames, static_cast<uint16>(val) );
+
+					if (mob_name_val == nullptr) {
+						ShowError( "Mob name for skill id %hu is not known.\n", val );
+						return false;
+					}
+					val_name = *mob_name_val;
+
+					entry.val.insert({ i, val_name });
+					continue;
+				}
+				case NPC_EMOTION:
+				case NPC_EMOTION_ON:
+					if (i == 0) {	// Emoticon is now stored in entry.emotion instead of val 1
+						char *constant = const_cast<char *>(constant_lookup(val, "ET_"));
+
+						if (constant != nullptr)
+							entry.emotion = constant;
+						else {
+							std::string emotion(str[val]);
+							entry.emotion = emotion;
+						}
+						continue;
+					}
+					break;
+				default:
+					break;
+			}
+
+			// In any case the remain values should be the ai
+			std::string *ai = util::umap_find( um_mob_mode2ai, val );
+
+			if (ai == nullptr || strncasecmp(str[index], "0x", 2) != 0) {
+				ShowError( "Unknown Ai for mode %s.\n", str[index] );
+				return false;
+			}
+			entry.mob_ai = *ai;
 		}
 		}
 	}
 	}
 
 
@@ -5066,58 +5119,45 @@ static bool mob_parse_row_mobskilldb_yaml(void) {
 		body << YAML::Key << "Skills";
 		body << YAML::Key << "Skills";
 		body << YAML::BeginSeq;
 		body << YAML::BeginSeq;
 		uint16 count = 0;
 		uint16 count = 0;
-		for (const auto &it : mobit.second) {
-			body << YAML::BeginMap;
-			body << YAML::Key << "Id" << YAML::Value << count;
-			body << YAML::Key << "Name" << YAML::Value << it.skill_name;
-			body << YAML::Key << "Level" << YAML::Value << it.skill_lv;
-			if (it.state_name != "BERSERK")
-				body << YAML::Key << "SkillState" << YAML::Value << it.state_name;
 
 
-			if (it.permillage != 10000 || it.casttime > 0 || it.delay != 5000 || !it.cancel) {
-				body << YAML::Key << "Cast";
-				body << YAML::BeginMap;
-				if (it.permillage != 10000)
-					body << YAML::Key << "Rate" << YAML::Value << it.permillage;
-				if (it.casttime > 0)
-					body << YAML::Key << "Time" << YAML::Value << it.casttime;
-				if (it.delay != 5000)
-					body << YAML::Key << "Delay" << YAML::Value << it.delay;
-				if (!it.cancel)
-					body << YAML::Key << "Cancelable" << YAML::Value << "false";
-				// else
-					// body << YAML::Key << "Cancelable" << YAML::Value << "true";	// default true
-				body << YAML::EndMap;
-			}
-
-			if (it.target_name != "TARGET")
-				body << YAML::Key << "SkillTarget" << YAML::Value << it.target_name;
-
-			if (!it.cond1_name.empty() && it.cond1_name != "ALWAYS" || !it.cond2_name.empty() || !it.val.empty()) {
-				body << YAML::Key << "Condition";
-				body << YAML::BeginMap;
-
-				if (!it.cond1_name.empty() && it.cond1_name != "ALWAYS")
-					body << YAML::Key << "Cond1" << YAML::Value << it.cond1_name;
-				if (!it.cond2_name.empty()) {
-					if (atoi(it.cond2_name.c_str()) > 0)
-						body << YAML::Key << "Cond2Value" << YAML::Value << it.cond2_name;
-					else
-						body << YAML::Key << "Cond2String" << YAML::Value << it.cond2_name;
-				}
-				for (const auto &valit : it.val) {
-					std::string val_name = "Val" + std::to_string(valit.first);
+		for (const auto &mob_skill : mobit.second) {
+			body << YAML::BeginMap;
+			body << YAML::Key << "Index" << YAML::Value << count;
+			body << YAML::Key << "Name" << YAML::Value << mob_skill.skill_name;
+			body << YAML::Key << "Level" << YAML::Value << mob_skill.skill_lv;
+			body << YAML::Key << "State" << YAML::Value << mob_skill.state_name;
+			body << YAML::Key << "CastRate" << YAML::Value << mob_skill.permillage;
+			body << YAML::Key << "CastTime" << YAML::Value << mob_skill.casttime;
+			body << YAML::Key << "CastDelay" << YAML::Value << mob_skill.delay;
+			body << YAML::Key << "CastCancel" << YAML::Value << ( !mob_skill.cancel ? "false" : "true" );
+			body << YAML::Key << "Target" << YAML::Value << mob_skill.target_name;
+			body << YAML::Key << "Condition" << YAML::Value << mob_skill.cond1_name;
+
+			if (!mob_skill.cond2_name.empty())
+				body << YAML::Key << "ConditionValue1" << YAML::Value << mob_skill.cond2_name;
+			if (mob_skill.cond3 > 0)
+				body << YAML::Key << "ConditionValue2" << YAML::Value << mob_skill.cond3;
+			if (!mob_skill.mob_ai.empty())
+				body << YAML::Key << "Ai" << YAML::Value << mob_skill.mob_ai;
+
+			if (!mob_skill.val.empty()) {
+				body << YAML::Key << "Summon";
+				body << YAML::BeginSeq;
 
 
-					body << YAML::Key << val_name << YAML::Value << valit.second;
+				for (const auto &valit : mob_skill.val) {
+					body << YAML::BeginMap;
+					body << YAML::Key << "Index" << YAML::Value << valit.first;
+					body << YAML::Key << "Mob" << YAML::Value << valit.second;
+					body << YAML::EndMap;
 				}
 				}
 
 
-				body << YAML::EndMap;
+				body << YAML::EndSeq;
 			}
 			}
 
 
-			if (!it.emotion.empty())
-				body << YAML::Key << "Emotion" << YAML::Value << it.emotion;
-			if (it.msg_id > 0)
-				body << YAML::Key << "Chat" << YAML::Value << it.msg_id;
+			if (!mob_skill.emotion.empty())
+				body << YAML::Key << "Emotion" << YAML::Value << mob_skill.emotion;
+			if (mob_skill.msg_id > 0)
+				body << YAML::Key << "Chat" << YAML::Value << mob_skill.msg_id;
 			body << YAML::EndMap;
 			body << YAML::EndMap;
 			count++;
 			count++;
 		}
 		}

+ 31 - 5
src/tool/csv2yaml.hpp

@@ -149,10 +149,12 @@ struct s_mercenary_skill_csv {
 std::unordered_map<uint16, std::vector<s_mercenary_skill_csv>> mercenary_skill_tree;
 std::unordered_map<uint16, std::vector<s_mercenary_skill_csv>> mercenary_skill_tree;
 
 
 struct s_mob_skill_csv {
 struct s_mob_skill_csv {
-	std::string state_name, skill_name, cond1_name, cond2_name, target_name, emotion;
+	std::string skill_name, state_name, cond1_name, cond2_name, target_name, emotion;
 	uint16 skill_lv, permillage, msg_id;
 	uint16 skill_lv, permillage, msg_id;
 	int32 casttime, delay;
 	int32 casttime, delay;
-	std::unordered_map<uint16, std::string> val;
+	std::string mob_ai;
+	int32 cond3;	// another condition value for MYHPINRATE and FRIENDHPINRATE
+	std::unordered_map<int, std::string> val;
 	bool cancel;
 	bool cancel;
 };
 };
 
 
@@ -220,20 +222,44 @@ static std::unordered_map<std::string, equip_pos> um_equipnames {
 };
 };
 
 
 static std::unordered_map<std::string, std::string> um_mob_skill_cond2 {
 static std::unordered_map<std::string, std::string> um_mob_skill_cond2 {
-	{	"anybad",		"-1"				},
+	{	"anybad",		"SC_ANYBAD"		},
 	{	"stone",		"SC_STONE"		},
 	{	"stone",		"SC_STONE"		},
 	{	"freeze",		"SC_FREEZE"		},
 	{	"freeze",		"SC_FREEZE"		},
-	{	"stun",			"SC_STUN"			},
+	{	"stun",			"SC_STUN"		},
 	{	"sleep",		"SC_SLEEP"		},
 	{	"sleep",		"SC_SLEEP"		},
 	{	"poison",		"SC_POISON"		},
 	{	"poison",		"SC_POISON"		},
 	{	"curse",		"SC_CURSE"		},
 	{	"curse",		"SC_CURSE"		},
-	{	"silence",		"SC_SILENCE"		},
+	{	"silence",		"SC_SILENCE"	},
 	{	"confusion",	"SC_CONFUSION"	},
 	{	"confusion",	"SC_CONFUSION"	},
 	{	"blind",		"SC_BLIND"		},
 	{	"blind",		"SC_BLIND"		},
 	{	"hiding",		"SC_HIDING"		},
 	{	"hiding",		"SC_HIDING"		},
 	{	"sight",		"SC_SIGHT"		},
 	{	"sight",		"SC_SIGHT"		},
 };
 };
 
 
+static std::unordered_map<int, std::string> um_mob_mode2ai {	// mode, AI
+	{ 0x0081, "01" },
+	{ 0x0083, "02" },
+	{ 0x1089, "03" },
+	{ 0x3885, "04" },
+	{ 0x2085, "05" },
+	{ 0x0000, "06" },
+	{ 0x108B, "07" },
+	{ 0x7085, "08" },
+	{ 0x3095, "09" },
+	{ 0x0084, "10" },
+	{ 0x0084, "11" },
+	{ 0x2085, "12" },
+	{ 0x308D, "13" },
+	{ 0x0091, "17" },
+	{ 0x3095, "19" },
+	{ 0x3295, "20" },
+	{ 0x3695, "21" },
+	{ 0x00A1, "24" },
+	{ 0x0001, "25" },
+	{ 0xB695, "26" },
+	{ 0x8084, "27" },
+};
+
 // Initialize Random Option constants
 // Initialize Random Option constants
 void init_random_option_constants() {
 void init_random_option_constants() {
 	#define export_constant2(a, b) script_set_constant_(a, b, a, false, false)
 	#define export_constant2(a, b) script_set_constant_(a, b, a, false, false)