Browse Source

Converted elemental_db to YAML (#6102)

* Converts the Elemental Summons Tables file into YAML.
* Includes CSV2YAML converter.

Thanks to @Lemongrass3110, @aleos89, @secretdataz !
Atemo 3 years ago
parent
commit
a205287307

+ 0 - 25
db/elemental_db.txt

@@ -1,25 +0,0 @@
-// Elemental Summons Database
-//
-// Structure of Database:
-// ID,Sprite_Name,Name,LV,HP,SP,Range1,ATK1,ATK2,DEF,MDEF,STR,AGI,VIT,INT,DEX,LUK,Range2,Range3,Scale,Race,Element,Speed,aDelay,aMotion,dMotion
-//
-// Notes:
-// Summoned Elemental’s STATs are affected by the Caster’s Base Level and STATs.
-// In other words, all values specified will be added to (and will not override) the calculated STATs of the summoned elemental.
-
-// Monster Elementals
-2114,EL_AGNI_S,Agni,100,0,1,1,0,0,0,0,0,0,0,0,0,0,5,12,0,0,83,200,504,1020,360
-2115,EL_AGNI_M,Agni,100,0,1,1,0,0,0,0,0,0,0,0,0,0,5,12,1,0,83,200,504,1020,360
-2116,EL_AGNI_L,Agni,100,0,1,1,0,0,0,0,0,0,0,0,0,0,5,12,2,0,83,200,504,1020,360
-
-2117,EL_AQUA_S,Aqua,100,0,1,1,0,0,0,0,0,0,0,0,0,0,5,12,0,0,81,200,504,1020,360
-2118,EL_AQUA_M,Aqua,100,0,1,1,0,0,0,0,0,0,0,0,0,0,5,12,1,0,81,200,504,1020,360
-2119,EL_AQUA_L,Aqua,100,0,1,1,0,0,0,0,1,1,1,1,1,1,5,12,2,0,81,200,504,1020,360
-
-2120,EL_VENTUS_S,Ventus,100,0,1,1,0,0,0,0,0,0,0,0,0,0,5,12,0,0,84,200,504,1020,360
-2121,EL_VENTUS_M,Ventus,100,0,1,1,0,0,0,0,0,0,0,0,0,0,5,12,1,0,84,200,504,1020,360
-2122,EL_VENTUS_L,Ventus,100,0,1,1,0,0,0,0,0,0,0,0,0,0,5,12,2,0,84,200,504,1020,360
-
-2123,EL_TERA_S,Tera,100,0,1,1,0,0,0,0,0,0,0,0,0,0,5,12,0,0,82,200,504,1020,360
-2124,EL_TERA_M,Tera,100,0,1,1,0,0,0,0,0,0,0,0,0,0,5,12,1,0,82,200,504,1020,360
-2125,EL_TERA_L,Tera,100,0,1,1,0,0,0,0,0,0,0,0,0,0,5,12,2,0,82,200,504,1020,360

+ 240 - 0
db/elemental_db.yml

@@ -0,0 +1,240 @@
+# This file is a part of rAthena.
+#   Copyright(C) 2021 rAthena Development Team
+#   https://rathena.org - https://github.com/rathena
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+###########################################################################
+# Elemental Summons Database
+###########################################################################
+#
+# Elemental Summons Settings
+#
+###########################################################################
+# - Id                      Elemental ID.
+#   AegisName               Server name to reference the elemental in scripts and lookups, should use no spaces.
+#   Name                    Name in English.
+#   Level                   Level.
+#   Hp                      Additional HP. Base HP depends on caster HP. (Default: 0)
+#   Sp                      Additional SP. Base SP depends on caster SP. (Default: 1)
+#   Attack                  Additional minimum attack. Base attack depends on caster attack. (Default: 0)
+#   Attack2                 Additional maximum attack. Base attack depends on caster attack. (Default: 0)
+#   Defense                 Physical defense of the elemental, reduces melee and ranged physical attack/skill damage. Base defense depends on the caster defense. (Default: 0)
+#   MagicDefense            Magic defense of the elemental, reduces magical skill damage. Base magic defense depends on the caster magic defense. (Default: 0)
+#   Str                     Strength which affects attack. (Default: 0)
+#   Agi                     Agility which affects flee. (Default: 0)
+#   Vit                     Vitality which affects defense. (Default: 0)
+#   Int                     Intelligence which affects magic attack. (Default: 0)
+#   Dex                     Dexterity which affects hit rate. (Default: 0)
+#   Luk                     Luck which affects perfect dodge/lucky flee/perfect flee/lucky dodge rate. (Default: 0)
+#   AttackRange             Attack range. (Default: 1)
+#   SkillRange              Skill cast range. (Default: 5)
+#   ChaseRange              Chase range. (Default: 12)
+#   Size                    Size.
+#   Race                    Race. (Default: Formless)
+#   Element                 Element.
+#   ElementLevel            Level of element.
+#   WalkSpeed               Walk speed. (Default: 200)
+#   AttackDelay             Attack speed. (Default: 504)
+#   AttackMotion            Attack animation speed. Base AttackMotion depends on the caster AttackMotion. (Default: 1020)
+#   DamageMotion            Damage animation speed. (Default: 360)
+#   Mode:                   List of elemental skills by mode. (Optional)
+#     <mode>:               Mode name to define the skill available. Available modes are Passive, Assist, and Aggressive.
+#       Skill               Skill name.
+#       Level               Skill level. (Default: 1)
+###########################################################################
+
+Header:
+  Type: ELEMENTAL_DB
+  Version: 1
+
+Body:
+  - Id: 2114
+    AegisName: EL_AGNI_S
+    Name: Agni
+    Level: 100
+    Size: Small
+    Element: Fire
+    ElementLevel: 4
+    Mode:
+      Passive:
+        Skill: EL_PYROTECHNIC
+      Assist:
+        Skill: EL_CIRCLE_OF_FIRE
+      Aggressive:
+        Skill: EL_FIRE_ARROW
+  - Id: 2115
+    AegisName: EL_AGNI_M
+    Name: Agni
+    Level: 100
+    Size: Medium
+    Element: Fire
+    ElementLevel: 4
+    Mode:
+      Passive:
+        Skill: EL_HEATER
+      Assist:
+        Skill: EL_FIRE_CLOAK
+      Aggressive:
+        Skill: EL_FIRE_BOMB
+  - Id: 2116
+    AegisName: EL_AGNI_L
+    Name: Agni
+    Level: 100
+    Size: Large
+    Element: Fire
+    ElementLevel: 4
+    Mode:
+      Passive:
+        Skill: EL_TROPIC
+      Assist:
+        Skill: EL_FIRE_MANTLE
+      Aggressive:
+        Skill: EL_FIRE_WAVE
+  - Id: 2117
+    AegisName: EL_AQUA_S
+    Name: Aqua
+    Level: 100
+    Size: Small
+    Element: Water
+    ElementLevel: 4
+    Mode:
+      Passive:
+        Skill: EL_AQUAPLAY
+      Assist:
+        Skill: EL_WATER_SCREEN
+      Aggressive:
+        Skill: EL_ICE_NEEDLE
+  - Id: 2118
+    AegisName: EL_AQUA_M
+    Name: Aqua
+    Level: 100
+    Size: Medium
+    Element: Water
+    ElementLevel: 4
+    Mode:
+      Passive:
+        Skill: EL_COOLER
+      Assist:
+        Skill: EL_WATER_DROP
+      Aggressive:
+        Skill: EL_WATER_SCREW
+  - Id: 2119
+    AegisName: EL_AQUA_L
+    Name: Aqua
+    Level: 100
+    Str: 1
+    Agi: 1
+    Vit: 1
+    Int: 1
+    Dex: 1
+    Luk: 1
+    Size: Large
+    Element: Water
+    ElementLevel: 4
+    Mode:
+      Passive:
+        Skill: EL_CHILLY_AIR
+      Assist:
+        Skill: EL_WATER_BARRIER
+      Aggressive:
+        Skill: EL_TIDAL_WEAPON
+  - Id: 2120
+    AegisName: EL_VENTUS_S
+    Name: Ventus
+    Level: 100
+    Size: Small
+    Element: Wind
+    ElementLevel: 4
+    Mode:
+      Passive:
+        Skill: EL_GUST
+      Assist:
+        Skill: EL_WIND_STEP
+      Aggressive:
+        Skill: EL_WIND_SLASH
+  - Id: 2121
+    AegisName: EL_VENTUS_M
+    Name: Ventus
+    Level: 100
+    Size: Medium
+    Element: Wind
+    ElementLevel: 4
+    Mode:
+      Passive:
+        Skill: EL_BLAST
+      Assist:
+        Skill: EL_WIND_CURTAIN
+      Aggressive:
+        Skill: EL_HURRICANE
+  - Id: 2122
+    AegisName: EL_VENTUS_L
+    Name: Ventus
+    Level: 100
+    Size: Large
+    Element: Wind
+    ElementLevel: 4
+    Mode:
+      Passive:
+        Skill: EL_WILD_STORM
+      Assist:
+        Skill: EL_ZEPHYR
+      Aggressive:
+        Skill: EL_TYPOON_MIS
+  - Id: 2123
+    AegisName: EL_TERA_S
+    Name: Tera
+    Level: 100
+    Size: Small
+    Element: Earth
+    ElementLevel: 4
+    Mode:
+      Passive:
+        Skill: EL_PETROLOGY
+      Assist:
+        Skill: EL_SOLID_SKIN
+      Aggressive:
+        Skill: EL_STONE_HAMMER
+  - Id: 2124
+    AegisName: EL_TERA_M
+    Name: Tera
+    Level: 100
+    Size: Medium
+    Element: Earth
+    ElementLevel: 4
+    Mode:
+      Passive:
+        Skill: EL_CURSED_SOIL
+      Assist:
+        Skill: EL_STONE_SHIELD
+      Aggressive:
+        Skill: EL_ROCK_CRUSHER
+  - Id: 2125
+    AegisName: EL_TERA_L
+    Name: Tera
+    Level: 100
+    Size: Large
+    Element: Earth
+    ElementLevel: 4
+    Mode:
+      Passive:
+        Skill: EL_UPHEAVAL
+      Assist:
+        Skill: EL_POWER_OF_GAIA
+      Aggressive:
+        Skill: EL_STONE_RAIN
+
+Footer:
+  Imports:
+  - Path: db/import/elemental_db.yml

+ 0 - 56
db/elemental_skill_db.txt

@@ -1,56 +0,0 @@
-// Elemental Summons Skill Database
-//
-// Structure of Database:
-// ElementalID,SkillID,SkillLevel,ReqMode
-//
-// Spirit Modes:
-//	1 = Passive, 2 = Defensive, 4 = Aggressive
-
-// EL_AGNI_S
-2114,8413,1,1	//EL_PYROTECHNIC,Pyrotechnic
-2114,8401,1,2	//EL_CIRCLE_OF_FIRE,Circle of Fire
-2114,8425,1,4	//EL_FIRE_ARROW,Fire Arrow
-// EL_AGNI_M
-2115,8414,1,1	//EL_HEATER,Heater
-2115,8402,1,2	//EL_FIRE_CLOAK,Fire Cloak
-2115,8426,1,4	//EL_FIRE_BOMB,Fire Bomb
-// EL_AGNI_L
-2116,8415,1,1	//EL_TROPIC,Tropic
-2116,8403,1,2	//EL_FIRE_MANTLE,Fire Mantle
-2116,8428,1,4	//EL_FIRE_WAVE,Fire Wave
-// EL_AQUA_S
-2117,8416,1,1	//EL_AQUAPLAY,Aqua Play
-2117,8404,1,2	//EL_WATER_SCREEN,Water Screen
-2117,8430,1,4	//EL_ICE_NEEDLE,Ice Needle
-// EL_AQUA_M
-2118,8417,1,1	//EL_COOLER,Cooler
-2118,8405,1,2	//EL_WATER_DROP,Water Drop
-2118,8431,1,4	//EL_WATER_SCREW,Water Screw
-// EL_AQUA_L
-2119,8418,1,1	//EL_CHILLY_AIR,Cool Air
-2119,8406,1,2	//EL_WATER_BARRIER,Water Barrier
-2119,8433,1,4	//EL_TIDAL_WEAPON,Tidal Weapon
-// EL_VENTUS_S
-2120,8419,1,1	//EL_GUST,Gust
-2120,8407,1,2	//EL_WIND_STEP,Wind Step
-2120,8434,1,4	//EL_WIND_SLASH,Wind Slasher
-// EL_VENTUS_M
-2121,8420,1,1	//EL_BLAST,Blast
-2121,8408,1,2	//EL_WIND_CURTAIN,Wind Curtain
-2121,8435,1,4	//EL_HURRICANE,Hurricane Rage
-// EL_VENTUS_L
-2122,8421,1,1	//EL_WILD_STORM,Wild Storm
-2122,8409,1,2	//EL_ZEPHYR,Zephyr
-2122,8437,1,4	//EL_TYPOON_MIS,Typhoon Missile
-// EL_TERA_S
-2123,8422,1,1	//EL_PETROLOGY,Petrology
-2123,8410,1,2	//EL_SOLID_SKIN,Solid Skin
-2123,8439,1,4	//EL_STONE_HAMMER,Stone Hammer
-// EL_TERA_M
-2124,8423,1,1	//EL_CURSED_SOIL,Cursed Soil
-2124,8411,1,2	//EL_STONE_SHIELD,Stone Shield
-2124,8440,1,4	//EL_ROCK_CRUSHER,Rock Launcher
-// EL_TERA_L
-2125,8424,1,1	//EL_UPHEAVAL,Upheaval
-2125,8412,1,2	//EL_POWER_OF_GAIA,Power of Gaia
-2125,8442,1,4	//EL_STONE_RAIN,Stone Rain

+ 0 - 9
db/import-tmpl/elemental_db.txt

@@ -1,9 +0,0 @@
-// Elemental Summons Database
-//
-// Structure of Database:
-// ID,Sprite_Name,Name,LV,HP,SP,Range1,ATK1,ATK2,DEF,MDEF,STR,AGI,VIT,INT,DEX,LUK,Range2,Range3,Scale,Race,Element,Speed,aDelay,aMotion,dMotion
-//
-// Notes:
-// Summoned Elemental’s STATs are affected by the Caster’s Base Level and STATs.
-// In other words, all values specified will be added to (and will not override) the calculated STATs of the summoned elemental.
-

+ 43 - 0
db/import-tmpl/elemental_db.yml

@@ -0,0 +1,43 @@
+###########################################################################
+# Elemental Summons Database
+###########################################################################
+#
+# Elemental Summons Settings
+#
+###########################################################################
+# - Id                      Elemental ID.
+#   AegisName               Server name to reference the elemental in scripts and lookups, should use no spaces.
+#   Name                    Name in English.
+#   Level                   Level.
+#   Hp                      Additional HP. Base HP depends on caster HP. (Default: 0)
+#   Sp                      Additional SP. Base SP depends on caster SP. (Default: 1)
+#   Attack                  Additional minimum attack. Base attack depends on caster attack. (Default: 0)
+#   Attack2                 Additional maximum attack. Base attack depend on caster attack. (Default: 0)
+#   Defense                 Physical defense of the elemental, reduces melee and ranged physical attack/skill damage. Base defense depends on the caster defense. (Default: 0)
+#   MagicDefense            Magic defense of the elemental, reduces magical skill damage. Base magic defense depends on the caster magic defense. (Default: 0)
+#   Str                     Strength which affects attack. (Default: 0)
+#   Agi                     Agility which affects flee. (Default: 0)
+#   Vit                     Vitality which affects defense. (Default: 0)
+#   Int                     Intelligence which affects magic attack. (Default: 0)
+#   Dex                     Dexterity which affects hit rate. (Default: 0)
+#   Luk                     Luck which affects perfect dodge/lucky flee/perfect flee/lucky dodge rate. (Default: 0)
+#   AttackRange             Attack range. (Default: 1)
+#   SkillRange              Skill cast range. (Default: 5)
+#   ChaseRange              Chase range. (Default: 12)
+#   Size                    Size.
+#   Race                    Race. (Default: Formless)
+#   Element                 Element.
+#   ElementLevel            Level of element.
+#   WalkSpeed               Walk speed. (Default: 200)
+#   AttackDelay             Attack speed. (Default: 504)
+#   AttackMotion            Attack animation speed. Base AttackMotion depends on the caster AttackMotion. (Default: 1020)
+#   DamageMotion            Damage animation speed. (Default: 360)
+#   Mode:                   List of elemental skills by mode. (Optional)
+#     <mode>:               Mode name to define the skill available. Available modes are Passive, Assist, and Aggressive.
+#       Skill               Skill name.
+#       Level               Skill level. (Default: 1)
+###########################################################################
+
+Header:
+  Type: ELEMENTAL_DB
+  Version: 1

+ 0 - 7
db/import-tmpl/elemental_skill_db.txt

@@ -1,7 +0,0 @@
-// Elemental Summons Skill Database
-//
-// Structure of Database:
-// ElementalID,SkillID,SkillLevel,ReqMode
-//
-// Spirit Modes:
-//	1 = Passive, 2 = Defensive, 4 = Aggressive

+ 39 - 0
doc/yaml/db/elemental_db.yml

@@ -0,0 +1,39 @@
+###########################################################################
+# Elemental Summons Database
+###########################################################################
+#
+# Elemental Summons Settings
+#
+###########################################################################
+# - Id                      Elemental ID.
+#   AegisName               Server name to reference the elemental in scripts and lookups, should use no spaces.
+#   Name                    Name in English.
+#   Level                   Level.
+#   Hp                      Additional HP. Base HP depends on caster HP. (Default: 0)
+#   Sp                      Additional SP. Base SP depends on caster SP. (Default: 1)
+#   Attack                  Additional minimum attack. Base attack depends on caster attack. (Default: 0)
+#   Attack2                 Additional maximum attack. Base attack depend on caster attack. (Default: 0)
+#   Defense                 Physical defense of the elemental, reduces melee and ranged physical attack/skill damage. Base defense depends on the caster defense. (Default: 0)
+#   MagicDefense            Magic defense of the elemental, reduces magical skill damage. Base magic defense depends on the caster magic defense. (Default: 0)
+#   Str                     Strength which affects attack. (Default: 0)
+#   Agi                     Agility which affects flee. (Default: 0)
+#   Vit                     Vitality which affects defense. (Default: 0)
+#   Int                     Intelligence which affects magic attack. (Default: 0)
+#   Dex                     Dexterity which affects hit rate. (Default: 0)
+#   Luk                     Luck which affects perfect dodge/lucky flee/perfect flee/lucky dodge rate. (Default: 0)
+#   AttackRange             Attack range. (Default: 1)
+#   SkillRange              Skill cast range. (Default: 5)
+#   ChaseRange              Chase range. (Default: 12)
+#   Size                    Size.
+#   Race                    Race. (Default: Formless)
+#   Element                 Element.
+#   ElementLevel            Level of element.
+#   WalkSpeed               Walk speed. (Default: 200)
+#   AttackDelay             Attack speed. (Default: 504)
+#   AttackMotion            Attack animation speed. Base AttackMotion depends on the caster AttackMotion. (Default: 1020)
+#   DamageMotion            Damage animation speed. (Default: 360)
+#   Mode:                   List of elemental skills by mode. (Optional)
+#     <mode>:               Mode name to define the skill available. Available modes are Passive, Assist, and Aggressive.
+#       Skill               Skill name.
+#       Level               Skill level. (Default: 1)
+###########################################################################

+ 1 - 2
src/map/atcommand.cpp

@@ -4038,12 +4038,11 @@ ACMD_FUNC(reload) {
 		hom_reload();
 		hom_reload();
 		mercenary_readdb();
 		mercenary_readdb();
 		mercenary_read_skilldb();
 		mercenary_read_skilldb();
-		reload_elementaldb();
+		elemental_db.reload();
 		clif_displaymessage(fd, msg_txt(sd,98)); // Monster database has been reloaded.
 		clif_displaymessage(fd, msg_txt(sd,98)); // Monster database has been reloaded.
 	} else if (strstr(command, "skilldb") || strncmp(message, "skilldb", 4) == 0) {
 	} else if (strstr(command, "skilldb") || strncmp(message, "skilldb", 4) == 0) {
 		skill_reload();
 		skill_reload();
 		hom_reload_skill();
 		hom_reload_skill();
-		reload_elemental_skilldb();
 		mercenary_read_skilldb();
 		mercenary_read_skilldb();
 		clif_displaymessage(fd, msg_txt(sd,99)); // Skill database has been reloaded.
 		clif_displaymessage(fd, msg_txt(sd,99)); // Skill database has been reloaded.
 	} else if (strstr(command, "atcommand") || strncmp(message, "atcommand", 4) == 0) {
 	} else if (strstr(command, "atcommand") || strncmp(message, "atcommand", 4) == 0) {

+ 2 - 2
src/map/battle.cpp

@@ -125,7 +125,7 @@ int battle_gettarget(struct block_list* bl)
 		case BL_PET: return ((struct pet_data*)bl)->target_id;
 		case BL_PET: return ((struct pet_data*)bl)->target_id;
 		case BL_HOM: return ((struct homun_data*)bl)->ud.target;
 		case BL_HOM: return ((struct homun_data*)bl)->ud.target;
 		case BL_MER: return ((struct mercenary_data*)bl)->ud.target;
 		case BL_MER: return ((struct mercenary_data*)bl)->ud.target;
-		case BL_ELEM: return ((struct elemental_data*)bl)->ud.target;
+		case BL_ELEM: return ((s_elemental_data*)bl)->ud.target;
 	}
 	}
 
 
 	return 0;
 	return 0;
@@ -8008,7 +8008,7 @@ enum damage_lv battle_weapon_attack(struct block_list* src, struct block_list* t
 				status_change_end(target, SC_DEVOTION, INVALID_TIMER);
 				status_change_end(target, SC_DEVOTION, INVALID_TIMER);
 		}
 		}
 		if (target->type == BL_PC && (wd.flag&BF_SHORT) && tsc->data[SC_CIRCLE_OF_FIRE_OPTION]) {
 		if (target->type == BL_PC && (wd.flag&BF_SHORT) && tsc->data[SC_CIRCLE_OF_FIRE_OPTION]) {
-			struct elemental_data *ed = ((TBL_PC*)target)->ed;
+			s_elemental_data *ed = ((TBL_PC*)target)->ed;
 
 
 			if (ed) {
 			if (ed) {
 				clif_skill_damage(&ed->bl, target, tick, status_get_amotion(src), 0, -30000, 1, EL_CIRCLE_OF_FIRE, tsc->data[SC_CIRCLE_OF_FIRE_OPTION]->val1, DMG_SINGLE);
 				clif_skill_damage(&ed->bl, target, tick, status_get_amotion(src), 0, -30000, 1, EL_CIRCLE_OF_FIRE, tsc->data[SC_CIRCLE_OF_FIRE_OPTION]->val1, DMG_SINGLE);

+ 3 - 3
src/map/clif.cpp

@@ -9845,7 +9845,7 @@ void clif_name( struct block_list* src, struct block_list *bl, send_target targe
 					safestrncpy(packet.name, ((TBL_NPC *)bl)->name, NAME_LENGTH);
 					safestrncpy(packet.name, ((TBL_NPC *)bl)->name, NAME_LENGTH);
 					break;
 					break;
 				case BL_ELEM:
 				case BL_ELEM:
-					safestrncpy(packet.name, ((TBL_ELEM *)bl)->db->name, NAME_LENGTH);
+					safestrncpy(packet.name, ((TBL_ELEM *)bl)->db->name.c_str(), NAME_LENGTH);
 					break;
 					break;
 			}
 			}
 
 
@@ -18552,7 +18552,7 @@ void clif_parse_ItemListWindowSelected(int fd, struct map_session_data* sd) {
  * Elemental System
  * Elemental System
  *==========================================*/
  *==========================================*/
 void clif_elemental_updatestatus(struct map_session_data *sd, int type) {
 void clif_elemental_updatestatus(struct map_session_data *sd, int type) {
-	struct elemental_data *ed;
+	s_elemental_data *ed;
 	struct status_data *status;
 	struct status_data *status;
 	int fd;
 	int fd;
 
 
@@ -18583,7 +18583,7 @@ void clif_elemental_updatestatus(struct map_session_data *sd, int type) {
 
 
 void clif_elemental_info(struct map_session_data *sd) {
 void clif_elemental_info(struct map_session_data *sd) {
 	int fd;
 	int fd;
-	struct elemental_data *ed;
+	s_elemental_data *ed;
 	struct status_data *status;
 	struct status_data *status;
 
 
 	if( !clif_session_isValid(sd) || (ed = sd->ed) == NULL )
 	if( !clif_session_isValid(sd) || (ed = sd->ed) == NULL )

+ 539 - 227
src/map/elemental.cpp

@@ -28,44 +28,33 @@
 #include "pc.hpp"
 #include "pc.hpp"
 #include "trade.hpp"
 #include "trade.hpp"
 
 
-struct s_elemental_db elemental_db[MAX_ELEMENTAL_CLASS]; // Elemental Database
-static uint16 elemental_count;
+using namespace rathena;
 
 
-int elemental_search_index(int class_) {
-	int i;
-	ARR_FIND(0, elemental_count, i, elemental_db[i].class_ == class_);
-	return (i == elemental_count)?-1:i;
-}
-
-bool elemental_class(int class_) {
-	return (bool)(elemental_search_index(class_) > -1);
-}
+ElementalDatabase elemental_db;
 
 
 struct view_data * elemental_get_viewdata(int class_) {
 struct view_data * elemental_get_viewdata(int class_) {
-	int i = elemental_search_index(class_);
-	if( i < 0 )
+	std::shared_ptr<s_elemental_db> db = elemental_db.find(class_);
+	if (db == nullptr)
 		return 0;
 		return 0;
 
 
-	return &elemental_db[i].vd;
+	return &db->vd;
 }
 }
 
 
-int elemental_create(struct map_session_data *sd, int class_, unsigned int lifetime) {
-	struct s_elemental ele;
-	struct s_elemental_db *db;
-	int i;
-
+int elemental_create(map_session_data *sd, int class_, unsigned int lifetime) {
 	nullpo_retr(1,sd);
 	nullpo_retr(1,sd);
 
 
-	if( (i = elemental_search_index(class_)) < 0 )
+	std::shared_ptr<s_elemental_db> db = elemental_db.find(class_);
+	if (db == nullptr) {
+		ShowError("elemental_create: Unknown elemental class %d.\n", class_);
 		return 0;
 		return 0;
+	}
 
 
-	db = &elemental_db[i];
-	memset(&ele,0,sizeof(struct s_elemental));
+	s_elemental ele = {};
 
 
 	ele.char_id = sd->status.char_id;
 	ele.char_id = sd->status.char_id;
 	ele.class_ = class_;
 	ele.class_ = class_;
 	ele.mode = EL_MODE_PASSIVE; // Initial mode
 	ele.mode = EL_MODE_PASSIVE; // Initial mode
-	i = db->status.size+1; // summon level
+	int i = db->status.size+1; // summon level
 
 
 	//[(Caster's Max HP/ 3 ) + (Caster's INT x 10 )+ (Caster's Job Level x 20 )] x [(Elemental Summon Level + 2) / 3]
 	//[(Caster's Max HP/ 3 ) + (Caster's INT x 10 )+ (Caster's Job Level x 20 )] x [(Elemental Summon Level + 2) / 3]
 	ele.hp = ele.max_hp = (sd->battle_status.max_hp/3 + sd->battle_status.int_*10 + sd->status.job_level*20) * ((i + 2) / 3);
 	ele.hp = ele.max_hp = (sd->battle_status.max_hp/3 + sd->battle_status.int_*10 + sd->status.job_level*20) * ((i + 2) / 3);
@@ -130,16 +119,15 @@ int elemental_create(struct map_session_data *sd, int class_, unsigned int lifet
 	return 1;
 	return 1;
 }
 }
 
 
-t_tick elemental_get_lifetime(struct elemental_data *ed) {
-	const struct TimerData * td;
+t_tick elemental_get_lifetime(s_elemental_data *ed) {
 	if( ed == NULL || ed->summon_timer == INVALID_TIMER )
 	if( ed == NULL || ed->summon_timer == INVALID_TIMER )
 		return 0;
 		return 0;
 
 
-	td = get_timer(ed->summon_timer);
+	const struct TimerData * td = get_timer(ed->summon_timer);
 	return (td != NULL) ? DIFF_TICK(td->tick, gettick()) : 0;
 	return (td != NULL) ? DIFF_TICK(td->tick, gettick()) : 0;
 }
 }
 
 
-int elemental_save(struct elemental_data *ed) {
+int elemental_save(s_elemental_data *ed) {
 	ed->elemental.mode = ed->battle_status.mode;
 	ed->elemental.mode = ed->battle_status.mode;
 	ed->elemental.hp = ed->battle_status.hp;
 	ed->elemental.hp = ed->battle_status.hp;
 	ed->elemental.sp = ed->battle_status.sp;
 	ed->elemental.sp = ed->battle_status.sp;
@@ -158,11 +146,13 @@ int elemental_save(struct elemental_data *ed) {
 }
 }
 
 
 static TIMER_FUNC(elemental_summon_end){
 static TIMER_FUNC(elemental_summon_end){
-	struct map_session_data *sd;
-	struct elemental_data *ed;
+	map_session_data *sd;
 
 
 	if( (sd = map_id2sd(id)) == NULL )
 	if( (sd = map_id2sd(id)) == NULL )
 		return 1;
 		return 1;
+
+	s_elemental_data *ed;
+
 	if( (ed = sd->ed) == NULL )
 	if( (ed = sd->ed) == NULL )
 		return 1;
 		return 1;
 
 
@@ -177,19 +167,17 @@ static TIMER_FUNC(elemental_summon_end){
 	return 0;
 	return 0;
 }
 }
 
 
-void elemental_summon_stop(struct elemental_data *ed) {
+void elemental_summon_stop(s_elemental_data *ed) {
 	nullpo_retv(ed);
 	nullpo_retv(ed);
 	if( ed->summon_timer != INVALID_TIMER )
 	if( ed->summon_timer != INVALID_TIMER )
 		delete_timer(ed->summon_timer, elemental_summon_end);
 		delete_timer(ed->summon_timer, elemental_summon_end);
 	ed->summon_timer = INVALID_TIMER;
 	ed->summon_timer = INVALID_TIMER;
 }
 }
 
 
-int elemental_delete(struct elemental_data *ed) {
-	struct map_session_data *sd;
-
+int elemental_delete(s_elemental_data *ed) {
 	nullpo_ret(ed);
 	nullpo_ret(ed);
 
 
-	sd = ed->master;
+	map_session_data *sd = ed->master;
 	ed->elemental.life_time = 0;
 	ed->elemental.life_time = 0;
 
 
 	elemental_clean_effect(ed);
 	elemental_clean_effect(ed);
@@ -204,7 +192,7 @@ int elemental_delete(struct elemental_data *ed) {
 	return unit_remove_map(&ed->bl, CLR_OUTSIGHT);
 	return unit_remove_map(&ed->bl, CLR_OUTSIGHT);
 }
 }
 
 
-void elemental_summon_init(struct elemental_data *ed) {
+void elemental_summon_init(s_elemental_data *ed) {
 	if( ed->summon_timer == INVALID_TIMER )
 	if( ed->summon_timer == INVALID_TIMER )
 		ed->summon_timer = add_timer(gettick() + ed->elemental.life_time, elemental_summon_end, ed->master->bl.id, 0);
 		ed->summon_timer = add_timer(gettick() + ed->elemental.life_time, elemental_summon_end, ed->master->bl.id, 0);
 
 
@@ -217,28 +205,28 @@ void elemental_summon_init(struct elemental_data *ed) {
  * @param flag : 0:not created, 1:was saved/loaded
  * @param flag : 0:not created, 1:was saved/loaded
  * @return 0:failed, 1:sucess
  * @return 0:failed, 1:sucess
  */
  */
-int elemental_data_received(struct s_elemental *ele, bool flag) {
-	struct map_session_data *sd;
-	struct elemental_data *ed;
-	struct s_elemental_db *db;
-	int i = elemental_search_index(ele->class_);
+int elemental_data_received(s_elemental *ele, bool flag) {
+	map_session_data *sd;
 
 
 	if( (sd = map_charid2sd(ele->char_id)) == NULL )
 	if( (sd = map_charid2sd(ele->char_id)) == NULL )
 		return 0;
 		return 0;
 
 
-	if( !flag || i < 0 ) { // Not created - loaded - DB info
+	std::shared_ptr<s_elemental_db> db = elemental_db.find(ele->class_);
+
+	if( !flag || db == nullptr ) { // Not created - loaded - DB info
 		sd->status.ele_id = 0;
 		sd->status.ele_id = 0;
 		return 0;
 		return 0;
 	}
 	}
 
 
-	db = &elemental_db[i];
+	s_elemental_data *ed;
+
 	if( !sd->ed ) {	// Initialize it after first summon.
 	if( !sd->ed ) {	// Initialize it after first summon.
-		sd->ed = ed = (struct elemental_data*)aCalloc(1,sizeof(struct elemental_data));
+		sd->ed = ed = (s_elemental_data*)aCalloc(1,sizeof(s_elemental_data));
 		ed->bl.type = BL_ELEM;
 		ed->bl.type = BL_ELEM;
 		ed->bl.id = npc_get_new_npc_id();
 		ed->bl.id = npc_get_new_npc_id();
 		ed->master = sd;
 		ed->master = sd;
 		ed->db = db;
 		ed->db = db;
-		memcpy(&ed->elemental, ele, sizeof(struct s_elemental));
+		memcpy(&ed->elemental, ele, sizeof(s_elemental));
 		status_set_viewdata(&ed->bl, ed->elemental.class_);
 		status_set_viewdata(&ed->bl, ed->elemental.class_);
 		ed->vd->head_mid = 10; // Why?
 		ed->vd->head_mid = 10; // Why?
 		status_change_init(&ed->bl);
 		status_change_init(&ed->bl);
@@ -259,7 +247,7 @@ int elemental_data_received(struct s_elemental *ele, bool flag) {
 		ed->masterteleport_timer = INVALID_TIMER;
 		ed->masterteleport_timer = INVALID_TIMER;
 		elemental_summon_init(ed);
 		elemental_summon_init(ed);
 	} else {
 	} else {
-		memcpy(&sd->ed->elemental, ele, sizeof(struct s_elemental));
+		memcpy(&sd->ed->elemental, ele, sizeof(s_elemental));
 		ed = sd->ed;
 		ed = sd->ed;
 	}
 	}
 
 
@@ -278,13 +266,11 @@ int elemental_data_received(struct s_elemental *ele, bool flag) {
 	return 1;
 	return 1;
 }
 }
 
 
-int elemental_clean_single_effect(struct elemental_data *ed, uint16 skill_id) {
-	struct block_list *bl;
-	sc_type type = status_skill2sc(skill_id);
-
+int elemental_clean_single_effect(s_elemental_data *ed, uint16 skill_id) {
 	nullpo_ret(ed);
 	nullpo_ret(ed);
 
 
-	bl = battle_get_master(&ed->bl);
+	sc_type type = status_skill2sc(skill_id);
+	block_list *bl = battle_get_master(&ed->bl);
 
 
 	if( type ) {
 	if( type ) {
 		switch( type ) {
 		switch( type ) {
@@ -325,11 +311,11 @@ int elemental_clean_single_effect(struct elemental_data *ed, uint16 skill_id) {
 	return 1;
 	return 1;
 }
 }
 
 
-int elemental_clean_effect(struct elemental_data *ed) {
-	struct map_session_data *sd;
-
+int elemental_clean_effect(s_elemental_data *ed) {
 	nullpo_ret(ed);
 	nullpo_ret(ed);
 
 
+	map_session_data *sd;
+
 	// Elemental side
 	// Elemental side
 	status_change_end(&ed->bl, SC_TROPIC, INVALID_TIMER);
 	status_change_end(&ed->bl, SC_TROPIC, INVALID_TIMER);
 	status_change_end(&ed->bl, SC_HEATER, INVALID_TIMER);
 	status_change_end(&ed->bl, SC_HEATER, INVALID_TIMER);
@@ -385,11 +371,7 @@ int elemental_clean_effect(struct elemental_data *ed) {
 	return 1;
 	return 1;
 }
 }
 
 
-int elemental_action(struct elemental_data *ed, struct block_list *bl, t_tick tick) {
-	struct s_skill_condition req;
-	uint16 skill_id, skill_lv;
-	int i;
-
+int elemental_action(s_elemental_data *ed, block_list *bl, t_tick tick) {
 	nullpo_ret(ed);
 	nullpo_ret(ed);
 	nullpo_ret(bl);
 	nullpo_ret(bl);
 
 
@@ -399,12 +381,12 @@ int elemental_action(struct elemental_data *ed, struct block_list *bl, t_tick ti
 	if( ed->target_id )
 	if( ed->target_id )
 		elemental_unlocktarget(ed);	// Remove previous target.
 		elemental_unlocktarget(ed);	// Remove previous target.
 
 
-	ARR_FIND(0, MAX_ELESKILLTREE, i, ed->db->skill[i].id && (ed->db->skill[i].mode&EL_SKILLMODE_AGGRESSIVE));
-	if( i == MAX_ELESKILLTREE )
+	std::shared_ptr<s_elemental_skill> skill = util::umap_find(ed->db->skill, EL_SKILLMODE_AGGRESSIVE);	// only one skill per mode is supported
+	if (skill == nullptr)
 		return 0;
 		return 0;
 
 
-	skill_id = ed->db->skill[i].id;
-	skill_lv = ed->db->skill[i].lv;
+	uint16 skill_id = skill->id;
+	uint16 skill_lv = skill->lv;
 
 
 	if( elemental_skillnotok(skill_id, ed) )
 	if( elemental_skillnotok(skill_id, ed) )
 		return 0;
 		return 0;
@@ -434,13 +416,12 @@ int elemental_action(struct elemental_data *ed, struct block_list *bl, t_tick ti
 				ed->ud.skilltimer = add_timer( tick+(t_tick)status_get_speed(&ed->bl)*walk_dist, skill_castend_id, ed->bl.id, 0 );
 				ed->ud.skilltimer = add_timer( tick+(t_tick)status_get_speed(&ed->bl)*walk_dist, skill_castend_id, ed->bl.id, 0 );
 		}
 		}
 		return 1;
 		return 1;
-
 	}
 	}
 
 
-	req = elemental_skill_get_requirements(skill_id, skill_lv);
+	s_skill_condition req = elemental_skill_get_requirements(skill_id, skill_lv);
 
 
 	if(req.hp || req.sp){
 	if(req.hp || req.sp){
-		struct map_session_data *sd = BL_CAST(BL_PC, battle_get_master(&ed->bl));
+		map_session_data *sd = BL_CAST(BL_PC, battle_get_master(&ed->bl));
 		if( sd ){
 		if( sd ){
 			if( sd->skill_id_old != SO_EL_ACTION && //regardless of remaining HP/SP it can be cast
 			if( sd->skill_id_old != SO_EL_ACTION && //regardless of remaining HP/SP it can be cast
 				(status_get_hp(&ed->bl) < req.hp || status_get_sp(&ed->bl) < req.sp) )
 				(status_get_hp(&ed->bl) < req.hp || status_get_sp(&ed->bl) < req.sp) )
@@ -466,23 +447,19 @@ int elemental_action(struct elemental_data *ed, struct block_list *bl, t_tick ti
  * Action that elemental perform after changing mode.
  * Action that elemental perform after changing mode.
  * Activates one of the skills of the new mode.
  * Activates one of the skills of the new mode.
  *-------------------------------------------------------------*/
  *-------------------------------------------------------------*/
-int elemental_change_mode_ack(struct elemental_data *ed, enum elemental_skillmode skill_mode) {
-	struct block_list *bl = &ed->master->bl;
-	uint16 skill_id, skill_lv;
-	int i;
-
+int elemental_change_mode_ack(s_elemental_data *ed, e_elemental_skillmode skill_mode) {
 	nullpo_ret(ed);
 	nullpo_ret(ed);
 
 
+	block_list *bl = &ed->master->bl;
 	if( !bl )
 	if( !bl )
 		return 0;
 		return 0;
 
 
-	// Select a skill.
-	ARR_FIND(0, MAX_ELESKILLTREE, i, ed->db->skill[i].id && (ed->db->skill[i].mode&skill_mode));
-	if( i == MAX_ELESKILLTREE )
+	std::shared_ptr<s_elemental_skill> skill = util::umap_find(ed->db->skill, skill_mode);
+	if (skill == nullptr)
 		return 0;
 		return 0;
 
 
-	skill_id = ed->db->skill[i].id;
-	skill_lv = ed->db->skill[i].lv;
+	uint16 skill_id = skill->id;
+	uint16 skill_lv = skill->lv;
 
 
 	if( elemental_skillnotok(skill_id, ed) )
 	if( elemental_skillnotok(skill_id, ed) )
 		return 0;
 		return 0;
@@ -508,9 +485,7 @@ int elemental_change_mode_ack(struct elemental_data *ed, enum elemental_skillmod
 /*===============================================================
 /*===============================================================
  * Change elemental mode.
  * Change elemental mode.
  *-------------------------------------------------------------*/
  *-------------------------------------------------------------*/
-int elemental_change_mode(struct elemental_data *ed, enum e_mode mode) {
-	enum elemental_skillmode skill_mode;
-
+int elemental_change_mode(s_elemental_data *ed, e_mode mode) {
 	nullpo_ret(ed);
 	nullpo_ret(ed);
 
 
 	// Remove target
 	// Remove target
@@ -520,6 +495,7 @@ int elemental_change_mode(struct elemental_data *ed, enum e_mode mode) {
 	if(ed->elemental.mode != mode ) elemental_clean_effect(ed);
 	if(ed->elemental.mode != mode ) elemental_clean_effect(ed);
 
 
 	ed->battle_status.mode = ed->elemental.mode = mode;
 	ed->battle_status.mode = ed->elemental.mode = mode;
+	e_elemental_skillmode skill_mode;
 
 
 	// Normalize elemental mode to elemental skill mode.
 	// Normalize elemental mode to elemental skill mode.
 	if( mode == EL_MODE_AGGRESSIVE ) skill_mode = EL_SKILLMODE_AGGRESSIVE;	// Aggressive spirit mode -> Aggressive spirit skill.
 	if( mode == EL_MODE_AGGRESSIVE ) skill_mode = EL_SKILLMODE_AGGRESSIVE;	// Aggressive spirit mode -> Aggressive spirit skill.
@@ -533,7 +509,7 @@ int elemental_change_mode(struct elemental_data *ed, enum e_mode mode) {
 	return 1;
 	return 1;
 }
 }
 
 
-void elemental_heal(struct elemental_data *ed, int hp, int sp) {
+void elemental_heal(s_elemental_data *ed, int hp, int sp) {
 	if (ed->master == NULL)
 	if (ed->master == NULL)
 		return;
 		return;
 	if( hp )
 	if( hp )
@@ -542,12 +518,12 @@ void elemental_heal(struct elemental_data *ed, int hp, int sp) {
 		clif_elemental_updatestatus(ed->master, SP_SP);
 		clif_elemental_updatestatus(ed->master, SP_SP);
 }
 }
 
 
-int elemental_dead(struct elemental_data *ed) {
+int elemental_dead(s_elemental_data *ed) {
 	elemental_delete(ed);
 	elemental_delete(ed);
 	return 0;
 	return 0;
 }
 }
 
 
-int elemental_unlocktarget(struct elemental_data *ed) {
+int elemental_unlocktarget(s_elemental_data *ed) {
 	nullpo_ret(ed);
 	nullpo_ret(ed);
 
 
 	ed->target_id = 0;
 	ed->target_id = 0;
@@ -556,14 +532,14 @@ int elemental_unlocktarget(struct elemental_data *ed) {
 	return 0;
 	return 0;
 }
 }
 
 
-bool elemental_skillnotok(uint16 skill_id, struct elemental_data *ed) {
+bool elemental_skillnotok(uint16 skill_id, s_elemental_data *ed) {
 	uint16 idx = skill_get_index(skill_id);
 	uint16 idx = skill_get_index(skill_id);
 	nullpo_retr(1,ed);
 	nullpo_retr(1,ed);
 	return idx == 0 ? false : skill_isNotOk(skill_id,ed->master); // return false or check if it,s ok for master as well
 	return idx == 0 ? false : skill_isNotOk(skill_id,ed->master); // return false or check if it,s ok for master as well
 }
 }
 
 
 struct s_skill_condition elemental_skill_get_requirements(uint16 skill_id, uint16 skill_lv){
 struct s_skill_condition elemental_skill_get_requirements(uint16 skill_id, uint16 skill_lv){
-	struct s_skill_condition req = {};
+	s_skill_condition req = {};
 	std::shared_ptr<s_skill_db> skill = skill_db.find(skill_id);
 	std::shared_ptr<s_skill_db> skill = skill_db.find(skill_id);
 
 
 	if( !skill ) // invalid skill id
 	if( !skill ) // invalid skill id
@@ -576,8 +552,8 @@ struct s_skill_condition elemental_skill_get_requirements(uint16 skill_id, uint1
 	return req;
 	return req;
 }
 }
 
 
-int elemental_set_target( struct map_session_data *sd, struct block_list *bl ) {
-	struct elemental_data *ed = sd->ed;
+int elemental_set_target( map_session_data *sd, block_list *bl ) {
+	s_elemental_data *ed = sd->ed;
 
 
 	nullpo_ret(ed);
 	nullpo_ret(ed);
 	nullpo_ret(bl);
 	nullpo_ret(bl);
@@ -594,15 +570,14 @@ int elemental_set_target( struct map_session_data *sd, struct block_list *bl ) {
 	return 1;
 	return 1;
 }
 }
 
 
-static int elemental_ai_sub_timer_activesearch(struct block_list *bl, va_list ap) {
-	struct elemental_data *ed;
-	struct block_list **target;
-	int dist;
-
+static int elemental_ai_sub_timer_activesearch(block_list *bl, va_list ap) {
 	nullpo_ret(bl);
 	nullpo_ret(bl);
 
 
-	ed = va_arg(ap,struct elemental_data *);
-	target = va_arg(ap,struct block_list**);
+	s_elemental_data *ed;
+	block_list **target;
+
+	ed = va_arg(ap, s_elemental_data *);
+	target = va_arg(ap, block_list**);
 
 
 	//If can't seek yet, not an enemy, or you can't attack it, skip.
 	//If can't seek yet, not an enemy, or you can't attack it, skip.
 	if( (*target) == bl || !status_check_skilluse(&ed->bl, bl, 0, 0) )
 	if( (*target) == bl || !status_check_skilluse(&ed->bl, bl, 0, 0) )
@@ -611,6 +586,8 @@ static int elemental_ai_sub_timer_activesearch(struct block_list *bl, va_list ap
 	if( battle_check_target(&ed->bl,bl,BCT_ENEMY) <= 0 )
 	if( battle_check_target(&ed->bl,bl,BCT_ENEMY) <= 0 )
 		return 0;
 		return 0;
 
 
+	int dist;
+
 	switch( bl->type ) {
 	switch( bl->type ) {
 		case BL_PC:
 		case BL_PC:
 			if( !map_flag_vs(ed->bl.m) )
 			if( !map_flag_vs(ed->bl.m) )
@@ -630,11 +607,7 @@ static int elemental_ai_sub_timer_activesearch(struct block_list *bl, va_list ap
 	return 0;
 	return 0;
 }
 }
 
 
-static int elemental_ai_sub_timer(struct elemental_data *ed, struct map_session_data *sd, t_tick tick) {
-	struct block_list *target = NULL;
-	int master_dist, view_range;
-	enum e_mode mode;
-
+static int elemental_ai_sub_timer(s_elemental_data *ed, map_session_data *sd, t_tick tick) {
 	nullpo_ret(ed);
 	nullpo_ret(ed);
 	nullpo_ret(sd);
 	nullpo_ret(sd);
 
 
@@ -679,12 +652,14 @@ static int elemental_ai_sub_timer(struct elemental_data *ed, struct map_session_
 	if(ed->ud.walkpath.path_pos < ed->ud.walkpath.path_len && ed->ud.target == sd->bl.id)
 	if(ed->ud.walkpath.path_pos < ed->ud.walkpath.path_len && ed->ud.target == sd->bl.id)
 		return 0; //No thinking until be near the master.
 		return 0; //No thinking until be near the master.
 
 
+	int master_dist, view_range;
+
 	if( ed->sc.count && ed->sc.data[SC_BLIND] )
 	if( ed->sc.count && ed->sc.data[SC_BLIND] )
 		view_range = 3;
 		view_range = 3;
 	else
 	else
 		view_range = ed->db->range2;
 		view_range = ed->db->range2;
 
 
-	mode = status_get_mode(&ed->bl);
+	e_mode mode = status_get_mode(&ed->bl);
 
 
 	master_dist = distance_bl(&sd->bl, &ed->bl);
 	master_dist = distance_bl(&sd->bl, &ed->bl);
 	if( master_dist > AREA_SIZE ) {	// Master out of vision range.
 	if( master_dist > AREA_SIZE ) {	// Master out of vision range.
@@ -706,6 +681,8 @@ static int elemental_ai_sub_timer(struct elemental_data *ed, struct map_session_
 			return 0;
 			return 0;
 	}
 	}
 
 
+	block_list *target = NULL;
+
 	if( mode == EL_MODE_AGGRESSIVE ) {
 	if( mode == EL_MODE_AGGRESSIVE ) {
 		target = map_id2bl(ed->ud.target);
 		target = map_id2bl(ed->ud.target);
 
 
@@ -740,7 +717,7 @@ static int elemental_ai_sub_timer(struct elemental_data *ed, struct map_session_
 	return 0;
 	return 0;
 }
 }
 
 
-static int elemental_ai_sub_foreachclient(struct map_session_data *sd, va_list ap) {
+static int elemental_ai_sub_foreachclient(map_session_data *sd, va_list ap) {
 	t_tick tick = va_arg(ap,t_tick);
 	t_tick tick = va_arg(ap,t_tick);
 	if(sd->status.ele_id && sd->ed)
 	if(sd->status.ele_id && sd->ed)
 		elemental_ai_sub_timer(sd->ed,sd,tick);
 		elemental_ai_sub_timer(sd->ed,sd,tick);
@@ -753,152 +730,487 @@ static TIMER_FUNC(elemental_ai_timer){
 	return 0;
 	return 0;
 }
 }
 
 
-/**
-* Reads Elemental DB lines
-* ID,Sprite_Name,Name,LV,HP,SP,Range1,ATK1,ATK2,DEF,MDEF,STR,AGI,VIT,INT,DEX,LUK,Range2,Range3,Scale,Race,Element,Speed,aDelay,aMotion,dMotion
-*/
-static bool read_elementaldb_sub(char* str[], int columns, int current) {
-	uint16 class_ = atoi(str[0]), i, ele;
-	struct s_elemental_db *db;
-	struct status_data *status;
-
-	//Find the ID, already exist or not in elemental_db
-	ARR_FIND(0,elemental_count,i,elemental_db[i].class_ == class_);
-	if (i >= elemental_count)
-		db = &elemental_db[elemental_count];
-	else
-		db = &elemental_db[i];
-	
-	db->class_ = atoi(str[0]);
-	safestrncpy(db->sprite, str[1], NAME_LENGTH);
-	safestrncpy(db->name, str[2], NAME_LENGTH);
-	db->lv = atoi(str[3]);
-
-	status = &db->status;
-	db->vd.class_ = db->class_;
-
-	status->max_hp = atoi(str[4]);
-	status->max_sp = atoi(str[5]);
-	status->rhw.range = atoi(str[6]);
+const std::string ElementalDatabase::getDefaultLocation() {
+	return std::string(db_path) + "/elemental_db.yml";
+}
+
+/*
+ * Reads and parses an entry from the elemental_db.
+ * @param node: YAML node containing the entry.
+ * @return count of successfully parsed rows
+ */
+uint64 ElementalDatabase::parseBodyNode(const YAML::Node &node) {
+	int32 id;
+
+	if (!this->asInt32(node, "Id", id))
+		return 0;
+
+	if (id < ELEMENTALID_AGNI_S || id > ELEMENTALID_TERA_L) {
+		this->invalidWarning(node["Id"], "Invalid Id %d (valid range: %d-%d).\n", id, ELEMENTALID_AGNI_S, ELEMENTALID_TERA_L);
+		return 0;
+	}
+
+	std::shared_ptr<s_elemental_db> elemental = this->find(id);
+	bool exists = elemental != nullptr;
+
+	if (!exists) {
+		if (!this->nodesExist(node, { "AegisName", "Name", "Size", "Element", "ElementLevel" }))
+			return 0;
+
+		elemental = std::make_shared<s_elemental_db>();
+		elemental->class_ = id;
+		elemental->vd.class_ = id;
+	}
+
+	if (this->nodeExists(node, "AegisName")) {
+		std::string name;
+
+		if (!this->asString(node, "AegisName", name))
+			return 0;
+
+		if (name.size() > NAME_LENGTH) {
+			this->invalidWarning(node["AegisName"], "AegisName \"%s\" exceeds maximum of %d characters, capping...\n", name.c_str(), NAME_LENGTH - 1);
+		}
+
+		name.resize(NAME_LENGTH);
+		elemental->sprite = name;
+	}
+
+	if (this->nodeExists(node, "Name")) {
+		std::string name;
+
+		if (!this->asString(node, "Name", name))
+			return 0;
+
+
+		if (name.size() > NAME_LENGTH) {
+			this->invalidWarning(node["Name"], "Name \"%s\" exceeds maximum of %d characters, capping...\n", name.c_str(), NAME_LENGTH - 1);
+		}
+
+		name.resize(NAME_LENGTH);
+		elemental->name = name;
+	}
+
+	if (this->nodeExists(node, "Level")) {
+		uint16 level;
+
+		if (!this->asUInt16(node, "Level", level))
+			return 0;
+
+		if (level > MAX_LEVEL) {
+			this->invalidWarning(node["Level"], "Level %d exceeds MAX_LEVEL, capping to %d.\n", level, MAX_LEVEL);
+			level = MAX_LEVEL;
+		}
+
+		elemental->lv = level;
+	} else {
+		if (!exists)
+			elemental->lv = 1;
+	}
+
+	if (this->nodeExists(node, "Hp")) {
+		uint32 hp;
+
+		if (!this->asUInt32(node, "Hp", hp))
+			return 0;
+
+		elemental->status.max_hp = hp;
+	} else {
+		if (!exists)
+			elemental->status.max_hp = 0;
+	}
+
+	if (this->nodeExists(node, "Sp")) {
+		uint32 sp;
+
+		if (!this->asUInt32(node, "Sp", sp))
+			return 0;
+
+		elemental->status.max_sp = sp;
+	} else {
+		if (!exists)
+			elemental->status.max_sp = 1;
+	}
+
+	if (this->nodeExists(node, "Attack")) {
+		uint16 atk;
+
+		if (!this->asUInt16(node, "Attack", atk))
+			return 0;
+
+		elemental->status.rhw.atk = atk;
+	} else {
+		if (!exists)
+			elemental->status.rhw.atk = 1;
+	}
+
+	if (this->nodeExists(node, "Attack2")) {
+		uint16 atk;
+
+		if (!this->asUInt16(node, "Attack2", atk))
+			return 0;
+
 #ifdef RENEWAL
 #ifdef RENEWAL
-	status->rhw.atk = atoi(str[7]); // BaseATK
-	status->rhw.matk = atoi(str[8]); // BaseMATK
+		elemental->status.rhw.matk = atk;
 #else
 #else
-	status->rhw.atk = atoi(str[7]); // MinATK
-	status->rhw.atk2 = atoi(str[8]); // MaxATK
+		elemental->status.rhw.atk2 = atk;
 #endif
 #endif
-	status->def = atoi(str[9]);
-	status->mdef = atoi(str[10]);
-	status->str = atoi(str[11]);
-	status->agi = atoi(str[12]);
-	status->vit = atoi(str[13]);
-	status->int_ = atoi(str[14]);
-	status->dex = atoi(str[15]);
-	status->luk = atoi(str[16]);
-	db->range2 = atoi(str[17]);
-	db->range3 = atoi(str[18]);
-	status->size = atoi(str[19]);
-	status->race = atoi(str[20]);
-
-	ele = atoi(str[21]);
-	status->def_ele = ele%20;
-	status->ele_lv = (unsigned char)floor(ele/20.);
-	if( !CHK_ELEMENT(status->def_ele) ) {
-		ShowWarning("read_elementaldb_sub: Elemental %d has invalid element type %d (max element is %d)\n", db->class_, status->def_ele, ELE_ALL - 1);
-		status->def_ele = ELE_NEUTRAL;
-	}
-	if( !CHK_ELEMENT_LEVEL(status->ele_lv) ) {
-		ShowWarning("read_elementaldb_sub: Elemental %d has invalid element level %d (max is %d)\n", db->class_, status->ele_lv, MAX_ELE_LEVEL);
-		status->ele_lv = 1;
-	}
-
-	status->aspd_rate = 1000;
-	status->speed = atoi(str[22]);
-	status->adelay = atoi(str[23]);
-	status->amotion = atoi(str[24]);
-	status->dmotion = atoi(str[25]);
-
-	if (i >= elemental_count)
-		elemental_count++;
-	return true;
-}
+	} else {
+		if (!exists)
+#ifdef RENEWAL
+			elemental->status.rhw.matk = 0;
+#else
+			elemental->status.rhw.atk2 = 0;
+#endif
+	}
 
 
-void read_elementaldb(void) {
-	const char *filename[] = { "elemental_db.txt", DBIMPORT"/elemental_db.txt" };
-	uint8 i;
+	if (this->nodeExists(node, "Defense")) {
+		int32 def;
 
 
-	elemental_count = 0;
-	memset(elemental_db, 0, sizeof(elemental_db));
-	for(i = 0; i<ARRAYLENGTH(filename); i++){
-		sv_readdb(db_path, filename[i], ',', 26, 26, -1, &read_elementaldb_sub, i > 0);
+		if (!this->asInt32(node, "Defense", def))
+			return 0;
+
+		if (def < DEFTYPE_MIN || def > DEFTYPE_MAX) {
+			this->invalidWarning(node["Defense"], "Invalid defense %d, capping...\n", def);
+			def = cap_value(def, DEFTYPE_MIN, DEFTYPE_MAX);
+		}
+
+		elemental->status.def = static_cast<defType>(def);
+	} else {
+		if (!exists)
+			elemental->status.def = 0;
 	}
 	}
-}
 
 
-/**
-* Reads Elemental Skill DB lines
-* ElementalID,SkillID,SkillLevel,ReqMode
-*/
-static bool read_elemental_skilldb_sub(char* str[], int columns, int current) {
-	uint16 class_ = atoi(str[0]), skill_id, skill_lv, skillmode;
-	uint8 i;
-	struct s_elemental_db *db;
-
-	ARR_FIND(0, MAX_ELEMENTAL_CLASS, i, class_ == elemental_db[i].class_);
-	if( i == MAX_ELEMENTAL_CLASS ) {
-		ShowError("read_elemental_skilldb_sub: Class not found in elemental_db for skill entry, line %d.\n", current);
-		return false;
-	}
-
-	skill_id = atoi(str[1]);
-	if( !SKILL_CHK_ELEM(skill_id) ) {
-		ShowError("read_elemental_skilldb_sub: Invalid Elemental skill '%d'.\n", skill_id);
-		return false;
-	}
-
-	db = &elemental_db[i];
-	skill_lv = atoi(str[2]);
-
-	skillmode = atoi(str[3]);
-	if( skillmode < EL_SKILLMODE_PASSIVE || skillmode > EL_SKILLMODE_AGGRESSIVE ) {
-		ShowError("read_elemental_skilldb_sub: Skillmode out of range, line %d.\n",current);
-		return false;
-	}
-	ARR_FIND( 0, MAX_ELESKILLTREE, i, db->skill[i].id == 0 || db->skill[i].id == skill_id );
-	if( i == MAX_ELESKILLTREE ) {
-		ShowWarning("read_elemental_skilldb_sub: Unable to load skill %d into Elemental %d's tree. Maximum number of skills per elemental has been reached.\n", skill_id, class_);
-		return false;
-	}
-	db->skill[i].id = skill_id;
-	db->skill[i].lv = skill_lv;
-	db->skill[i].mode = skillmode;
-	return true;
-}
+	if (this->nodeExists(node, "MagicDefense")) {
+		int32 def;
+
+		if (!this->asInt32(node, "MagicDefense", def))
+			return 0;
+
+		if (def < DEFTYPE_MIN || def > DEFTYPE_MAX) {
+			this->invalidWarning(node["MagicDefense"], "Invalid magic defense %d, capping...\n", def);
+			def = cap_value(def, DEFTYPE_MIN, DEFTYPE_MAX);
+		}
 
 
-void read_elemental_skilldb(void) {
-	const char *filename[] = { "elemental_skill_db.txt", DBIMPORT"/elemental_skill_db.txt" };
-	uint8 i;
-	for(i = 0; i<ARRAYLENGTH(filename); i++){
-		sv_readdb(db_path, filename[i], ',', 4, 4, -1, &read_elemental_skilldb_sub, i > 0);
+		elemental->status.mdef = static_cast<defType>(def);
+	} else {
+		if (!exists)
+			elemental->status.mdef = 0;
 	}
 	}
-}
 
 
-void reload_elementaldb(void) {
-	read_elementaldb();
-	reload_elemental_skilldb();
-}
+	if (this->nodeExists(node, "Str")) {
+		uint16 stat;
+
+		if (!this->asUInt16(node, "Str", stat))
+			return 0;
+
+		elemental->status.str = stat;
+	} else {
+		if (!exists)
+			elemental->status.str = 0;
+	}
+
+	if (this->nodeExists(node, "Agi")) {
+		uint16 stat;
+
+		if (!this->asUInt16(node, "Agi", stat))
+			return 0;
+
+		elemental->status.agi = stat;
+	} else {
+		if (!exists)
+			elemental->status.agi = 0;
+	}
+
+	if (this->nodeExists(node, "Vit")) {
+		uint16 stat;
+
+		if (!this->asUInt16(node, "Vit", stat))
+			return 0;
+
+		elemental->status.vit = stat;
+	} else {
+		if (!exists)
+			elemental->status.vit = 0;
+	}
 
 
-void reload_elemental_skilldb(void) {
-	read_elemental_skilldb();
+	if (this->nodeExists(node, "Int")) {
+		uint16 stat;
+
+		if (!this->asUInt16(node, "Int", stat))
+			return 0;
+
+		elemental->status.int_ = stat;
+	} else {
+		if (!exists)
+			elemental->status.int_ = 0;
+	}
+
+	if (this->nodeExists(node, "Dex")) {
+		uint16 stat;
+
+		if (!this->asUInt16(node, "Dex", stat))
+			return 0;
+
+		elemental->status.dex = stat;
+	} else {
+		if (!exists)
+			elemental->status.dex = 0;
+	}
+
+	if (this->nodeExists(node, "Luk")) {
+		uint16 stat;
+
+		if (!this->asUInt16(node, "Luk", stat))
+			return 0;
+
+		elemental->status.luk = stat;
+	} else {
+		if (!exists)
+			elemental->status.luk = 0;
+	}
+
+	if (this->nodeExists(node, "AttackRange")) {
+		uint16 range;
+
+		if (!this->asUInt16(node, "AttackRange", range))
+			return 0;
+
+		elemental->status.rhw.range = range;
+	} else {
+		if (!exists)
+			elemental->status.rhw.range = 0;
+	}
+
+	if (this->nodeExists(node, "SkillRange")) {
+		uint16 range;
+
+		if (!this->asUInt16(node, "SkillRange", range))
+			return 0;
+
+		elemental->range2 = range;
+	} else {
+		if (!exists)
+			elemental->range2 = 5;
+	}
+
+	if (this->nodeExists(node, "ChaseRange")) {
+		uint16 range;
+
+		if (!this->asUInt16(node, "ChaseRange", range))
+			return 0;
+
+		elemental->range3 = range;
+	} else {
+		if (!exists)
+			elemental->range3 = 12;
+	}
+
+	if (this->nodeExists(node, "Size")) {
+		std::string size;
+
+		if (!this->asString(node, "Size", size))
+			return 0;
+
+		std::string size_constant = "Size_" + size;
+		int64 constant;
+
+		if (!script_get_constant(size_constant.c_str(), &constant) || constant < SZ_SMALL || constant > SZ_BIG) {
+			this->invalidWarning(node["Size"], "Invalid size %s, defaulting to Small.\n", size.c_str());
+			constant = SZ_SMALL;
+		}
+
+		elemental->status.size = static_cast<e_size>(constant);
+	}
+
+	if (this->nodeExists(node, "Race")) {
+		std::string race;
+
+		if (!this->asString(node, "Race", race))
+			return 0;
+
+		std::string race_constant = "RC_" + race;
+		int64 constant;
+
+		if (!script_get_constant(race_constant.c_str(), &constant) || !CHK_RACE(constant)) {
+			this->invalidWarning(node["Race"], "Invalid race %s, defaulting to Formless.\n", race.c_str());
+			constant = RC_FORMLESS;
+		}
+
+		elemental->status.race = static_cast<e_race>(constant);
+	} else {
+		if (!exists)
+			elemental->status.race = RC_FORMLESS;
+	}
+
+	if (this->nodeExists(node, "Element")) {
+		std::string ele;
+
+		if (!this->asString(node, "Element", ele))
+			return 0;
+
+		std::string ele_constant = "ELE_" + ele;
+		int64 constant;
+
+		if (!script_get_constant(ele_constant.c_str(), &constant) || !CHK_ELEMENT(constant)) {
+			this->invalidWarning(node["Element"], "Invalid element %s, defaulting to Neutral.\n", ele.c_str());
+			constant = ELE_NEUTRAL;
+		}
+
+		elemental->status.def_ele = static_cast<e_element>(constant);
+	}
+
+	if (this->nodeExists(node, "ElementLevel")) {
+		uint16 level;
+
+		if (!this->asUInt16(node, "ElementLevel", level))
+			return 0;
+
+		if (!CHK_ELEMENT_LEVEL(level)) {
+			this->invalidWarning(node["ElementLevel"], "Invalid element level %hu, defaulting to 1.\n", level);
+			level = 1;
+		}
+
+		elemental->status.ele_lv = static_cast<uint8>(level);
+	}
+
+	if (this->nodeExists(node, "WalkSpeed")) {
+		uint16 speed;
+
+		if (!this->asUInt16(node, "WalkSpeed", speed))
+			return 0;
+
+		if (speed < MIN_WALK_SPEED || speed > MAX_WALK_SPEED) {
+			this->invalidWarning(node["WalkSpeed"], "Invalid walk speed %hu, capping...\n", speed);
+			speed = cap_value(speed, MIN_WALK_SPEED, MAX_WALK_SPEED);
+		}
+
+		elemental->status.speed = speed;
+	} else {
+		if (!exists)
+			elemental->status.speed = 200;
+	}
+
+	if (this->nodeExists(node, "AttackDelay")) {
+		uint16 speed;
+
+		if (!this->asUInt16(node, "AttackDelay", speed))
+			return 0;
+
+		elemental->status.adelay = cap_value(speed, 0, 4000);
+	} else {
+		if (!exists)
+			elemental->status.adelay = 504;
+	}
+
+	if (this->nodeExists(node, "AttackMotion")) {
+		uint16 speed;
+
+		if (!this->asUInt16(node, "AttackMotion", speed))
+			return 0;
+
+		elemental->status.amotion = cap_value(speed, 0, 2000);
+	} else {
+		if (!exists)
+			elemental->status.amotion = 1020;
+	}
+
+	if (this->nodeExists(node, "DamageMotion")) {
+		uint16 speed;
+
+		if (!this->asUInt16(node, "DamageMotion", speed))
+			return 0;
+
+		elemental->status.dmotion = speed;
+	} else {
+		if (!exists)
+			elemental->status.dmotion = 360;
+	}
+
+	elemental->status.aspd_rate = 1000;
+
+	if (this->nodeExists(node, "Mode")) {
+		const YAML::Node &ModeNode = node["Mode"];
+
+		for (const auto &Modeit : ModeNode) {
+			std::string mode_name = Modeit.first.as<std::string>();
+
+			std::string mode_constant = "EL_SKILLMODE_" + mode_name;
+			int64 constant;
+
+			if (!script_get_constant(mode_constant.c_str(), &constant) || constant < EL_SKILLMODE_PASSIVE || constant > EL_SKILLMODE_AGGRESSIVE) {
+				this->invalidWarning(node["Mode"], "Invalid mode %s, skipping.\n", mode_name.c_str());
+				continue;
+			}
+
+			e_elemental_skillmode mode = static_cast<e_elemental_skillmode>(constant);
+
+			std::shared_ptr<s_elemental_skill> entry = util::umap_find(elemental->skill, mode);
+			bool mode_exists = entry != nullptr;
+
+			if (!mode_exists)
+				entry = std::make_shared<s_elemental_skill>();
+
+			const YAML::Node &SkillNode = ModeNode[mode_name];
+			std::string skill_name;
+
+			if (!this->asString(SkillNode, "Skill", skill_name))
+				return 0;
+
+			uint16 skill_id = skill_name2id(skill_name.c_str());
+
+			if (skill_id == 0) {
+				this->invalidWarning(SkillNode["Skill"], "Skipping invalid skill %s for mode %s.\n", skill_name.c_str(), mode_name.c_str());
+				return 0;
+			}
+
+			if (!SKILL_CHK_ELEM(skill_id)) {
+				this->invalidWarning(SkillNode["Skill"], "Skill %s (%u) is out of the skill range [%u-%u], skipping.\n", skill_name.c_str(), skill_id, EL_SKILLBASE, EL_SKILLBASE + MAX_ELEMENTALSKILL - 1);
+				return 0;
+			}
+
+			entry->id = skill_id;
+
+			if (this->nodeExists(SkillNode, "Level")) {
+				uint16 level;
+
+				if (!this->asUInt16(SkillNode, "Level", level))
+					return 0;
+
+				if (level > skill_get_max(skill_id)) {
+					this->invalidWarning(SkillNode["Level"], "Skill %s's level %hu exceeds max level %hu.\n", skill_name.c_str(), level, skill_get_max(skill_id));
+					return 0;
+				}
+
+				entry->lv = level;
+			} else {
+				if (!mode_exists)
+					entry->lv = 1;
+			}
+
+			if (entry->lv == 0) {
+				elemental->skill.erase(mode);
+				return 0;
+			}
+
+			if (!mode_exists)
+				elemental->skill.insert({ mode, entry });
+		}
+	}
+
+	if (!exists)
+		this->put(id, elemental);
+
+	return 1;
 }
 }
 
 
 void do_init_elemental(void) {
 void do_init_elemental(void) {
-	read_elementaldb();
-	read_elemental_skilldb();
+	elemental_db.load();
 
 
 	add_timer_func_list(elemental_ai_timer,"elemental_ai_timer");
 	add_timer_func_list(elemental_ai_timer,"elemental_ai_timer");
 	add_timer_interval(gettick()+MIN_ELETHINKTIME,elemental_ai_timer,0,0,MIN_ELETHINKTIME);
 	add_timer_interval(gettick()+MIN_ELETHINKTIME,elemental_ai_timer,0,0,MIN_ELETHINKTIME);
 }
 }
 
 
 void do_final_elemental(void) {
 void do_final_elemental(void) {
-	return;
+	elemental_db.clear();
 }
 }

+ 49 - 42
src/map/elemental.hpp

@@ -4,6 +4,7 @@
 #ifndef ELEMENTAL_HPP
 #ifndef ELEMENTAL_HPP
 #define ELEMENTAL_HPP
 #define ELEMENTAL_HPP
 
 
+#include "../common/database.hpp"
 #include "../common/mmo.hpp"
 #include "../common/mmo.hpp"
 #include "../common/timer.hpp"
 #include "../common/timer.hpp"
 
 
@@ -19,7 +20,7 @@ const t_tick MIN_ELETHINKTIME = 100;
 #define EL_MODE_PASSIVE MD_CANMOVE
 #define EL_MODE_PASSIVE MD_CANMOVE
 
 
 ///Enum of Elemental Skill Mode
 ///Enum of Elemental Skill Mode
-enum elemental_skillmode : uint8 {
+enum e_elemental_skillmode : uint8 {
 	EL_SKILLMODE_PASSIVE    = 0x1,
 	EL_SKILLMODE_PASSIVE    = 0x1,
 	EL_SKILLMODE_ASSIST     = 0x2,
 	EL_SKILLMODE_ASSIST     = 0x2,
 	EL_SKILLMODE_AGGRESSIVE = 0x4,
 	EL_SKILLMODE_AGGRESSIVE = 0x4,
@@ -41,36 +42,33 @@ enum elemental_elementalid  : uint16 {
 	ELEMENTALID_TERA_L,
 	ELEMENTALID_TERA_L,
 };
 };
 
 
-struct elemental_skill {
-	unsigned short id, lv;
-	short mode;
+struct s_elemental_skill {
+	uint16 id, lv;
 };
 };
 
 
 struct s_elemental_db {
 struct s_elemental_db {
-	int class_;
-	char sprite[NAME_LENGTH], name[NAME_LENGTH];
-	unsigned short lv;
-	short range2, range3;
-	struct status_data status;
-	struct view_data vd;
-	struct elemental_skill skill[MAX_ELESKILLTREE];
+	int32 class_;
+	std::string sprite, name;
+	uint16 lv;
+	uint16 range2, range3;
+	status_data status;
+	view_data vd;
+	std::unordered_map<e_elemental_skillmode, std::shared_ptr<s_elemental_skill>> skill;	/// mode, skill
 };
 };
 
 
-extern struct s_elemental_db elemental_db[MAX_ELEMENTAL_CLASS];
+struct s_elemental_data {
+	block_list bl;
+	unit_data ud;
+	view_data *vd;
+	status_data base_status, battle_status;
+	status_change sc;
+	regen_data regen;
 
 
-struct elemental_data {
-	struct block_list bl;
-	struct unit_data ud;
-	struct view_data *vd;
-	struct status_data base_status, battle_status;
-	struct status_change sc;
-	struct regen_data regen;
-
-	struct s_elemental_db *db;
-	struct s_elemental elemental;
+	std::shared_ptr<s_elemental_db> db;
+	s_elemental elemental;
 
 
 	int masterteleport_timer;
 	int masterteleport_timer;
-	struct map_session_data *master;
+	map_session_data *master;
 	int summon_timer;
 	int summon_timer;
 	int skill_timer;
 	int skill_timer;
 
 
@@ -79,38 +77,47 @@ struct elemental_data {
 	int target_id, attacked_id;
 	int target_id, attacked_id;
 };
 };
 
 
-bool elemental_class(int class_);
+class ElementalDatabase : public TypesafeYamlDatabase<int32, s_elemental_db> {
+public:
+	ElementalDatabase() : TypesafeYamlDatabase("ELEMENTAL_DB", 1) {
+
+	}
+
+	const std::string getDefaultLocation();
+	uint64 parseBodyNode(const YAML::Node& node);
+};
+
+extern ElementalDatabase elemental_db;
+
 struct view_data * elemental_get_viewdata(int class_);
 struct view_data * elemental_get_viewdata(int class_);
 
 
-int elemental_create(struct map_session_data *sd, int class_, unsigned int lifetime);
-int elemental_data_received(struct s_elemental *ele, bool flag);
-int elemental_save(struct elemental_data *ed);
+int elemental_create(map_session_data *sd, int class_, unsigned int lifetime);
+int elemental_data_received(s_elemental *ele, bool flag);
+int elemental_save(s_elemental_data *ed);
 
 
-int elemental_change_mode_ack(struct elemental_data *ed, enum elemental_skillmode skill_mode);
-int elemental_change_mode(struct elemental_data *ed, enum e_mode mode);
+int elemental_change_mode_ack(s_elemental_data *ed, e_elemental_skillmode skill_mode);
+int elemental_change_mode(s_elemental_data *ed, e_mode mode);
 
 
-void elemental_heal(struct elemental_data *ed, int hp, int sp);
-int elemental_dead(struct elemental_data *ed);
+void elemental_heal(s_elemental_data *ed, int hp, int sp);
+int elemental_dead(s_elemental_data *ed);
 
 
-int elemental_delete(struct elemental_data *ed);
-void elemental_summon_stop(struct elemental_data *ed);
+int elemental_delete(s_elemental_data *ed);
+void elemental_summon_stop(s_elemental_data *ed);
 
 
-t_tick elemental_get_lifetime(struct elemental_data *ed);
+t_tick elemental_get_lifetime(s_elemental_data *ed);
 
 
-int elemental_unlocktarget(struct elemental_data *ed);
-bool elemental_skillnotok(uint16 skill_id, struct elemental_data *ed);
-int elemental_set_target( struct map_session_data *sd, struct block_list *bl );
-int elemental_clean_single_effect(struct elemental_data *ed, uint16 skill_id);
-int elemental_clean_effect(struct elemental_data *ed);
-int elemental_action(struct elemental_data *ed, struct block_list *bl, t_tick tick);
+int elemental_unlocktarget(s_elemental_data *ed);
+bool elemental_skillnotok(uint16 skill_id, s_elemental_data *ed);
+int elemental_set_target( map_session_data *sd, block_list *bl );
+int elemental_clean_single_effect(s_elemental_data *ed, uint16 skill_id);
+int elemental_clean_effect(s_elemental_data *ed);
+int elemental_action(s_elemental_data *ed, block_list *bl, t_tick tick);
 struct s_skill_condition elemental_skill_get_requirements(uint16 skill_id, uint16 skill_lv);
 struct s_skill_condition elemental_skill_get_requirements(uint16 skill_id, uint16 skill_lv);
 
 
 #define elemental_stop_walking(ed, type) unit_stop_walking(&(ed)->bl, type)
 #define elemental_stop_walking(ed, type) unit_stop_walking(&(ed)->bl, type)
 #define elemental_stop_attack(ed) unit_stop_attack(&(ed)->bl)
 #define elemental_stop_attack(ed) unit_stop_attack(&(ed)->bl)
 
 
 void read_elemental_skilldb(void);
 void read_elemental_skilldb(void);
-void reload_elementaldb(void);
-void reload_elemental_skilldb(void);
 void do_init_elemental(void);
 void do_init_elemental(void);
 void do_final_elemental(void);
 void do_final_elemental(void);
 
 

+ 1 - 2
src/map/map-server.vcxproj

@@ -303,8 +303,7 @@
     <Copy SourceFiles="$(SolutionDir)db\import-tmpl\castle_db.yml" DestinationFolder="$(SolutionDir)db\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)db\import\castle_db.yml')" />
     <Copy SourceFiles="$(SolutionDir)db\import-tmpl\castle_db.yml" DestinationFolder="$(SolutionDir)db\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)db\import\castle_db.yml')" />
     <Copy SourceFiles="$(SolutionDir)db\import-tmpl\const.yml" DestinationFolder="$(SolutionDir)db\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)db\import\const.yml')" />
     <Copy SourceFiles="$(SolutionDir)db\import-tmpl\const.yml" DestinationFolder="$(SolutionDir)db\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)db\import\const.yml')" />
     <Copy SourceFiles="$(SolutionDir)db\import-tmpl\create_arrow_db.yml" DestinationFolder="$(SolutionDir)db\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)db\import\create_arrow_db.yml')" />
     <Copy SourceFiles="$(SolutionDir)db\import-tmpl\create_arrow_db.yml" DestinationFolder="$(SolutionDir)db\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)db\import\create_arrow_db.yml')" />
-    <Copy SourceFiles="$(SolutionDir)db\import-tmpl\elemental_db.txt" DestinationFolder="$(SolutionDir)db\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)db\import\elemental_db.txt')" />
-    <Copy SourceFiles="$(SolutionDir)db\import-tmpl\elemental_skill_db.txt" DestinationFolder="$(SolutionDir)db\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)db\import\elemental_skill_db.txt')" />
+    <Copy SourceFiles="$(SolutionDir)db\import-tmpl\elemental_db.yml" DestinationFolder="$(SolutionDir)db\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)db\import\elemental_db.yml')" />
     <Copy SourceFiles="$(SolutionDir)db\import-tmpl\exp_homun.yml" DestinationFolder="$(SolutionDir)db\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)db\import\exp_homun.yml')" />
     <Copy SourceFiles="$(SolutionDir)db\import-tmpl\exp_homun.yml" DestinationFolder="$(SolutionDir)db\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)db\import\exp_homun.yml')" />
     <Copy SourceFiles="$(SolutionDir)db\import-tmpl\exp_guild.yml" DestinationFolder="$(SolutionDir)db\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)db\import\exp_guild.yml')" />
     <Copy SourceFiles="$(SolutionDir)db\import-tmpl\exp_guild.yml" DestinationFolder="$(SolutionDir)db\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)db\import\exp_guild.yml')" />
     <Copy SourceFiles="$(SolutionDir)db\import-tmpl\guild_skill_tree.yml" DestinationFolder="$(SolutionDir)db\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)db\import\guild_skill_tree.yml')" />
     <Copy SourceFiles="$(SolutionDir)db\import-tmpl\guild_skill_tree.yml" DestinationFolder="$(SolutionDir)db\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)db\import\guild_skill_tree.yml')" />

+ 1 - 1
src/map/map.cpp

@@ -2249,7 +2249,7 @@ struct pet_data* map_id2pd(int id){
 	return BL_CAST(BL_PET, bl);
 	return BL_CAST(BL_PET, bl);
 }
 }
 
 
-struct elemental_data* map_id2ed(int id) {
+struct s_elemental_data* map_id2ed(int id) {
 	struct block_list* bl = map_id2bl(id);
 	struct block_list* bl = map_id2bl(id);
 	return BL_CAST(BL_ELEM, bl);
 	return BL_CAST(BL_ELEM, bl);
 }
 }

+ 2 - 2
src/map/map.hpp

@@ -1089,7 +1089,7 @@ struct npc_data * map_id2nd(int id);
 struct homun_data* map_id2hd(int id);
 struct homun_data* map_id2hd(int id);
 struct mercenary_data* map_id2mc(int id);
 struct mercenary_data* map_id2mc(int id);
 struct pet_data* map_id2pd(int id);
 struct pet_data* map_id2pd(int id);
-struct elemental_data* map_id2ed(int id);
+struct s_elemental_data* map_id2ed(int id);
 struct chat_data* map_id2cd(int id);
 struct chat_data* map_id2cd(int id);
 struct block_list * map_id2bl(int id);
 struct block_list * map_id2bl(int id);
 bool map_blid_exists( int id );
 bool map_blid_exists( int id );
@@ -1196,7 +1196,7 @@ typedef struct skill_unit       TBL_SKILL;
 typedef struct pet_data         TBL_PET;
 typedef struct pet_data         TBL_PET;
 typedef struct homun_data       TBL_HOM;
 typedef struct homun_data       TBL_HOM;
 typedef struct mercenary_data   TBL_MER;
 typedef struct mercenary_data   TBL_MER;
-typedef struct elemental_data	TBL_ELEM;
+typedef struct s_elemental_data	TBL_ELEM;
 
 
 #define BL_CAST(type_, bl) \
 #define BL_CAST(type_, bl) \
 	( ((bl) == (struct block_list*)NULL || (bl)->type != (type_)) ? (T ## type_ *)NULL : (T ## type_ *)(bl) )
 	( ((bl) == (struct block_list*)NULL || (bl)->type != (type_)) ? (T ## type_ *)NULL : (T ## type_ *)(bl) )

+ 1 - 1
src/map/mob.cpp

@@ -2390,7 +2390,7 @@ void mob_log_damage(struct mob_data *md, struct block_list *src, int damage)
 		}
 		}
 		case BL_ELEM:
 		case BL_ELEM:
 		{
 		{
-			struct elemental_data *ele = (TBL_ELEM*)src;
+			s_elemental_data *ele = (TBL_ELEM*)src;
 			if( ele->master )
 			if( ele->master )
 				char_id = ele->master->status.char_id;
 				char_id = ele->master->status.char_id;
 			if( damage )
 			if( damage )

+ 1 - 1
src/map/pc.hpp

@@ -661,7 +661,7 @@ struct map_session_data {
 	struct pet_data *pd;
 	struct pet_data *pd;
 	struct homun_data *hd;	// [blackhole89]
 	struct homun_data *hd;	// [blackhole89]
 	struct mercenary_data *md;
 	struct mercenary_data *md;
-	struct elemental_data *ed;
+	s_elemental_data *ed;
 
 
 	struct s_hate_mob {
 	struct s_hate_mob {
 		int  m; //-1 - none, other: map index corresponding to map name.
 		int  m; //-1 - none, other: map index corresponding to map name.

+ 5 - 0
src/map/script_constants.hpp

@@ -8245,6 +8245,11 @@
 	export_constant(PARAM_CON);
 	export_constant(PARAM_CON);
 	export_constant(PARAM_CRT);
 	export_constant(PARAM_CRT);
 
 
+	/* Elemental Skill Mode */
+	export_constant(EL_SKILLMODE_PASSIVE);
+	export_constant(EL_SKILLMODE_ASSIST);
+	export_constant(EL_SKILLMODE_AGGRESSIVE);
+
 	#undef export_constant
 	#undef export_constant
 	#undef export_constant2
 	#undef export_constant2
 	#undef export_parameter
 	#undef export_parameter

+ 4 - 4
src/map/skill.cpp

@@ -6166,7 +6166,7 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, uint
 
 
 	case EL_TIDAL_WEAPON:
 	case EL_TIDAL_WEAPON:
 		if( src->type == BL_ELEM ) {
 		if( src->type == BL_ELEM ) {
-			struct elemental_data *ele = BL_CAST(BL_ELEM,src);
+			s_elemental_data *ele = BL_CAST(BL_ELEM,src);
 			struct status_change *tsc_ele = status_get_sc(&ele->bl);
 			struct status_change *tsc_ele = status_get_sc(&ele->bl);
 			sc_type type = status_skill2sc(skill_id), type2;
 			sc_type type = status_skill2sc(skill_id), type2;
 
 
@@ -11010,7 +11010,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui
 
 
 	case SO_EL_CURE:
 	case SO_EL_CURE:
 		if( sd ) {
 		if( sd ) {
-			struct elemental_data *ed = sd->ed;
+			s_elemental_data *ed = sd->ed;
 			int s_hp, s_sp;
 			int s_hp, s_sp;
 
 
 			if( !ed )
 			if( !ed )
@@ -11145,7 +11145,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui
 	case EL_SOLID_SKIN:
 	case EL_SOLID_SKIN:
 	case EL_STONE_SHIELD:
 	case EL_STONE_SHIELD:
 	case EL_WIND_STEP: {
 	case EL_WIND_STEP: {
-			struct elemental_data *ele = BL_CAST(BL_ELEM, src);
+			s_elemental_data *ele = BL_CAST(BL_ELEM, src);
 			if( ele ) {
 			if( ele ) {
 				sc_type type2 = (sc_type)(type-1);
 				sc_type type2 = (sc_type)(type-1);
 				struct status_change *sc = status_get_sc(&ele->bl);
 				struct status_change *sc = status_get_sc(&ele->bl);
@@ -11171,7 +11171,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui
 		skill_unitsetting(src,skill_id,skill_lv,bl->x,bl->y,0);
 		skill_unitsetting(src,skill_id,skill_lv,bl->x,bl->y,0);
 		break;
 		break;
 	case EL_WATER_SCREEN: {
 	case EL_WATER_SCREEN: {
-			struct elemental_data *ele = BL_CAST(BL_ELEM, src);
+			s_elemental_data *ele = BL_CAST(BL_ELEM, src);
 			if( ele ) {
 			if( ele ) {
 				struct status_change *sc = status_get_sc(&ele->bl);
 				struct status_change *sc = status_get_sc(&ele->bl);
 				sc_type type2 = (sc_type)(type-1);
 				sc_type type2 = (sc_type)(type-1);

+ 5 - 5
src/map/status.cpp

@@ -5264,10 +5264,10 @@ int status_calc_homunculus_(struct homun_data *hd, enum e_status_calc_opt opt)
  * @param opt: Whether it is first calc or not (0 on status change)
  * @param opt: Whether it is first calc or not (0 on status change)
  * @return 0
  * @return 0
  */
  */
-int status_calc_elemental_(struct elemental_data *ed, enum e_status_calc_opt opt)
+int status_calc_elemental_(s_elemental_data *ed, e_status_calc_opt opt)
 {
 {
 	struct status_data *status = &ed->base_status;
 	struct status_data *status = &ed->base_status;
-	struct s_elemental *ele = &ed->elemental;
+	s_elemental *ele = &ed->elemental;
 	struct map_session_data *sd = ed->master;
 	struct map_session_data *sd = ed->master;
 
 
 	if( !sd )
 	if( !sd )
@@ -8455,7 +8455,7 @@ const char* status_get_name(struct block_list *bl)
 		case BL_HOM:	return ((TBL_HOM*)bl)->homunculus.name;
 		case BL_HOM:	return ((TBL_HOM*)bl)->homunculus.name;
 		//case BL_MER: // They only have database names which are global, not specific to GID.
 		//case BL_MER: // They only have database names which are global, not specific to GID.
 		case BL_NPC:	return ((TBL_NPC*)bl)->name;
 		case BL_NPC:	return ((TBL_NPC*)bl)->name;
-		//case BL_ELEM: // They only have database names which are global, not specific to GID.
+		case BL_ELEM:	return ((TBL_ELEM *)bl)->db->name.c_str(); // They only have database names which are global, not specific to GID.
 	}
 	}
 	return "Unknown";
 	return "Unknown";
 }
 }
@@ -8815,7 +8815,7 @@ void status_set_viewdata(struct block_list *bl, int class_)
 		vd = hom_get_viewdata(class_);
 		vd = hom_get_viewdata(class_);
 	else if (mercenary_db(class_))
 	else if (mercenary_db(class_))
 		vd = mercenary_get_viewdata(class_);
 		vd = mercenary_get_viewdata(class_);
-	else if (elemental_class(class_))
+	else if (elemental_db.exists(class_))
 		vd = elemental_get_viewdata(class_);
 		vd = elemental_get_viewdata(class_);
 	else
 	else
 		vd = NULL;
 		vd = NULL;
@@ -8952,7 +8952,7 @@ void status_set_viewdata(struct block_list *bl, int class_)
 		break;
 		break;
 	case BL_ELEM:
 	case BL_ELEM:
 		{
 		{
-			struct elemental_data *ed = (struct elemental_data*)bl;
+			s_elemental_data *ed = (s_elemental_data*)bl;
 			if (vd)
 			if (vd)
 				ed->vd = vd;
 				ed->vd = vd;
 			else
 			else

+ 2 - 2
src/map/status.hpp

@@ -18,7 +18,7 @@ struct mob_data;
 struct pet_data;
 struct pet_data;
 struct homun_data;
 struct homun_data;
 struct mercenary_data;
 struct mercenary_data;
-struct elemental_data;
+struct s_elemental_data;
 struct npc_data;
 struct npc_data;
 struct status_change;
 struct status_change;
 
 
@@ -2882,7 +2882,7 @@ void status_calc_pet_(struct pet_data* pd, enum e_status_calc_opt opt);
 int status_calc_pc_(struct map_session_data* sd, enum e_status_calc_opt opt);
 int status_calc_pc_(struct map_session_data* sd, enum e_status_calc_opt opt);
 int status_calc_homunculus_(struct homun_data *hd, enum e_status_calc_opt opt);
 int status_calc_homunculus_(struct homun_data *hd, enum e_status_calc_opt opt);
 int status_calc_mercenary_(struct mercenary_data *md, enum e_status_calc_opt opt);
 int status_calc_mercenary_(struct mercenary_data *md, enum e_status_calc_opt opt);
-int status_calc_elemental_(struct elemental_data *ed, enum e_status_calc_opt opt);
+int status_calc_elemental_(s_elemental_data *ed, e_status_calc_opt opt);
 int status_calc_npc_(struct npc_data *nd, enum e_status_calc_opt opt);
 int status_calc_npc_(struct npc_data *nd, enum e_status_calc_opt opt);
 
 
 void status_calc_misc(struct block_list *bl, struct status_data *status, int level);
 void status_calc_misc(struct block_list *bl, struct status_data *status, int level);

+ 3 - 3
src/map/unit.cpp

@@ -66,7 +66,7 @@ struct unit_data* unit_bl2ud(struct block_list *bl)
 	case BL_NPC: return &((struct npc_data*)bl)->ud;
 	case BL_NPC: return &((struct npc_data*)bl)->ud;
 	case BL_HOM: return &((struct homun_data*)bl)->ud;
 	case BL_HOM: return &((struct homun_data*)bl)->ud;
 	case BL_MER: return &((struct mercenary_data*)bl)->ud;
 	case BL_MER: return &((struct mercenary_data*)bl)->ud;
-	case BL_ELEM: return &((struct elemental_data*)bl)->ud;
+	case BL_ELEM: return &((s_elemental_data*)bl)->ud;
 	default : return NULL;
 	default : return NULL;
 	}
 	}
 }
 }
@@ -3222,7 +3222,7 @@ int unit_remove_map_(struct block_list *bl, clr_type clrtype, const char* file,
 			break;
 			break;
 		}
 		}
 		case BL_ELEM: {
 		case BL_ELEM: {
-			struct elemental_data *ed = (struct elemental_data *)bl;
+			s_elemental_data *ed = (s_elemental_data *)bl;
 
 
 			ud->canact_tick = ud->canmove_tick;
 			ud->canact_tick = ud->canmove_tick;
 
 
@@ -3571,7 +3571,7 @@ int unit_free(struct block_list *bl, clr_type clrtype)
 			break;
 			break;
 		}
 		}
 		case BL_ELEM: {
 		case BL_ELEM: {
-			struct elemental_data *ed = (TBL_ELEM*)bl;
+			s_elemental_data *ed = (TBL_ELEM*)bl;
 			struct map_session_data *sd = ed->master;
 			struct map_session_data *sd = ed->master;
 
 
 			if( elemental_get_lifetime(ed) > 0 )
 			if( elemental_get_lifetime(ed) > 0 )

+ 125 - 0
src/tool/csv2yaml.cpp

@@ -122,6 +122,14 @@ static void job_txt_data(const std::string &modePath, const std::string &fixedPa
 		sv_readdb(modePath.c_str(), "job_param_db.txt", ',', 2, PARAM_MAX + 1, CLASS_COUNT, &pc_readdb_job_param, false);
 		sv_readdb(modePath.c_str(), "job_param_db.txt", ',', 2, PARAM_MAX + 1, CLASS_COUNT, &pc_readdb_job_param, false);
 }
 }
 
 
+// Elemental Summons Skill Database data to memory
+static void elemental_skill_txt_data(const std::string& modePath, const std::string& fixedPath) {
+	elemental_skill_tree.clear();
+
+	if (fileExists(fixedPath + "/elemental_skill_db.txt"))
+		sv_readdb(fixedPath.c_str(), "elemental_skill_db.txt", ',', 4, 4, -1, read_elemental_skilldb, false);
+}
+
 template<typename Func>
 template<typename Func>
 bool process( const std::string& type, uint32 version, const std::vector<std::string>& paths, const std::string& name, Func lambda, const std::string& rename = "" ){
 bool process( const std::string& type, uint32 version, const std::vector<std::string>& paths, const std::string& name, Func lambda, const std::string& rename = "" ){
 	for( const std::string& path : paths ){
 	for( const std::string& path : paths ){
@@ -474,6 +482,13 @@ int do_init( int argc, char** argv ){
 		return 0;
 		return 0;
 	}
 	}
 
 
+	elemental_skill_txt_data(path_db_mode, path_db);
+	if (!process("ELEMENTAL_DB", 1, root_paths, "elemental_db", [](const std::string &path, const std::string &name_ext) -> bool {
+		return sv_readdb(path.c_str(), name_ext.c_str(), ',', 26, 26, -1, &read_elementaldb, false);
+	})) {
+		return 0;
+	}
+
 	// TODO: add implementations ;-)
 	// TODO: add implementations ;-)
 
 
 	return 0;
 	return 0;
@@ -4403,3 +4418,113 @@ static bool pc_readdb_job1(char* fields[], int columns, int current) {
 
 
 	return true;
 	return true;
 }
 }
+
+// elemental_db.yml function
+//---------------------------
+static bool read_elemental_skilldb(char* str[], int columns, int current) {
+	uint16 skill_id = atoi(str[1]);
+	std::string *skill_name = util::umap_find(aegis_skillnames, skill_id);
+
+	if (skill_name == nullptr) {
+		ShowError("read_elemental_skilldb: Invalid skill '%hu'.\n", skill_id);
+		return false;
+	}
+
+	uint16 skillmode = atoi(str[3]);
+
+	std::string constant = constant_lookup(skillmode, "EL_SKILLMODE_");
+	constant.erase(0, 13);
+	std::string mode_name = name2Upper(constant);
+
+	s_elemental_skill_csv entry = {};
+
+	entry.skill_name = *skill_name;
+	entry.lv = atoi(str[2]);
+	entry.mode_name = mode_name;
+
+	uint16 class_ = atoi(str[0]);
+
+	if (util::umap_find(elemental_skill_tree, class_))
+		elemental_skill_tree[class_].push_back(entry);
+	else {
+		elemental_skill_tree[class_] = std::vector<s_elemental_skill_csv>();
+		elemental_skill_tree[class_].push_back(entry);
+	}
+
+	return true;
+}
+
+// Copied and adjusted from elemental.cpp
+static bool read_elementaldb(char* str[], int columns, int current) {
+	body << YAML::BeginMap;
+	body << YAML::Key << "Id" << YAML::Value << str[0];
+	body << YAML::Key << "AegisName" << YAML::Value << str[1];
+	body << YAML::Key << "Name" << YAML::Value << str[2];
+	body << YAML::Key << "Level" << YAML::Value << str[3];
+	if (atoi(str[4]) != 0)
+		body << YAML::Key << "Hp" << YAML::Value << str[4];
+	if (atoi(str[5]) != 1)
+		body << YAML::Key << "Sp" << YAML::Value << str[5];
+	if (atoi(str[7]) != 0)
+		body << YAML::Key << "Attack" << YAML::Value << str[7];
+	if (atoi(str[8]) != 0)
+		body << YAML::Key << "Attack2" << YAML::Value << str[8];
+	if (atoi(str[9]) != 0)
+		body << YAML::Key << "Defense" << YAML::Value << str[9];
+	if (atoi(str[10]) != 0)
+		body << YAML::Key << "MagicDefense" << YAML::Value << str[10];
+	if (atoi(str[11]) != 0)
+		body << YAML::Key << "Str" << YAML::Value << str[11];
+	if (atoi(str[12]) != 0)
+		body << YAML::Key << "Agi" << YAML::Value << str[12];
+	if (atoi(str[13]) != 0)
+		body << YAML::Key << "Vit" << YAML::Value << str[13];
+	if (atoi(str[14]) != 0)
+		body << YAML::Key << "Int" << YAML::Value << str[14];
+	if (atoi(str[15]) != 0)
+		body << YAML::Key << "Dex" << YAML::Value << str[15];
+	if (atoi(str[16]) != 0)
+		body << YAML::Key << "Luk" << YAML::Value << str[16];
+	if (atoi(str[6]) != 1)
+		body << YAML::Key << "AttackRange" << YAML::Value << str[6];
+	if (atoi(str[17]) != 5)
+		body << YAML::Key << "SkillRange" << YAML::Value << str[17];
+	if (atoi(str[18]) != 12)
+		body << YAML::Key << "ChaseRange" << YAML::Value << str[18];
+	body << YAML::Key << "Size" << YAML::Value << constant_lookup(strtol(str[19], nullptr, 10), "Size_") + 5;
+	if (atoi(str[20]) != 0)
+		body << YAML::Key << "Race" << YAML::Value << name2Upper(constant_lookup(atoi(str[20]), "RC_") + 3);
+
+	int ele = strtol(str[21], nullptr, 10);
+	body << YAML::Key << "Element" << YAML::Value << name2Upper(constant_lookup(ele % 20, "ELE_") + 4);
+	body << YAML::Key << "ElementLevel" << YAML::Value << floor(ele / 20.);
+
+	if (atoi(str[22]) != 200)
+		body << YAML::Key << "WalkSpeed" << YAML::Value << str[22];
+	if (atoi(str[23]) != 504)
+		body << YAML::Key << "AttackDelay" << YAML::Value << str[23];
+	if (atoi(str[24]) != 1020)
+		body << YAML::Key << "AttackMotion" << YAML::Value << str[24];
+	if (atoi(str[25]) != 360)
+		body << YAML::Key << "DamageMotion" << YAML::Value << str[25];
+
+	auto list = elemental_skill_tree.find( atoi(str[0]) );
+	if (list != elemental_skill_tree.end()) {
+		body << YAML::Key << "Mode";
+			body << YAML::BeginMap;
+		for (const auto &it : list->second) {
+			body << YAML::Key << it.mode_name;
+			body << YAML::BeginMap;
+			body << YAML::Key << "Skill" << YAML::Value << it.skill_name;
+			if (it.lv != 1)
+				body << YAML::Key << "Level" << YAML::Value << it.lv;
+			body << YAML::EndMap;
+		}
+			body << YAML::EndMap;
+	}
+
+	body << YAML::EndMap;
+
+	return true;
+}
+

+ 10 - 0
src/tool/csv2yaml.hpp

@@ -120,6 +120,14 @@ std::unordered_map<int, std::vector<int64>> job_hp, job_sp;
 std::unordered_map<int, s_job_param> job_param;
 std::unordered_map<int, s_job_param> job_param;
 std::unordered_map<int, int> exp_base_level, exp_job_level;
 std::unordered_map<int, int> exp_base_level, exp_job_level;
 
 
+struct s_elemental_skill_csv {
+	std::string skill_name,
+		mode_name;
+	uint16 lv;
+};
+
+std::unordered_map<uint16, std::vector<s_elemental_skill_csv>> elemental_skill_tree;
+
 static std::map<std::string, int> um_mapid2jobname {
 static std::map<std::string, int> um_mapid2jobname {
 	{ "Novice", JOB_NOVICE }, // Novice and Super Novice share the same value
 	{ "Novice", JOB_NOVICE }, // Novice and Super Novice share the same value
 	{ "SuperNovice", JOB_NOVICE },
 	{ "SuperNovice", JOB_NOVICE },
@@ -476,5 +484,7 @@ static bool pc_readdb_job_exp(char *fields[], int columns, int current);
 static bool pc_readdb_job_exp_sub(char *fields[], int columns, int current);
 static bool pc_readdb_job_exp_sub(char *fields[], int columns, int current);
 static bool pc_readdb_job_basehpsp(char *fields[], int columns, int current);
 static bool pc_readdb_job_basehpsp(char *fields[], int columns, int current);
 static bool pc_readdb_job1(char *fields[], int columns, int current);
 static bool pc_readdb_job1(char *fields[], int columns, int current);
+static bool read_elemental_skilldb(char* str[], int columns, int current);
+static bool read_elementaldb(char* str[], int columns, int current);
 
 
 #endif /* CSV2YAML_HPP */
 #endif /* CSV2YAML_HPP */

+ 1 - 0
src/tool/yaml.hpp

@@ -42,6 +42,7 @@
 #include "../map/chat.hpp"
 #include "../map/chat.hpp"
 #include "../map/date.hpp"
 #include "../map/date.hpp"
 #include "../map/instance.hpp"
 #include "../map/instance.hpp"
+#include "../map/elemental.hpp"
 #include "../map/mercenary.hpp"
 #include "../map/mercenary.hpp"
 #include "../map/mob.hpp"
 #include "../map/mob.hpp"
 #include "../map/npc.hpp"
 #include "../map/npc.hpp"

+ 1 - 0
src/tool/yaml2sql.cpp

@@ -38,6 +38,7 @@
 #include "../map/channel.hpp"
 #include "../map/channel.hpp"
 #include "../map/chat.hpp"
 #include "../map/chat.hpp"
 #include "../map/date.hpp"
 #include "../map/date.hpp"
+#include "../map/elemental.hpp"
 #include "../map/instance.hpp"
 #include "../map/instance.hpp"
 #include "../map/mercenary.hpp"
 #include "../map/mercenary.hpp"
 #include "../map/mob.hpp"
 #include "../map/mob.hpp"