From 240f71cbce415c60da99285db8c41d19b78d4266 Mon Sep 17 00:00:00 2001 From: Aleos Date: Wed, 15 Feb 2023 14:42:51 -0500 Subject: [PATCH] Converts Homunculus Database to YAML (#4659) * Converts the Homunculus Database and Homunculus Skill Tree Database into one YAML flat file. * General cleanups and optimizations. * Includes CSV2YAML converter. Thanks to @Lemongrass3110 and @Atemo! Co-authored-by: Atemo --- db/homun_skill_tree.txt | 135 ---- db/homunculus_db.yml | 65 ++ db/import-tmpl/homun_skill_tree.txt | 15 - db/import-tmpl/homunculus_db.txt | 20 - db/import-tmpl/homunculus_db.yml | 57 ++ db/pre-re/homunculus_db.txt | 33 - db/pre-re/homunculus_db.yml | 623 +++++++++++++++ db/re/homunculus_db.txt | 33 - db/re/homunculus_db.yml | 948 +++++++++++++++++++++++ doc/yaml/db/homunculus_db.yml | 36 + src/map/atcommand.cpp | 4 +- src/map/clif.cpp | 2 +- src/map/homunculus.cpp | 1086 +++++++++++++++++---------- src/map/homunculus.hpp | 102 ++- src/map/itemdb.hpp | 1 + src/map/map-server.vcxproj | 3 +- src/map/script_constants.hpp | 23 + src/map/status.cpp | 41 +- src/tool/csv2yaml.cpp | 308 ++++++++ src/tool/csv2yaml.hpp | 5 + src/tool/yaml.hpp | 3 +- src/tool/yaml2sql.cpp | 1 + 22 files changed, 2869 insertions(+), 675 deletions(-) delete mode 100644 db/homun_skill_tree.txt create mode 100644 db/homunculus_db.yml delete mode 100644 db/import-tmpl/homun_skill_tree.txt delete mode 100644 db/import-tmpl/homunculus_db.txt create mode 100644 db/import-tmpl/homunculus_db.yml delete mode 100644 db/pre-re/homunculus_db.txt create mode 100644 db/pre-re/homunculus_db.yml delete mode 100644 db/re/homunculus_db.txt create mode 100644 db/re/homunculus_db.yml create mode 100644 doc/yaml/db/homunculus_db.yml diff --git a/db/homun_skill_tree.txt b/db/homun_skill_tree.txt deleted file mode 100644 index a36c73896c..0000000000 --- a/db/homun_skill_tree.txt +++ /dev/null @@ -1,135 +0,0 @@ -// Homunculus Skill Tree Database -// -// Structure of Database: -// Class,SkillID,MaxLv,NeedLevel,Prerequisite SkillID1,Prerequisite SkillLv1,PrereqSkillID2,PrereqSkillLv2,PrereqSkillID3,PrereqSkillLv3,PrereqSkillID4,PrereqSkillLv4,PrereqSkillID5,PrereqSkillLv5,IntimacyLvReq //SKILLNAME#Skill Name# -// -// 01. Class Homunculus ID. -// 02. SkillID Skill ID of the homunuculus skill. -// 03. MaxLv Maximum level of the homunuculus skill. -// 04. NeedLevel Homunculus level required for the skill to become available -// 05. Prerequisite SkillID Homunculus skill required for the skill to become available. -// 06. Prerequisite SkillLv Level of the required homunculus skill. -// ... -// 15. IntimacyLvReq Minimum level of intimacy to unlock skill. -// -// NOTE: MAX_PC_SKILL_REQUIRE (typically 5) ID/Lv pairs must be specified. - -//Lif -6001,8001,5,0,0,0,0,0,0,0,0,0,0,0,0 //HLIF_HEAL -6001,8002,5,0,8001,3,0,0,0,0,0,0,0,0,0 //HLIF_AVOID -6001,8003,5,0,8001,5,0,0,0,0,0,0,0,0,0 //HLIF_BRAIN -//Amistr -6002,8005,5,0,0,0,0,0,0,0,0,0,0,0,0 //HAMI_CASTLE -6002,8006,5,0,8005,5,0,0,0,0,0,0,0,0,0 //HAMI_DEFENCE -6002,8007,5,0,8006,3,0,0,0,0,0,0,0,0,0 //HAMI_SKIN -//Filir -6003,8009,5,0,0,0,0,0,0,0,0,0,0,0,0 //HFLI_MOON -6003,8010,5,0,8009,3,0,0,0,0,0,0,0,0,0 //HFLI_FLEET -6003,8011,5,0,8010,3,0,0,0,0,0,0,0,0,0 //HFLI_SPEED -//Vanilmirth -6004,8013,5,0,0,0,0,0,0,0,0,0,0,0,0 //HVAN_CAPRICE -6004,8014,5,0,8013,3,0,0,0,0,0,0,0,0,0 //HVAN_CHAOTIC -6004,8015,5,0,8013,5,0,0,0,0,0,0,0,0,0 //HVAN_INSTRUCT -//Lif2 -6005,8001,5,0,0,0,0,0,0,0,0,0,0,0,0 //HLIF_HEAL -6005,8002,5,0,8001,3,0,0,0,0,0,0,0,0,0 //HLIF_AVOID -6005,8003,5,0,8001,5,0,0,0,0,0,0,0,0,0 //HLIF_BRAIN -//Amistr2 -6006,8005,5,0,0,0,0,0,0,0,0,0,0,0,0 //HAMI_CASTLE -6006,8006,5,0,8005,5,0,0,0,0,0,0,0,0,0 //HAMI_DEFENCE -6006,8007,5,0,8006,3,0,0,0,0,0,0,0,0,0 //HAMI_SKIN -//Filir2 -6007,8009,5,0,0,0,0,0,0,0,0,0,0,0,0 //HFLI_MOON -6007,8010,5,0,8009,3,0,0,0,0,0,0,0,0,0 //HFLI_FLEET -6007,8011,5,0,8010,3,0,0,0,0,0,0,0,0,0 //HFLI_SPEED -//Vanilmirth2 -6008,8013,5,0,0,0,0,0,0,0,0,0,0,0,0 //HVAN_CAPRICE -6008,8014,5,0,8013,3,0,0,0,0,0,0,0,0,0 //HVAN_CHAOTIC -6008,8015,5,0,8013,5,0,0,0,0,0,0,0,0,0 //HVAN_INSTRUCT -//Lif_H -6009,8001,5,0,0,0,0,0,0,0,0,0,0,0,0 //HLIF_HEAL -6009,8002,5,0,8001,3,0,0,0,0,0,0,0,0,0 //HLIF_AVOID -6009,8003,5,0,8001,5,0,0,0,0,0,0,0,0,0 //HLIF_BRAIN -6009,8004,3,0,0,0,0,0,0,0,0,0,0,0,910 //HLIF_CHANGE -//Amistr_H -6010,8005,5,0,0,0,0,0,0,0,0,0,0,0,0 //HAMI_CASTLE -6010,8006,5,0,8005,5,0,0,0,0,0,0,0,0,0 //HAMI_DEFENCE -6010,8007,5,0,8006,3,0,0,0,0,0,0,0,0,0 //HAMI_SKIN -6010,8008,3,0,0,0,0,0,0,0,0,0,0,0,910 //HAMI_BLOODLUST -//Filir_H -6011,8009,5,0,0,0,0,0,0,0,0,0,0,0,0 //HFLI_MOON -6011,8010,5,0,8009,3,0,0,0,0,0,0,0,0,0 //HFLI_FLEET -6011,8011,5,0,8010,3,0,0,0,0,0,0,0,0,0 //HFLI_SPEED -6011,8012,3,0,0,0,0,0,0,0,0,0,0,0,910 //HFLI_SBR44 -//Vanilmirth_H -6012,8013,5,0,0,0,0,0,0,0,0,0,0,0,0 //HVAN_CAPRICE -6012,8014,5,0,8013,3,0,0,0,0,0,0,0,0,0 //HVAN_CHAOTIC -6012,8015,5,0,8013,5,0,0,0,0,0,0,0,0,0 //HVAN_INSTRUCT -6012,8016,3,0,0,0,0,0,0,0,0,0,0,0,910 //HVAN_EXPLOSION -//Lif2_H -6013,8001,5,0,0,0,0,0,0,0,0,0,0,0,0 //HLIF_HEAL -6013,8002,5,0,8001,3,0,0,0,0,0,0,0,0,0 //HLIF_AVOID -6013,8003,5,0,8001,5,0,0,0,0,0,0,0,0,0 //HLIF_BRAIN -6013,8004,3,0,0,0,0,0,0,0,0,0,0,0,910 //HLIF_CHANGE -//Amistr2_H -6014,8005,5,0,0,0,0,0,0,0,0,0,0,0,0 //HAMI_CASTLE -6014,8006,5,0,8005,5,0,0,0,0,0,0,0,0,0 //HAMI_DEFENCE -6014,8007,5,0,8006,3,0,0,0,0,0,0,0,0,0 //HAMI_SKIN -6014,8008,3,0,0,0,0,0,0,0,0,0,0,0,910 //HAMI_BLOODLUST -//Filir2_H -6015,8009,5,0,0,0,0,0,0,0,0,0,0,0,0 //HFLI_MOON -6015,8010,5,0,8009,3,0,0,0,0,0,0,0,0,0 //HFLI_FLEET -6015,8011,5,0,8010,3,0,0,0,0,0,0,0,0,0 //HFLI_SPEED -6015,8012,3,0,0,0,0,0,0,0,0,0,0,0,910 //HFLI_SBR44 -//Vanilmirth2_H -6016,8013,5,0,0,0,0,0,0,0,0,0,0,0,0 //HVAN_CAPRICE -6016,8014,5,0,8013,3,0,0,0,0,0,0,0,0,0 //HVAN_CHAOTIC -6016,8015,5,0,8013,5,0,0,0,0,0,0,0,0,0 //HVAN_INSTRUCT -6016,8016,3,0,0,0,0,0,0,0,0,0,0,0,910 //HVAN_EXPLOSION -//Eira -6048,8022,5,128,0,0,0,0,0,0,0,0,0,0,0 //MH_LIGHT_OF_REGENE -6048,8023,5,114,0,0,0,0,0,0,0,0,0,0,0 //MH_OVERED_BOOST -6048,8024,10,106,0,0,0,0,0,0,0,0,0,0,0 //MH_ERASER_CUTTER -6048,8025,10,121,0,0,0,0,0,0,0,0,0,0,0 //MH_XENO_SLASHER -6048,8026,5,137,0,0,0,0,0,0,0,0,0,0,0 //MH_SILENT_BREEZE -6048,8046,10,210,0,0,0,0,0,0,0,0,0,0,0 //MH_CLASSY_FLUTTER -6048,8047,10,215,0,0,0,0,0,0,0,0,0,0,0 //MH_TWISTER_CUTTER -6048,8048,10,230,0,0,0,0,0,0,0,0,0,0,0 //MH_ABSOLUTE_ZEPHYR -//Bayeri -6049,8031,10,105,0,0,0,0,0,0,0,0,0,0,0 //MH_STAHL_HORN -6049,8032,5,112,0,0,0,0,0,0,0,0,0,0,0 //MH_GOLDENE_FERSE -6049,8033,5,121,0,0,0,0,0,0,0,0,0,0,0 //MH_STEINWAND -6049,8034,10,138,0,0,0,0,0,0,0,0,0,0,0 //MH_HEILIGE_STANGE -6049,8035,5,130,0,0,0,0,0,0,0,0,0,0,0 //MH_ANGRIFFS_MODUS -6049,8055,10,210,0,0,0,0,0,0,0,0,0,0,0 //MH_LICHT_GEHORN -6049,8056,10,215,0,0,0,0,0,0,0,0,0,0,0 //MH_GLANZEN_SPIES -6049,8057,10,230,0,0,0,0,0,0,0,0,0,0,0 //MH_HEILIGE_PFERD -6049,8058,10,230,0,0,0,0,0,0,0,0,0,0,0 //MH_GOLDENE_TONE -//Sera -6050,8018,5,132,0,0,0,0,0,0,0,0,0,0,0 //MH_SUMMON_LEGION -6050,8019,10,105,0,0,0,0,0,0,0,0,0,0,0 //MH_NEEDLE_OF_PARALYZE -6050,8020,5,116,0,0,0,0,0,0,0,0,0,0,0 //MH_POISON_MIST -6050,8021,10,123,0,0,0,0,0,0,0,0,0,0,0 //MH_PAIN_KILLER -6050,8052,10,210,0,0,0,0,0,0,0,0,0,0,0 //MH_POLISHING_NEEDLE -6050,8053,10,215,0,0,0,0,0,0,0,0,0,0,0 //MH_TOXIN_OF_MANDARA -6050,8054,10,230,0,0,0,0,0,0,0,0,0,0,0 //MH_NEEDLE_STINGER -//Dieter -6051,8039,5,122,0,0,0,0,0,0,0,0,0,0,0 //MH_MAGMA_FLOW -6051,8040,5,116,0,0,0,0,0,0,0,0,0,0,0 //MH_GRANITIC_ARMOR -6051,8041,10,109,0,0,0,0,0,0,0,0,0,0,0 //MH_LAVA_SLIDE -6051,8042,10,131,0,0,0,0,0,0,0,0,0,0,0 //MH_PYROCLASTIC -6051,8043,5,102,0,0,0,0,0,0,0,0,0,0,0 //MH_VOLCANIC_ASH -6051,8059,10,210,0,0,0,0,0,0,0,0,0,0,0 //MH_BLAZING_LAVA -6051,8044,10,215,0,0,0,0,0,0,0,0,0,0,0 //MH_BLAST_FORGE -6051,8045,10,230,0,0,0,0,0,0,0,0,0,0,0 //MH_TEMPERING -//Elanor -6052,8027,1,100,0,0,0,0,0,0,0,0,0,0,0 //MH_STYLE_CHANGE -6052,8028,5,100,0,0,0,0,0,0,0,0,0,0,0 //MH_SONIC_CRAW -6052,8029,10,114,0,0,0,0,0,0,0,0,0,0,0 //MH_SILVERVEIN_RUSH -6052,8030,10,128,0,0,0,0,0,0,0,0,0,0,0 //MH_MIDNIGHT_FRENZY -6052,8036,5,100,0,0,0,0,0,0,0,0,0,0,0 //MH_TINDER_BREAKER -6052,8037,5,112,0,0,0,0,0,0,0,0,0,0,0 //MH_CBC -6052,8038,5,133,0,0,0,0,0,0,0,0,0,0,0 //MH_EQC -6052,8049,10,210,0,0,0,0,0,0,0,0,0,0,0 //MH_BRUSHUP_CLAW -6052,8050,10,215,0,0,0,0,0,0,0,0,0,0,0 //MH_BLAZING_AND_FURIOUS -6052,8051,10,230,0,0,0,0,0,0,0,0,0,0,0 //MH_THE_ONE_FIGHTER_RISES diff --git a/db/homunculus_db.yml b/db/homunculus_db.yml new file mode 100644 index 0000000000..5646cd9462 --- /dev/null +++ b/db/homunculus_db.yml @@ -0,0 +1,65 @@ +# This file is a part of rAthena. +# Copyright(C) 2023 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 . +# +########################################################################### +# Homunculus Database +########################################################################### +# +# Homunculus Settings +# +########################################################################### +# - BaseClass Base class. +# Name Name of homunculus. +# EvolutionClass Evolution class. +# Food Homunculus food item. (Default: Pet_Food) +# HungryDelay Time interval in milliseconds after which the hunger value is altered. (Default: 60000) +# Race Race. (Default: Demihuman) +# Element Element. (Default: Neutral) +# Size Size. (Default: Small) +# EvolutionSize Evolution size. (Default: Medium) +# AttackDelay Base ASPD. (Default: 700) +# Status: Homunculus stats. +# - Type Type of status. +# Base Base value of this status. (Default: 1) +# GrowthMinimum Minimum growth of this status. (Default: 0) +# GrowthMaximum Maximum growth of this status. (Default: 0) +# EvolutionMinimum Minimum evolution growth of this status. Only applies for homunculus that can evolve. (Default: 0) +# EvolutionMaximum Maximum evolution growth of this status. Only applies for homunculus that can evolve. (Default: 0) +# SkillTree: Skill tree. +# - Skill Skill name. +# Clear True to remove the given skill name. (Optional) +# MaxLevel Maximum level of skill. +# RequiredLevel Required base level of homunculus to learn. (Default: 0) +# RequiredIntimacy Required intimacy of homunculus to learn. (Default: 0) +# RequireEvolution Require the homunculus to be evolved to be available. (Default: false) +# Required: Prerequisite skills. (Default: null) +# - Skill Prerequisite skill name. +# Level Level of prerequisite skill. +# Clear True to remove the given prerequisite skill name. (Optional) +########################################################################### + +Header: + Type: HOMUNCULUS_DB + Version: 1 + +Footer: + Imports: + - Path: db/pre-re/homunculus_db.yml + Mode: Prerenewal + - Path: db/re/homunculus_db.yml + Mode: Renewal + - Path: db/import/homunculus_db.yml diff --git a/db/import-tmpl/homun_skill_tree.txt b/db/import-tmpl/homun_skill_tree.txt deleted file mode 100644 index 1005a3ac73..0000000000 --- a/db/import-tmpl/homun_skill_tree.txt +++ /dev/null @@ -1,15 +0,0 @@ -// Homunculus Skill Tree Database -// -// Structure of Database: -// Class,SkillID,MaxLv,NeedLevel,Prerequisite SkillID1,Prerequisite SkillLv1,PrereqSkillID2,PrereqSkillLv2,PrereqSkillID3,PrereqSkillLv3,PrereqSkillID4,PrereqSkillLv4,PrereqSkillID5,PrereqSkillLv5,IntimacyLvReq //SKILLNAME#Skill Name# -// -// 01. Class Homunculus ID. -// 02. SkillID Skill ID of the homunuculus skill. -// 03. MaxLv Maximum level of the homunuculus skill. -// 04. NeedLevel Homunculus level required for the skill to become available -// 05. Prerequisite SkillID Homunculus skill required for the skill to become available. -// 06. Prerequisite SkillLv Level of the required homunculus skill. -// ... -// 15. IntimacyLvReq Minimum level of intimacy to unlock skill. -// -// NOTE: MAX_PC_SKILL_REQUIRE (typically 5) ID/Lv pairs must be specified. diff --git a/db/import-tmpl/homunculus_db.txt b/db/import-tmpl/homunculus_db.txt deleted file mode 100644 index 66cff6cc0b..0000000000 --- a/db/import-tmpl/homunculus_db.txt +++ /dev/null @@ -1,20 +0,0 @@ -// Homunculus Database -// -// Structure of Database: -// Class,EvoClass,Name,FoodID,HungryDelay,BaseSize,EvoSize,Race,Element,bASPD,bHP,bSP,bSTR,bAGI,bVIT,bINT,bDEX,bLUK,gnHP,gxHP,gnSP,gxSP,gnSTR,gxSTR,gnAGI,gxAGI,gnVIT,gxVIT,gnINT,gxINT,gnDEX,gxDEX,gnLUK,gxLUK,enHP,exHP,enSP,exSP,enSTR,exSTR,enAGI,exAGI,enVIT,exVIT,enINT,exINT,enDEX,exDEX,enLUK,exLUK -// -// 01. Class Homunculus ID. -// 02. EvoClass Homunculus ID of the evolved version. -// 03. Name Name of the homunculus. -// 04. FoodID Item ID of the homunuclus food. -// 05. HungryDelay Time interval in milliseconds after which the homunculus' hunger value is altered. -// 06. BaseSize Size of the base homunculus class (0 = small, 1 = normal, 2 = large). -// 07. EvoSize Size of the evolved homunculus class (0 = small, 1 = normal, 2 = large). -// 08. Race Race of the homunculus (0 = formless, 1 = undead, 2 = brute, 3 = plant, 4 = insect, 5 = fish, 6 = demon, 7 = demi-human, 8 = angel, 9 = dragon). -// 09. Element Element of the homunculus (0 = neutral, 1 = water, 2 = earth, 3 = fire, 4 = wind, 5 = poison, 6 = holy, 7 = dark, 8 = ghost, 9 = undead). -// The element level is always 1. -// ... -// -// Legend: b: base, gn: growth min, gx: growth max, en: evolution min, ex: evolution max -// NOTE: Only the growth values are in a 1/10 scale, the other stats are 1/1 (eg: 5 gmAGI means 0.5 agi) - diff --git a/db/import-tmpl/homunculus_db.yml b/db/import-tmpl/homunculus_db.yml new file mode 100644 index 0000000000..ac4f14b302 --- /dev/null +++ b/db/import-tmpl/homunculus_db.yml @@ -0,0 +1,57 @@ +# This file is a part of rAthena. +# Copyright(C) 2023 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 . +# +########################################################################### +# Homunculus Database +########################################################################### +# +# Homunculus Settings +# +########################################################################### +# - BaseClass Base class. +# Name Name of homunculus. +# EvolutionClass Evolution class. +# Food Homunculus food item. (Default: Pet_Food) +# HungryDelay Time interval in milliseconds after which the hunger value is altered. (Default: 60000) +# Race Race. (Default: Demihuman) +# Element Element. (Default: Neutral) +# Size Size. (Default: Small) +# EvolutionSize Evolution size. (Default: Medium) +# AttackDelay Base ASPD. (Default: 700) +# Status: Homunculus stats. +# - Type Type of status. +# Base Base value of this status. (Default: 1) +# GrowthMinimum Minimum growth of this status. (Default: 0) +# GrowthMaximum Maximum growth of this status. (Default: 0) +# EvolutionMinimum Minimum evolution growth of this status. Only applies for homunculus that can evolve. (Default: 0) +# EvolutionMaximum Maximum evolution growth of this status. Only applies for homunculus that can evolve. (Default: 0) +# SkillTree: Skill tree. +# - Skill Skill name. +# Clear True to remove the given skill name. (Optional) +# MaxLevel Maximum level of skill. +# RequiredLevel Required base level of homunculus to learn. (Default: 0) +# RequiredIntimacy Required intimacy of homunculus to learn. (Default: 0) +# RequireEvolution Require the homunculus to be evolved to be available. (Default: false) +# Required: Prerequisite skills. (Default: null) +# - Skill Prerequisite skill name. +# Level Level of prerequisite skill. +# Clear True to remove the given prerequisite skill name. (Optional) +########################################################################### + +Header: + Type: HOMUNCULUS_DB + Version: 1 diff --git a/db/pre-re/homunculus_db.txt b/db/pre-re/homunculus_db.txt deleted file mode 100644 index d099db1738..0000000000 --- a/db/pre-re/homunculus_db.txt +++ /dev/null @@ -1,33 +0,0 @@ -// Homunculus Database -// -// Structure of Database: -// Class,EvoClass,Name,FoodID,HungryDelay,BaseSize,EvoSize,Race,Element,bASPD,bHP,bSP,bSTR,bAGI,bVIT,bINT,bDEX,bLUK,gnHP,gxHP,gnSP,gxSP,gnSTR,gxSTR,gnAGI,gxAGI,gnVIT,gxVIT,gnINT,gxINT,gnDEX,gxDEX,gnLUK,gxLUK,enHP,exHP,enSP,exSP,enSTR,exSTR,enAGI,exAGI,enVIT,exVIT,enINT,exINT,enDEX,exDEX,enLUK,exLUK -// -// 01. Class Homunculus ID. -// 02. EvoClass Homunculus ID of the evolved version. -// 03. Name Name of the homunculus. -// 04. FoodID Item ID of the homunuclus food. -// 05. HungryDelay Time interval in milliseconds after which the homunculus' hunger value is altered. -// 06. BaseSize Size of the base homunculus class (0 = small, 1 = normal, 2 = large). -// 07. EvoSize Size of the evolved homunculus class (0 = small, 1 = normal, 2 = large). -// 08. Race Race of the homunculus (0 = formless, 1 = undead, 2 = brute, 3 = plant, 4 = insect, 5 = fish, 6 = demon, 7 = demi-human, 8 = angel, 9 = dragon). -// 09. Element Element of the homunculus (0 = neutral, 1 = water, 2 = earth, 3 = fire, 4 = wind, 5 = poison, 6 = holy, 7 = dark, 8 = ghost, 9 = undead). -// The element level is always 1. -// ... -// -// Legend: b: base, gn: growth min, gx: growth max, en: evolution min, ex: evolution max -// NOTE: Only the growth values are in a 1/10 scale, the other stats are 1/1 (eg: 5 gmAGI means 0.5 agi) - -6001,6009,Lif,537,60000,0,1,7,0,700,150,40,17,20,15,35,24,12,60,100,4,9,5,19,5,19,5,19,4,20,6,20,6,20,1,10,10,20,1,5,1,4,1,5,4,10,1,10,1,3 -6002,6010,Amistr,912,60000,0,1,2,0,700,320,10,20,17,35,11,24,12,80,130,1,4,8,20,4,20,4,20,1,10,3,19,3,19,10,20,1,10,1,10,1,5,4,10,1,3,1,4,1,5 -6003,6011,Filir,910,60000,0,1,2,0,700,90,25,29,35,9,8,30,9,45,75,3,6,4,20,8,20,1,10,3,19,4,20,3,19,5,15,5,15,4,10,1,10,1,3,1,4,1,5,1,5 -6004,6012,Vanilmirth,911,60000,0,1,0,0,700,80,11,11,11,11,11,11,11,30,150,0,7,1,30,1,30,1,30,1,30,1,30,1,30,1,30,1,30,1,10,1,10,1,10,1,10,1,10,1,10 -6005,6013,Lif,537,60000,0,1,7,0,700,150,40,17,20,15,35,24,12,60,100,4,9,5,19,5,19,5,19,4,20,6,20,6,20,1,10,10,20,1,5,1,4,1,5,4,10,1,10,1,3 -6006,6014,Amistr,912,60000,0,1,2,0,700,320,10,20,17,35,11,24,12,80,130,1,4,8,20,4,20,4,20,1,10,3,19,3,19,10,20,1,10,1,10,1,5,4,10,1,3,1,4,1,5 -6007,6015,Filir,910,60000,0,1,2,0,700,90,25,29,35,9,8,30,9,45,75,3,6,4,20,8,20,1,10,3,19,4,20,3,19,5,15,5,15,4,10,1,10,1,3,1,4,1,5,1,5 -6008,6016,Vanilmirth,911,60000,0,1,0,0,700,80,11,11,11,11,11,11,11,30,150,0,7,1,30,1,30,1,30,1,30,1,30,1,30,1,30,1,30,1,10,1,10,1,10,1,10,1,10,1,10 -6048,6048,Eira,6098,60000,1,1,7,0,700,150,40,17,20,15,35,24,12,60,100,4,9,5,19,5,19,5,19,4,20,6,20,6,20,1,10,10,20,1,5,1,4,1,5,4,10,1,10,1,3 -6049,6049,Bayeri,6112,60000,1,1,2,0,700,320,10,20,17,35,11,24,12,80,130,1,4,8,20,4,20,4,20,1,10,3,19,3,19,10,20,1,10,1,10,1,5,4,10,1,3,1,4,1,5 -6050,6050,Sera,6108,60000,1,1,4,0,700,90,25,29,35,9,8,30,9,45,75,3,6,4,20,8,20,1,10,3,19,4,20,3,19,5,15,5,15,4,10,1,10,1,3,1,4,1,5,1,5 -6051,6051,Dieter,6104,60000,1,1,0,0,700,80,11,11,11,11,11,11,11,30,150,0,7,1,30,1,30,1,30,1,30,1,30,1,30,1,30,1,30,1,10,1,10,1,10,1,10,1,10,1,10 -6052,6052,Eleanor,6115,60000,1,1,2,0,700,320,10,20,17,35,11,24,12,80,130,1,4,8,20,4,20,4,20,1,10,3,19,3,19,10,20,1,10,1,10,1,5,4,10,1,3,1,4,1,5 diff --git a/db/pre-re/homunculus_db.yml b/db/pre-re/homunculus_db.yml new file mode 100644 index 0000000000..b5060a36d5 --- /dev/null +++ b/db/pre-re/homunculus_db.yml @@ -0,0 +1,623 @@ +# This file is a part of rAthena. +# Copyright(C) 2023 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 . +# +########################################################################### +# Homunculus Database +########################################################################### +# +# Homunculus Settings +# +########################################################################### +# - BaseClass Base class. +# Name Name of homunculus. +# EvolutionClass Evolution class. +# Food Homunculus food item. (Default: Pet_Food) +# HungryDelay Time interval in milliseconds after which the hunger value is altered. (Default: 60000) +# Race Race. (Default: Demihuman) +# Element Element. (Default: Neutral) +# Size Size. (Default: Small) +# EvolutionSize Evolution size. (Default: Medium) +# AttackDelay Base ASPD. (Default: 700) +# Status: Homunculus stats. +# - Type Type of status. +# Base Base value of this status. (Default: 1) +# GrowthMinimum Minimum growth of this status. (Default: 0) +# GrowthMaximum Maximum growth of this status. (Default: 0) +# EvolutionMinimum Minimum evolution growth of this status. Only applies for homunculus that can evolve. (Default: 0) +# EvolutionMaximum Maximum evolution growth of this status. Only applies for homunculus that can evolve. (Default: 0) +# SkillTree: Skill tree. +# - Skill Skill name. +# Clear True to remove the given skill name. (Optional) +# MaxLevel Maximum level of skill. +# RequiredLevel Required base level of homunculus to learn. (Default: 0) +# RequiredIntimacy Required intimacy of homunculus to learn. (Default: 0) +# RequireEvolution Require the homunculus to be evolved to be available. (Default: false) +# Required: Prerequisite skills. (Default: null) +# - Skill Prerequisite skill name. +# Level Level of prerequisite skill. +# Clear True to remove the given prerequisite skill name. (Optional) +########################################################################### + +Header: + Type: HOMUNCULUS_DB + Version: 1 + +Body: + - Class: Lif + Name: Lif + EvolutionClass: Lif_H + Status: + - Type: Hp + Base: 150 + GrowthMinimum: 60 + GrowthMaximum: 100 + EvolutionMinimum: 1 + EvolutionMaximum: 10 + - Type: Sp + Base: 40 + GrowthMinimum: 4 + GrowthMaximum: 9 + EvolutionMinimum: 10 + EvolutionMaximum: 20 + - Type: Str + Base: 17 + GrowthMinimum: 5 + GrowthMaximum: 19 + EvolutionMinimum: 1 + EvolutionMaximum: 5 + - Type: Agi + Base: 20 + GrowthMinimum: 5 + GrowthMaximum: 19 + EvolutionMinimum: 1 + EvolutionMaximum: 4 + - Type: Vit + Base: 15 + GrowthMinimum: 5 + GrowthMaximum: 19 + EvolutionMinimum: 1 + EvolutionMaximum: 5 + - Type: Int + Base: 35 + GrowthMinimum: 4 + GrowthMaximum: 20 + EvolutionMinimum: 4 + EvolutionMaximum: 10 + - Type: Dex + Base: 24 + GrowthMinimum: 6 + GrowthMaximum: 20 + EvolutionMinimum: 1 + EvolutionMaximum: 10 + - Type: Luk + Base: 12 + GrowthMinimum: 6 + GrowthMaximum: 20 + EvolutionMinimum: 1 + EvolutionMaximum: 3 + SkillTree: + - Skill: HLIF_HEAL + MaxLevel: 5 + - Skill: HLIF_AVOID + MaxLevel: 5 + Required: + - Skill: HLIF_HEAL + Level: 3 + - Skill: HLIF_BRAIN + MaxLevel: 5 + Required: + - Skill: HLIF_HEAL + Level: 5 + - Skill: HLIF_CHANGE + MaxLevel: 3 + RequiredIntimacy: 910 + RequireEvolution: true + - Class: Amistr + Name: Amistr + EvolutionClass: Amistr_H + Food: Zargon + Race: Brute + Status: + - Type: Hp + Base: 320 + GrowthMinimum: 80 + GrowthMaximum: 130 + EvolutionMinimum: 10 + EvolutionMaximum: 20 + - Type: Sp + Base: 10 + GrowthMinimum: 1 + GrowthMaximum: 4 + EvolutionMinimum: 1 + EvolutionMaximum: 10 + - Type: Str + Base: 20 + GrowthMinimum: 8 + GrowthMaximum: 20 + EvolutionMinimum: 1 + EvolutionMaximum: 10 + - Type: Agi + Base: 17 + GrowthMinimum: 4 + GrowthMaximum: 20 + EvolutionMinimum: 1 + EvolutionMaximum: 5 + - Type: Vit + Base: 35 + GrowthMinimum: 4 + GrowthMaximum: 20 + EvolutionMinimum: 4 + EvolutionMaximum: 10 + - Type: Int + Base: 11 + GrowthMinimum: 1 + GrowthMaximum: 10 + EvolutionMinimum: 1 + EvolutionMaximum: 3 + - Type: Dex + Base: 24 + GrowthMinimum: 3 + GrowthMaximum: 19 + EvolutionMinimum: 1 + EvolutionMaximum: 4 + - Type: Luk + Base: 12 + GrowthMinimum: 3 + GrowthMaximum: 19 + EvolutionMinimum: 1 + EvolutionMaximum: 5 + SkillTree: + - Skill: HAMI_CASTLE + MaxLevel: 5 + - Skill: HAMI_DEFENCE + MaxLevel: 5 + Required: + - Skill: HAMI_CASTLE + Level: 5 + - Skill: HAMI_SKIN + MaxLevel: 5 + Required: + - Skill: HAMI_DEFENCE + Level: 3 + - Skill: HAMI_BLOODLUST + MaxLevel: 3 + RequiredIntimacy: 910 + RequireEvolution: true + - Class: Filir + Name: Filir + EvolutionClass: Filir_H + Food: Garlet + Race: Brute + Status: + - Type: Hp + Base: 90 + GrowthMinimum: 45 + GrowthMaximum: 75 + EvolutionMinimum: 5 + EvolutionMaximum: 15 + - Type: Sp + Base: 25 + GrowthMinimum: 3 + GrowthMaximum: 6 + EvolutionMinimum: 5 + EvolutionMaximum: 15 + - Type: Str + Base: 29 + GrowthMinimum: 4 + GrowthMaximum: 20 + EvolutionMinimum: 4 + EvolutionMaximum: 10 + - Type: Agi + Base: 35 + GrowthMinimum: 8 + GrowthMaximum: 20 + EvolutionMinimum: 1 + EvolutionMaximum: 10 + - Type: Vit + Base: 9 + GrowthMinimum: 1 + GrowthMaximum: 10 + EvolutionMinimum: 1 + EvolutionMaximum: 3 + - Type: Int + Base: 8 + GrowthMinimum: 3 + GrowthMaximum: 19 + EvolutionMinimum: 1 + EvolutionMaximum: 4 + - Type: Dex + Base: 30 + GrowthMinimum: 4 + GrowthMaximum: 20 + EvolutionMinimum: 1 + EvolutionMaximum: 5 + - Type: Luk + Base: 9 + GrowthMinimum: 3 + GrowthMaximum: 19 + EvolutionMinimum: 1 + EvolutionMaximum: 5 + SkillTree: + - Skill: HFLI_MOON + MaxLevel: 5 + - Skill: HFLI_FLEET + MaxLevel: 5 + Required: + - Skill: HFLI_MOON + Level: 3 + - Skill: HFLI_SPEED + MaxLevel: 5 + Required: + - Skill: HFLI_FLEET + Level: 3 + - Skill: HFLI_SBR44 + MaxLevel: 3 + RequiredIntimacy: 910 + RequireEvolution: true + - Class: Vanilmirth + Name: Vanilmirth + EvolutionClass: Vanilmirth_H + Food: Scell + Race: Formless + Status: + - Type: Hp + Base: 80 + GrowthMinimum: 30 + GrowthMaximum: 150 + EvolutionMinimum: 1 + EvolutionMaximum: 30 + - Type: Sp + Base: 11 + GrowthMinimum: 0 + GrowthMaximum: 7 + EvolutionMinimum: 1 + EvolutionMaximum: 30 + - Type: Str + Base: 11 + GrowthMinimum: 1 + GrowthMaximum: 30 + EvolutionMinimum: 1 + EvolutionMaximum: 10 + - Type: Agi + Base: 11 + GrowthMinimum: 1 + GrowthMaximum: 30 + EvolutionMinimum: 1 + EvolutionMaximum: 10 + - Type: Vit + Base: 11 + GrowthMinimum: 1 + GrowthMaximum: 30 + EvolutionMinimum: 1 + EvolutionMaximum: 10 + - Type: Int + Base: 11 + GrowthMinimum: 1 + GrowthMaximum: 30 + EvolutionMinimum: 1 + EvolutionMaximum: 10 + - Type: Dex + Base: 11 + GrowthMinimum: 1 + GrowthMaximum: 30 + EvolutionMinimum: 1 + EvolutionMaximum: 10 + - Type: Luk + Base: 11 + GrowthMinimum: 1 + GrowthMaximum: 30 + EvolutionMinimum: 1 + EvolutionMaximum: 10 + SkillTree: + - Skill: HVAN_CAPRICE + MaxLevel: 5 + - Skill: HVAN_CHAOTIC + MaxLevel: 5 + Required: + - Skill: HVAN_CAPRICE + Level: 3 + - Skill: HVAN_INSTRUCT + MaxLevel: 5 + Required: + - Skill: HVAN_CAPRICE + Level: 5 + - Skill: HVAN_EXPLOSION + MaxLevel: 3 + RequiredIntimacy: 910 + RequireEvolution: true + - Class: Lif2 + Name: Lif + EvolutionClass: Lif_H2 + Status: + - Type: Hp + Base: 150 + GrowthMinimum: 60 + GrowthMaximum: 100 + EvolutionMinimum: 1 + EvolutionMaximum: 10 + - Type: Sp + Base: 40 + GrowthMinimum: 4 + GrowthMaximum: 9 + EvolutionMinimum: 10 + EvolutionMaximum: 20 + - Type: Str + Base: 17 + GrowthMinimum: 5 + GrowthMaximum: 19 + EvolutionMinimum: 1 + EvolutionMaximum: 5 + - Type: Agi + Base: 20 + GrowthMinimum: 5 + GrowthMaximum: 19 + EvolutionMinimum: 1 + EvolutionMaximum: 4 + - Type: Vit + Base: 15 + GrowthMinimum: 5 + GrowthMaximum: 19 + EvolutionMinimum: 1 + EvolutionMaximum: 5 + - Type: Int + Base: 35 + GrowthMinimum: 4 + GrowthMaximum: 20 + EvolutionMinimum: 4 + EvolutionMaximum: 10 + - Type: Dex + Base: 24 + GrowthMinimum: 6 + GrowthMaximum: 20 + EvolutionMinimum: 1 + EvolutionMaximum: 10 + - Type: Luk + Base: 12 + GrowthMinimum: 6 + GrowthMaximum: 20 + EvolutionMinimum: 1 + EvolutionMaximum: 3 + SkillTree: + - Skill: HLIF_HEAL + MaxLevel: 5 + - Skill: HLIF_AVOID + MaxLevel: 5 + Required: + - Skill: HLIF_HEAL + Level: 3 + - Skill: HLIF_BRAIN + MaxLevel: 5 + Required: + - Skill: HLIF_HEAL + Level: 5 + - Skill: HLIF_CHANGE + MaxLevel: 3 + RequiredIntimacy: 910 + RequireEvolution: true + - Class: Amistr2 + Name: Amistr + EvolutionClass: Amistr_H2 + Food: Zargon + Race: Brute + Status: + - Type: Hp + Base: 320 + GrowthMinimum: 80 + GrowthMaximum: 130 + EvolutionMinimum: 10 + EvolutionMaximum: 20 + - Type: Sp + Base: 10 + GrowthMinimum: 1 + GrowthMaximum: 4 + EvolutionMinimum: 1 + EvolutionMaximum: 10 + - Type: Str + Base: 20 + GrowthMinimum: 8 + GrowthMaximum: 20 + EvolutionMinimum: 1 + EvolutionMaximum: 10 + - Type: Agi + Base: 17 + GrowthMinimum: 4 + GrowthMaximum: 20 + EvolutionMinimum: 1 + EvolutionMaximum: 5 + - Type: Vit + Base: 35 + GrowthMinimum: 4 + GrowthMaximum: 20 + EvolutionMinimum: 4 + EvolutionMaximum: 10 + - Type: Int + Base: 11 + GrowthMinimum: 1 + GrowthMaximum: 10 + EvolutionMinimum: 1 + EvolutionMaximum: 3 + - Type: Dex + Base: 24 + GrowthMinimum: 3 + GrowthMaximum: 19 + EvolutionMinimum: 1 + EvolutionMaximum: 4 + - Type: Luk + Base: 12 + GrowthMinimum: 3 + GrowthMaximum: 19 + EvolutionMinimum: 1 + EvolutionMaximum: 5 + SkillTree: + - Skill: HAMI_CASTLE + MaxLevel: 5 + - Skill: HAMI_DEFENCE + MaxLevel: 5 + Required: + - Skill: HAMI_CASTLE + Level: 5 + - Skill: HAMI_SKIN + MaxLevel: 5 + Required: + - Skill: HAMI_DEFENCE + Level: 3 + - Skill: HAMI_BLOODLUST + MaxLevel: 3 + RequiredIntimacy: 910 + RequireEvolution: true + - Class: Filir2 + Name: Filir + EvolutionClass: Filir_H2 + Food: Garlet + Race: Brute + Status: + - Type: Hp + Base: 90 + GrowthMinimum: 45 + GrowthMaximum: 75 + EvolutionMinimum: 5 + EvolutionMaximum: 15 + - Type: Sp + Base: 25 + GrowthMinimum: 3 + GrowthMaximum: 6 + EvolutionMinimum: 5 + EvolutionMaximum: 15 + - Type: Str + Base: 29 + GrowthMinimum: 4 + GrowthMaximum: 20 + EvolutionMinimum: 4 + EvolutionMaximum: 10 + - Type: Agi + Base: 35 + GrowthMinimum: 8 + GrowthMaximum: 20 + EvolutionMinimum: 1 + EvolutionMaximum: 10 + - Type: Vit + Base: 9 + GrowthMinimum: 1 + GrowthMaximum: 10 + EvolutionMinimum: 1 + EvolutionMaximum: 3 + - Type: Int + Base: 8 + GrowthMinimum: 3 + GrowthMaximum: 19 + EvolutionMinimum: 1 + EvolutionMaximum: 4 + - Type: Dex + Base: 30 + GrowthMinimum: 4 + GrowthMaximum: 20 + EvolutionMinimum: 1 + EvolutionMaximum: 5 + - Type: Luk + Base: 9 + GrowthMinimum: 3 + GrowthMaximum: 19 + EvolutionMinimum: 1 + EvolutionMaximum: 5 + SkillTree: + - Skill: HFLI_MOON + MaxLevel: 5 + - Skill: HFLI_FLEET + MaxLevel: 5 + Required: + - Skill: HFLI_MOON + Level: 3 + - Skill: HFLI_SPEED + MaxLevel: 5 + Required: + - Skill: HFLI_FLEET + Level: 3 + - Skill: HFLI_SBR44 + MaxLevel: 3 + RequiredIntimacy: 910 + RequireEvolution: true + - Class: Vanilmirth2 + Name: Vanilmirth + EvolutionClass: Vanilmirth_H2 + Food: Scell + Race: Formless + Status: + - Type: Hp + Base: 80 + GrowthMinimum: 30 + GrowthMaximum: 150 + EvolutionMinimum: 1 + EvolutionMaximum: 30 + - Type: Sp + Base: 11 + GrowthMinimum: 0 + GrowthMaximum: 7 + EvolutionMinimum: 1 + EvolutionMaximum: 30 + - Type: Str + Base: 11 + GrowthMinimum: 1 + GrowthMaximum: 30 + EvolutionMinimum: 1 + EvolutionMaximum: 10 + - Type: Agi + Base: 11 + GrowthMinimum: 1 + GrowthMaximum: 30 + EvolutionMinimum: 1 + EvolutionMaximum: 10 + - Type: Vit + Base: 11 + GrowthMinimum: 1 + GrowthMaximum: 30 + EvolutionMinimum: 1 + EvolutionMaximum: 10 + - Type: Int + Base: 11 + GrowthMinimum: 1 + GrowthMaximum: 30 + EvolutionMinimum: 1 + EvolutionMaximum: 10 + - Type: Dex + Base: 11 + GrowthMinimum: 1 + GrowthMaximum: 30 + EvolutionMinimum: 1 + EvolutionMaximum: 10 + - Type: Luk + Base: 11 + GrowthMinimum: 1 + GrowthMaximum: 30 + EvolutionMinimum: 1 + EvolutionMaximum: 10 + SkillTree: + - Skill: HVAN_CAPRICE + MaxLevel: 5 + - Skill: HVAN_CHAOTIC + MaxLevel: 5 + Required: + - Skill: HVAN_CAPRICE + Level: 3 + - Skill: HVAN_INSTRUCT + MaxLevel: 5 + Required: + - Skill: HVAN_CAPRICE + Level: 5 + - Skill: HVAN_EXPLOSION + MaxLevel: 3 + RequiredIntimacy: 910 + RequireEvolution: true diff --git a/db/re/homunculus_db.txt b/db/re/homunculus_db.txt deleted file mode 100644 index d0be7c5a67..0000000000 --- a/db/re/homunculus_db.txt +++ /dev/null @@ -1,33 +0,0 @@ -// Homunculus Database -// -// Structure of Database: -// Class,EvoClass,Name,FoodID,HungryDelay,BaseSize,EvoSize,Race,Element,bASPD,bHP,bSP,bSTR,bAGI,bVIT,bINT,bDEX,bLUK,gnHP,gxHP,gnSP,gxSP,gnSTR,gxSTR,gnAGI,gxAGI,gnVIT,gxVIT,gnINT,gxINT,gnDEX,gxDEX,gnLUK,gxLUK,enHP,exHP,enSP,exSP,enSTR,exSTR,enAGI,exAGI,enVIT,exVIT,enINT,exINT,enDEX,exDEX,enLUK,exLUK -// -// 01. Class Homunculus ID. -// 02. EvoClass Homunculus ID of the evolved version. -// 03. Name Name of the homunculus. -// 04. FoodID Item ID of the homunuclus food. -// 05. HungryDelay Time interval in milliseconds after which the homunculus' hunger value is altered. -// 06. BaseSize Size of the base homunculus class (0 = small, 1 = normal, 2 = large). -// 07. EvoSize Size of the evolved homunculus class (0 = small, 1 = normal, 2 = large). -// 08. Race Race of the homunculus (0 = formless, 1 = undead, 2 = brute, 3 = plant, 4 = insect, 5 = fish, 6 = demon, 7 = demi-human, 8 = angel, 9 = dragon). -// 09. Element Element of the homunculus (0 = neutral, 1 = water, 2 = earth, 3 = fire, 4 = wind, 5 = poison, 6 = holy, 7 = dark, 8 = ghost, 9 = undead). -// The element level is always 1. -// ... -// -// Legend: b: base, gn: growth min, gx: growth max, en: evolution min, ex: evolution max -// NOTE: Only the growth values are in a 1/10 scale, the other stats are 1/1 (eg: 5 gmAGI means 0.5 agi) - -6001,6009,Lif,537,60000,0,1,7,0,700,150,40,17,20,15,35,24,12,60,100,4,9,5,19,5,19,5,19,4,20,6,20,6,20,800,2400,220,480,10,30,10,30,20,40,30,50,20,50,10,30 -6002,6010,Amistr,912,60000,0,1,2,0,700,320,10,20,17,35,11,24,12,80,130,1,4,8,20,4,20,4,20,1,10,3,19,3,19,1600,3600,120,360,20,50,10,30,20,50,20,50,10,30,10,30 -6003,6011,Filir,910,60000,0,1,2,0,700,90,25,29,35,9,8,30,9,45,75,3,6,4,20,8,20,1,10,3,19,4,20,3,19,1200,3200,200,400,20,50,10,30,20,50,20,50,10,30,10,30 -6004,6012,Vanilmirth,911,60000,0,1,0,0,700,80,11,11,11,11,11,11,11,30,150,0,7,1,30,1,30,1,30,1,30,1,30,1,30,1200,4800,480,640,10,30,10,30,10,30,20,50,10,50,10,100 -6005,6013,Lif,537,60000,0,1,7,0,700,150,40,17,20,15,35,24,12,60,100,4,9,5,19,5,19,5,19,4,20,6,20,6,20,800,2400,220,480,10,30,10,30,20,40,30,50,20,50,10,30 -6006,6014,Amistr,912,60000,0,1,2,0,700,320,10,20,17,35,11,24,12,80,130,1,4,8,20,4,20,4,20,1,10,3,19,3,19,1600,3600,120,360,20,50,10,30,20,50,20,50,10,30,10,30 -6007,6015,Filir,910,60000,0,1,2,0,700,90,25,29,35,9,8,30,9,45,75,3,6,4,20,8,20,1,10,3,19,4,20,3,19,1200,3200,200,400,20,50,10,30,20,50,20,50,10,30,10,30 -6008,6016,Vanilmirth,911,60000,0,1,0,0,700,80,11,11,11,11,11,11,11,30,150,0,7,1,30,1,30,1,30,1,30,1,30,1,30,1200,4800,480,640,10,30,10,30,10,30,20,50,10,50,10,100 -6048,6048,Eira,6098,60000,1,1,8,4,700,150,40,17,20,15,35,24,12,40,160,20,42,13,39,28,42,15,25,14,48,16,36,9,18,1000,2000,10,200,1,10,1,10,1,10,1,10,1,10,1,10 -6049,6049,Bayeri,6112,60000,1,1,2,6,700,320,10,20,17,35,11,24,12,90,360,48,52,18,36,8,36,16,32,22,44,12,24,20,36,1000,2000,10,200,1,10,1,10,1,10,1,10,1,10,1,10 -6050,6050,Sera,6108,60000,1,1,4,2,700,90,25,29,35,9,8,30,9,60,240,36,64,10,25,16,32,5,25,7,35,28,40,20,40,1000,2000,10,200,1,10,1,10,1,10,1,10,1,10,1,10 -6051,6051,Dieter,6104,60000,1,1,0,3,700,80,11,11,11,11,11,11,11,240,480,40,120,20,40,13,26,18,36,15,40,16,32,4,16,1000,2000,10,200,1,10,1,10,1,10,1,10,1,10,1,10 -6052,6052,Eleanor,6115,60000,1,1,7,5,700,320,10,20,17,35,11,24,12,60,300,10,20,20,40,10,50,24,48,5,15,12,36,2,10,1000,2000,10,200,1,10,1,10,1,10,1,10,1,10,1,10 diff --git a/db/re/homunculus_db.yml b/db/re/homunculus_db.yml new file mode 100644 index 0000000000..f29b0a1c79 --- /dev/null +++ b/db/re/homunculus_db.yml @@ -0,0 +1,948 @@ +# This file is a part of rAthena. +# Copyright(C) 2023 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 . +# +########################################################################### +# Homunculus Database +########################################################################### +# +# Homunculus Settings +# +########################################################################### +# - BaseClass Base class. +# Name Name of homunculus. +# EvolutionClass Evolution class. +# Food Homunculus food item. (Default: Pet_Food) +# HungryDelay Time interval in milliseconds after which the hunger value is altered. (Default: 60000) +# Race Race. (Default: Demihuman) +# Element Element. (Default: Neutral) +# Size Size. (Default: Small) +# EvolutionSize Evolution size. (Default: Medium) +# AttackDelay Base ASPD. (Default: 700) +# Status: Homunculus stats. +# - Type Type of status. +# Base Base value of this status. (Default: 1) +# GrowthMinimum Minimum growth of this status. (Default: 0) +# GrowthMaximum Maximum growth of this status. (Default: 0) +# EvolutionMinimum Minimum evolution growth of this status. Only applies for homunculus that can evolve. (Default: 0) +# EvolutionMaximum Maximum evolution growth of this status. Only applies for homunculus that can evolve. (Default: 0) +# SkillTree: Skill tree. +# - Skill Skill name. +# Clear True to remove the given skill name. (Optional) +# MaxLevel Maximum level of skill. +# RequiredLevel Required base level of homunculus to learn. (Default: 0) +# RequiredIntimacy Required intimacy of homunculus to learn. (Default: 0) +# RequireEvolution Require the homunculus to be evolved to be available. (Default: false) +# Required: Prerequisite skills. (Default: null) +# - Skill Prerequisite skill name. +# Level Level of prerequisite skill. +# Clear True to remove the given prerequisite skill name. (Optional) +########################################################################### + +Header: + Type: HOMUNCULUS_DB + Version: 1 + +Body: + - Class: Lif + Name: Lif + EvolutionClass: Lif_H + Status: + - Type: Hp + Base: 150 + GrowthMinimum: 60 + GrowthMaximum: 100 + EvolutionMinimum: 800 + EvolutionMaximum: 2400 + - Type: Sp + Base: 40 + GrowthMinimum: 4 + GrowthMaximum: 9 + EvolutionMinimum: 220 + EvolutionMaximum: 480 + - Type: Str + Base: 17 + GrowthMinimum: 5 + GrowthMaximum: 19 + EvolutionMinimum: 10 + EvolutionMaximum: 30 + - Type: Agi + Base: 20 + GrowthMinimum: 5 + GrowthMaximum: 19 + EvolutionMinimum: 10 + EvolutionMaximum: 30 + - Type: Vit + Base: 15 + GrowthMinimum: 5 + GrowthMaximum: 19 + EvolutionMinimum: 20 + EvolutionMaximum: 40 + - Type: Int + Base: 35 + GrowthMinimum: 4 + GrowthMaximum: 20 + EvolutionMinimum: 30 + EvolutionMaximum: 50 + - Type: Dex + Base: 24 + GrowthMinimum: 6 + GrowthMaximum: 20 + EvolutionMinimum: 20 + EvolutionMaximum: 50 + - Type: Luk + Base: 12 + GrowthMinimum: 6 + GrowthMaximum: 20 + EvolutionMinimum: 10 + EvolutionMaximum: 30 + SkillTree: + - Skill: HLIF_HEAL + MaxLevel: 5 + - Skill: HLIF_AVOID + MaxLevel: 5 + Required: + - Skill: HLIF_HEAL + Level: 3 + - Skill: HLIF_BRAIN + MaxLevel: 5 + Required: + - Skill: HLIF_HEAL + Level: 5 + - Skill: HLIF_CHANGE + MaxLevel: 3 + RequiredIntimacy: 910 + RequireEvolution: true + - Class: Amistr + Name: Amistr + EvolutionClass: Amistr_H + Food: Zargon + Race: Brute + Status: + - Type: Hp + Base: 320 + GrowthMinimum: 80 + GrowthMaximum: 130 + EvolutionMinimum: 1600 + EvolutionMaximum: 3600 + - Type: Sp + Base: 10 + GrowthMinimum: 1 + GrowthMaximum: 4 + EvolutionMinimum: 120 + EvolutionMaximum: 360 + - Type: Str + Base: 20 + GrowthMinimum: 8 + GrowthMaximum: 20 + EvolutionMinimum: 20 + EvolutionMaximum: 50 + - Type: Agi + Base: 17 + GrowthMinimum: 4 + GrowthMaximum: 20 + EvolutionMinimum: 10 + EvolutionMaximum: 30 + - Type: Vit + Base: 35 + GrowthMinimum: 4 + GrowthMaximum: 20 + EvolutionMinimum: 20 + EvolutionMaximum: 50 + - Type: Int + Base: 11 + GrowthMinimum: 1 + GrowthMaximum: 10 + EvolutionMinimum: 20 + EvolutionMaximum: 50 + - Type: Dex + Base: 24 + GrowthMinimum: 3 + GrowthMaximum: 19 + EvolutionMinimum: 10 + EvolutionMaximum: 30 + - Type: Luk + Base: 12 + GrowthMinimum: 3 + GrowthMaximum: 19 + EvolutionMinimum: 10 + EvolutionMaximum: 30 + SkillTree: + - Skill: HAMI_CASTLE + MaxLevel: 5 + - Skill: HAMI_DEFENCE + MaxLevel: 5 + Required: + - Skill: HAMI_CASTLE + Level: 5 + - Skill: HAMI_SKIN + MaxLevel: 5 + Required: + - Skill: HAMI_DEFENCE + Level: 3 + - Skill: HAMI_BLOODLUST + MaxLevel: 3 + RequiredIntimacy: 910 + RequireEvolution: true + - Class: Filir + Name: Filir + EvolutionClass: Filir_H + Food: Garlet + Race: Brute + Status: + - Type: Hp + Base: 90 + GrowthMinimum: 45 + GrowthMaximum: 75 + EvolutionMinimum: 1200 + EvolutionMaximum: 3200 + - Type: Sp + Base: 25 + GrowthMinimum: 3 + GrowthMaximum: 6 + EvolutionMinimum: 200 + EvolutionMaximum: 400 + - Type: Str + Base: 29 + GrowthMinimum: 4 + GrowthMaximum: 20 + EvolutionMinimum: 20 + EvolutionMaximum: 50 + - Type: Agi + Base: 35 + GrowthMinimum: 8 + GrowthMaximum: 20 + EvolutionMinimum: 10 + EvolutionMaximum: 30 + - Type: Vit + Base: 9 + GrowthMinimum: 1 + GrowthMaximum: 10 + EvolutionMinimum: 20 + EvolutionMaximum: 50 + - Type: Int + Base: 8 + GrowthMinimum: 3 + GrowthMaximum: 19 + EvolutionMinimum: 20 + EvolutionMaximum: 50 + - Type: Dex + Base: 30 + GrowthMinimum: 4 + GrowthMaximum: 20 + EvolutionMinimum: 10 + EvolutionMaximum: 30 + - Type: Luk + Base: 9 + GrowthMinimum: 3 + GrowthMaximum: 19 + EvolutionMinimum: 10 + EvolutionMaximum: 30 + SkillTree: + - Skill: HFLI_MOON + MaxLevel: 5 + - Skill: HFLI_FLEET + MaxLevel: 5 + Required: + - Skill: HFLI_MOON + Level: 3 + - Skill: HFLI_SPEED + MaxLevel: 5 + Required: + - Skill: HFLI_FLEET + Level: 3 + - Skill: HFLI_SBR44 + MaxLevel: 3 + RequiredIntimacy: 910 + RequireEvolution: true + - Class: Vanilmirth + Name: Vanilmirth + EvolutionClass: Vanilmirth_H + Food: Scell + Race: Formless + Status: + - Type: Hp + Base: 80 + GrowthMinimum: 30 + GrowthMaximum: 150 + EvolutionMinimum: 1200 + EvolutionMaximum: 4800 + - Type: Sp + Base: 11 + GrowthMinimum: 0 + GrowthMaximum: 7 + EvolutionMinimum: 480 + EvolutionMaximum: 640 + - Type: Str + Base: 11 + GrowthMinimum: 1 + GrowthMaximum: 30 + EvolutionMinimum: 10 + EvolutionMaximum: 30 + - Type: Agi + Base: 11 + GrowthMinimum: 1 + GrowthMaximum: 30 + EvolutionMinimum: 10 + EvolutionMaximum: 30 + - Type: Vit + Base: 11 + GrowthMinimum: 1 + GrowthMaximum: 30 + EvolutionMinimum: 10 + EvolutionMaximum: 30 + - Type: Int + Base: 11 + GrowthMinimum: 1 + GrowthMaximum: 30 + EvolutionMinimum: 20 + EvolutionMaximum: 50 + - Type: Dex + Base: 11 + GrowthMinimum: 1 + GrowthMaximum: 30 + EvolutionMinimum: 10 + EvolutionMaximum: 50 + - Type: Luk + Base: 11 + GrowthMinimum: 1 + GrowthMaximum: 30 + EvolutionMinimum: 10 + EvolutionMaximum: 100 + SkillTree: + - Skill: HVAN_CAPRICE + MaxLevel: 5 + - Skill: HVAN_CHAOTIC + MaxLevel: 5 + Required: + - Skill: HVAN_CAPRICE + Level: 3 + - Skill: HVAN_INSTRUCT + MaxLevel: 5 + Required: + - Skill: HVAN_CAPRICE + Level: 5 + - Skill: HVAN_EXPLOSION + MaxLevel: 3 + RequiredIntimacy: 910 + RequireEvolution: true + - Class: Lif2 + Name: Lif + EvolutionClass: Lif_H2 + Status: + - Type: Hp + Base: 150 + GrowthMinimum: 60 + GrowthMaximum: 100 + EvolutionMinimum: 800 + EvolutionMaximum: 2400 + - Type: Sp + Base: 40 + GrowthMinimum: 4 + GrowthMaximum: 9 + EvolutionMinimum: 220 + EvolutionMaximum: 480 + - Type: Str + Base: 17 + GrowthMinimum: 5 + GrowthMaximum: 19 + EvolutionMinimum: 10 + EvolutionMaximum: 30 + - Type: Agi + Base: 20 + GrowthMinimum: 5 + GrowthMaximum: 19 + EvolutionMinimum: 10 + EvolutionMaximum: 30 + - Type: Vit + Base: 15 + GrowthMinimum: 5 + GrowthMaximum: 19 + EvolutionMinimum: 20 + EvolutionMaximum: 40 + - Type: Int + Base: 35 + GrowthMinimum: 4 + GrowthMaximum: 20 + EvolutionMinimum: 30 + EvolutionMaximum: 50 + - Type: Dex + Base: 24 + GrowthMinimum: 6 + GrowthMaximum: 20 + EvolutionMinimum: 20 + EvolutionMaximum: 50 + - Type: Luk + Base: 12 + GrowthMinimum: 6 + GrowthMaximum: 20 + EvolutionMinimum: 10 + EvolutionMaximum: 30 + SkillTree: + - Skill: HLIF_HEAL + MaxLevel: 5 + - Skill: HLIF_AVOID + MaxLevel: 5 + Required: + - Skill: HLIF_HEAL + Level: 3 + - Skill: HLIF_BRAIN + MaxLevel: 5 + Required: + - Skill: HLIF_HEAL + Level: 5 + - Skill: HLIF_CHANGE + MaxLevel: 3 + RequiredIntimacy: 910 + RequireEvolution: true + - Class: Amistr2 + Name: Amistr + EvolutionClass: Amistr_H2 + Food: Zargon + Race: Brute + Status: + - Type: Hp + Base: 320 + GrowthMinimum: 80 + GrowthMaximum: 130 + EvolutionMinimum: 1600 + EvolutionMaximum: 3600 + - Type: Sp + Base: 10 + GrowthMinimum: 1 + GrowthMaximum: 4 + EvolutionMinimum: 120 + EvolutionMaximum: 360 + - Type: Str + Base: 20 + GrowthMinimum: 8 + GrowthMaximum: 20 + EvolutionMinimum: 20 + EvolutionMaximum: 50 + - Type: Agi + Base: 17 + GrowthMinimum: 4 + GrowthMaximum: 20 + EvolutionMinimum: 10 + EvolutionMaximum: 30 + - Type: Vit + Base: 35 + GrowthMinimum: 4 + GrowthMaximum: 20 + EvolutionMinimum: 20 + EvolutionMaximum: 50 + - Type: Int + Base: 11 + GrowthMinimum: 1 + GrowthMaximum: 10 + EvolutionMinimum: 20 + EvolutionMaximum: 50 + - Type: Dex + Base: 24 + GrowthMinimum: 3 + GrowthMaximum: 19 + EvolutionMinimum: 10 + EvolutionMaximum: 30 + - Type: Luk + Base: 12 + GrowthMinimum: 3 + GrowthMaximum: 19 + EvolutionMinimum: 10 + EvolutionMaximum: 30 + SkillTree: + - Skill: HAMI_CASTLE + MaxLevel: 5 + - Skill: HAMI_DEFENCE + MaxLevel: 5 + Required: + - Skill: HAMI_CASTLE + Level: 5 + - Skill: HAMI_SKIN + MaxLevel: 5 + Required: + - Skill: HAMI_DEFENCE + Level: 3 + - Skill: HAMI_BLOODLUST + MaxLevel: 3 + RequiredIntimacy: 910 + RequireEvolution: true + - Class: Filir2 + Name: Filir + EvolutionClass: Filir_H2 + Food: Garlet + Race: Brute + Status: + - Type: Hp + Base: 90 + GrowthMinimum: 45 + GrowthMaximum: 75 + EvolutionMinimum: 1200 + EvolutionMaximum: 3200 + - Type: Sp + Base: 25 + GrowthMinimum: 3 + GrowthMaximum: 6 + EvolutionMinimum: 200 + EvolutionMaximum: 400 + - Type: Str + Base: 29 + GrowthMinimum: 4 + GrowthMaximum: 20 + EvolutionMinimum: 20 + EvolutionMaximum: 50 + - Type: Agi + Base: 35 + GrowthMinimum: 8 + GrowthMaximum: 20 + EvolutionMinimum: 10 + EvolutionMaximum: 30 + - Type: Vit + Base: 9 + GrowthMinimum: 1 + GrowthMaximum: 10 + EvolutionMinimum: 20 + EvolutionMaximum: 50 + - Type: Int + Base: 8 + GrowthMinimum: 3 + GrowthMaximum: 19 + EvolutionMinimum: 20 + EvolutionMaximum: 50 + - Type: Dex + Base: 30 + GrowthMinimum: 4 + GrowthMaximum: 20 + EvolutionMinimum: 10 + EvolutionMaximum: 30 + - Type: Luk + Base: 9 + GrowthMinimum: 3 + GrowthMaximum: 19 + EvolutionMinimum: 10 + EvolutionMaximum: 30 + SkillTree: + - Skill: HFLI_MOON + MaxLevel: 5 + - Skill: HFLI_FLEET + MaxLevel: 5 + Required: + - Skill: HFLI_MOON + Level: 3 + - Skill: HFLI_SPEED + MaxLevel: 5 + Required: + - Skill: HFLI_FLEET + Level: 3 + - Skill: HFLI_SBR44 + MaxLevel: 3 + RequiredIntimacy: 910 + RequireEvolution: true + - Class: Vanilmirth2 + Name: Vanilmirth + EvolutionClass: Vanilmirth_H2 + Food: Scell + Race: Formless + Status: + - Type: Hp + Base: 80 + GrowthMinimum: 30 + GrowthMaximum: 150 + EvolutionMinimum: 1200 + EvolutionMaximum: 4800 + - Type: Sp + Base: 11 + GrowthMinimum: 0 + GrowthMaximum: 7 + EvolutionMinimum: 480 + EvolutionMaximum: 640 + - Type: Str + Base: 11 + GrowthMinimum: 1 + GrowthMaximum: 30 + EvolutionMinimum: 10 + EvolutionMaximum: 30 + - Type: Agi + Base: 11 + GrowthMinimum: 1 + GrowthMaximum: 30 + EvolutionMinimum: 10 + EvolutionMaximum: 30 + - Type: Vit + Base: 11 + GrowthMinimum: 1 + GrowthMaximum: 30 + EvolutionMinimum: 10 + EvolutionMaximum: 30 + - Type: Int + Base: 11 + GrowthMinimum: 1 + GrowthMaximum: 30 + EvolutionMinimum: 20 + EvolutionMaximum: 50 + - Type: Dex + Base: 11 + GrowthMinimum: 1 + GrowthMaximum: 30 + EvolutionMinimum: 10 + EvolutionMaximum: 50 + - Type: Luk + Base: 11 + GrowthMinimum: 1 + GrowthMaximum: 30 + EvolutionMinimum: 10 + EvolutionMaximum: 100 + SkillTree: + - Skill: HVAN_CAPRICE + MaxLevel: 5 + - Skill: HVAN_CHAOTIC + MaxLevel: 5 + Required: + - Skill: HVAN_CAPRICE + Level: 3 + - Skill: HVAN_INSTRUCT + MaxLevel: 5 + Required: + - Skill: HVAN_CAPRICE + Level: 5 + - Skill: HVAN_EXPLOSION + MaxLevel: 3 + RequiredIntimacy: 910 + RequireEvolution: true + - Class: Eira + Name: Eira + Food: Small_Snow_Flower + Race: Angel + Element: Wind + Size: Medium + Status: + - Type: Hp + Base: 150 + GrowthMinimum: 40 + GrowthMaximum: 160 + - Type: Sp + Base: 40 + GrowthMinimum: 20 + GrowthMaximum: 42 + - Type: Str + Base: 17 + GrowthMinimum: 13 + GrowthMaximum: 39 + - Type: Agi + Base: 20 + GrowthMinimum: 28 + GrowthMaximum: 42 + - Type: Vit + Base: 15 + GrowthMinimum: 15 + GrowthMaximum: 25 + - Type: Int + Base: 35 + GrowthMinimum: 14 + GrowthMaximum: 48 + - Type: Dex + Base: 24 + GrowthMinimum: 16 + GrowthMaximum: 36 + - Type: Luk + Base: 12 + GrowthMinimum: 9 + GrowthMaximum: 18 + SkillTree: + - Skill: MH_LIGHT_OF_REGENE + MaxLevel: 5 + RequiredLevel: 128 + - Skill: MH_OVERED_BOOST + MaxLevel: 5 + RequiredLevel: 114 + - Skill: MH_ERASER_CUTTER + MaxLevel: 10 + RequiredLevel: 106 + - Skill: MH_XENO_SLASHER + MaxLevel: 10 + RequiredLevel: 121 + - Skill: MH_SILENT_BREEZE + MaxLevel: 5 + RequiredLevel: 137 + - Skill: MH_CLASSY_FLUTTER + MaxLevel: 10 + RequiredLevel: 210 + - Skill: MH_TWISTER_CUTTER + MaxLevel: 10 + RequiredLevel: 215 + - Skill: MH_ABSOLUTE_ZEPHYR + MaxLevel: 10 + RequiredLevel: 230 + - Class: Bayeri + Name: Bayeri + Food: Fresh_Plant + Race: Brute + Element: Holy + Size: Medium + Status: + - Type: Hp + Base: 320 + GrowthMinimum: 90 + GrowthMaximum: 360 + - Type: Sp + Base: 10 + GrowthMinimum: 48 + GrowthMaximum: 52 + - Type: Str + Base: 20 + GrowthMinimum: 18 + GrowthMaximum: 36 + - Type: Agi + Base: 17 + GrowthMinimum: 8 + GrowthMaximum: 36 + - Type: Vit + Base: 35 + GrowthMinimum: 16 + GrowthMaximum: 32 + - Type: Int + Base: 11 + GrowthMinimum: 22 + GrowthMaximum: 44 + - Type: Dex + Base: 24 + GrowthMinimum: 12 + GrowthMaximum: 24 + - Type: Luk + Base: 12 + GrowthMinimum: 20 + GrowthMaximum: 36 + SkillTree: + - Skill: MH_STAHL_HORN + MaxLevel: 10 + RequiredLevel: 105 + - Skill: MH_GOLDENE_FERSE + MaxLevel: 5 + RequiredLevel: 112 + - Skill: MH_STEINWAND + MaxLevel: 5 + RequiredLevel: 121 + - Skill: MH_HEILIGE_STANGE + MaxLevel: 10 + RequiredLevel: 138 + - Skill: MH_ANGRIFFS_MODUS + MaxLevel: 5 + RequiredLevel: 130 + - Skill: MH_LICHT_GEHORN + MaxLevel: 10 + RequiredLevel: 210 + - Skill: MH_GLANZEN_SPIES + MaxLevel: 10 + RequiredLevel: 215 + - Skill: MH_HEILIGE_PFERD + MaxLevel: 10 + RequiredLevel: 230 + - Skill: MH_GOLDENE_TONE + MaxLevel: 10 + RequiredLevel: 230 + - Class: Sera + Name: Sera + Food: Apple_Pudding + Race: Insect + Element: Earth + Size: Medium + Status: + - Type: Hp + Base: 90 + GrowthMinimum: 60 + GrowthMaximum: 240 + - Type: Sp + Base: 25 + GrowthMinimum: 36 + GrowthMaximum: 64 + - Type: Str + Base: 29 + GrowthMinimum: 10 + GrowthMaximum: 25 + - Type: Agi + Base: 35 + GrowthMinimum: 16 + GrowthMaximum: 32 + - Type: Vit + Base: 9 + GrowthMinimum: 5 + GrowthMaximum: 25 + - Type: Int + Base: 8 + GrowthMinimum: 7 + GrowthMaximum: 35 + - Type: Dex + Base: 30 + GrowthMinimum: 28 + GrowthMaximum: 40 + - Type: Luk + Base: 9 + GrowthMinimum: 20 + GrowthMaximum: 40 + SkillTree: + - Skill: MH_SUMMON_LEGION + MaxLevel: 5 + RequiredLevel: 132 + - Skill: MH_NEEDLE_OF_PARALYZE + MaxLevel: 10 + RequiredLevel: 105 + - Skill: MH_POISON_MIST + MaxLevel: 5 + RequiredLevel: 116 + - Skill: MH_PAIN_KILLER + MaxLevel: 10 + RequiredLevel: 123 + - Skill: MH_POLISHING_NEEDLE + MaxLevel: 10 + RequiredLevel: 210 + - Skill: MH_TOXIN_OF_MANDARA + MaxLevel: 10 + RequiredLevel: 215 + - Skill: MH_NEEDLE_STINGER + MaxLevel: 10 + RequiredLevel: 230 + - Class: Dieter + Name: Dieter + Food: Big_Cell + Race: Formless + Element: Fire + Size: Medium + Status: + - Type: Hp + Base: 80 + GrowthMinimum: 240 + GrowthMaximum: 480 + - Type: Sp + Base: 11 + GrowthMinimum: 40 + GrowthMaximum: 120 + - Type: Str + Base: 11 + GrowthMinimum: 20 + GrowthMaximum: 40 + - Type: Agi + Base: 11 + GrowthMinimum: 13 + GrowthMaximum: 26 + - Type: Vit + Base: 11 + GrowthMinimum: 18 + GrowthMaximum: 36 + - Type: Int + Base: 11 + GrowthMinimum: 15 + GrowthMaximum: 40 + - Type: Dex + Base: 11 + GrowthMinimum: 16 + GrowthMaximum: 32 + - Type: Luk + Base: 11 + GrowthMinimum: 4 + GrowthMaximum: 16 + SkillTree: + - Skill: MH_MAGMA_FLOW + MaxLevel: 5 + RequiredLevel: 122 + - Skill: MH_GRANITIC_ARMOR + MaxLevel: 5 + RequiredLevel: 116 + - Skill: MH_LAVA_SLIDE + MaxLevel: 10 + RequiredLevel: 109 + - Skill: MH_PYROCLASTIC + MaxLevel: 10 + RequiredLevel: 131 + - Skill: MH_VOLCANIC_ASH + MaxLevel: 5 + RequiredLevel: 102 + - Skill: MH_BLAST_FORGE + MaxLevel: 10 + RequiredLevel: 215 + - Skill: MH_TEMPERING + MaxLevel: 10 + RequiredLevel: 230 + - Skill: MH_BLAZING_LAVA + MaxLevel: 10 + RequiredLevel: 210 + - Class: Eleanor + Name: Eleanor + Food: Bun_ + Element: Poison + Size: Medium + Status: + - Type: Hp + Base: 320 + GrowthMinimum: 60 + GrowthMaximum: 300 + - Type: Sp + Base: 10 + GrowthMinimum: 10 + GrowthMaximum: 20 + - Type: Str + Base: 20 + GrowthMinimum: 20 + GrowthMaximum: 40 + - Type: Agi + Base: 17 + GrowthMinimum: 10 + GrowthMaximum: 50 + - Type: Vit + Base: 35 + GrowthMinimum: 24 + GrowthMaximum: 48 + - Type: Int + Base: 11 + GrowthMinimum: 5 + GrowthMaximum: 15 + - Type: Dex + Base: 24 + GrowthMinimum: 12 + GrowthMaximum: 36 + - Type: Luk + Base: 12 + GrowthMinimum: 2 + GrowthMaximum: 10 + SkillTree: + - Skill: MH_STYLE_CHANGE + MaxLevel: 1 + RequiredLevel: 100 + - Skill: MH_SONIC_CRAW + MaxLevel: 5 + RequiredLevel: 100 + - Skill: MH_SILVERVEIN_RUSH + MaxLevel: 10 + RequiredLevel: 114 + - Skill: MH_MIDNIGHT_FRENZY + MaxLevel: 10 + RequiredLevel: 128 + - Skill: MH_TINDER_BREAKER + MaxLevel: 5 + RequiredLevel: 100 + - Skill: MH_CBC + MaxLevel: 5 + RequiredLevel: 112 + - Skill: MH_EQC + MaxLevel: 5 + RequiredLevel: 133 + - Skill: MH_BRUSHUP_CLAW + MaxLevel: 10 + RequiredLevel: 210 + - Skill: MH_BLAZING_AND_FURIOUS + MaxLevel: 10 + RequiredLevel: 215 + - Skill: MH_THE_ONE_FIGHTER_RISES + MaxLevel: 10 + RequiredLevel: 230 diff --git a/doc/yaml/db/homunculus_db.yml b/doc/yaml/db/homunculus_db.yml new file mode 100644 index 0000000000..2624f56df7 --- /dev/null +++ b/doc/yaml/db/homunculus_db.yml @@ -0,0 +1,36 @@ +########################################################################### +# Homunculus Database +########################################################################### +# +# Homunculus Settings +# +########################################################################### +# - BaseClass Base class. +# Name Name of homunculus. +# EvolutionClass Evolution class. +# Food Homunculus food item. (Default: Pet_Food) +# HungryDelay Time interval in milliseconds after which the hunger value is altered. (Default: 60000) +# Race Race. (Default: Demihuman) +# Element Element. (Default: Neutral) +# Size Size. (Default: Small) +# EvolutionSize Evolution size. (Default: Medium) +# AttackDelay Base ASPD. (Default: 700) +# Status: Homunculus stats. +# - Type Type of status. +# Base Base value of this status. (Default: 1) +# GrowthMinimum Minimum growth of this status. (Default: 0) +# GrowthMaximum Maximum growth of this status. (Default: 0) +# EvolutionMinimum Minimum evolution growth of this status. Only applies for homunculus that can evolve. (Default: 0) +# EvolutionMaximum Maximum evolution growth of this status. Only applies for homunculus that can evolve. (Default: 0) +# SkillTree: Skill tree. +# - Skill Skill name. +# Clear True to remove the given skill name. (Optional) +# MaxLevel Maximum level of skill. +# RequiredLevel Required base level of homunculus to learn. (Default: 0) +# RequiredIntimacy Required intimacy of homunculus to learn. (Default: 0) +# RequireEvolution Require the homunculus to be evolved to be available. (Default: false) +# Required: Prerequisite skills. (Default: null) +# - Skill Prerequisite skill name. +# Level Level of prerequisite skill. +# Clear True to remove the given prerequisite skill name. (Optional) +########################################################################### diff --git a/src/map/atcommand.cpp b/src/map/atcommand.cpp index 8298a33621..81a2b3a088 100644 --- a/src/map/atcommand.cpp +++ b/src/map/atcommand.cpp @@ -4214,7 +4214,7 @@ ACMD_FUNC(reload) { clif_displaymessage(fd, msg_txt(sd,98)); // Monster database has been reloaded. } else if (strstr(command, "skilldb") || strncmp(message, "skilldb", 4) == 0) { skill_reload(); - hom_reload_skill(); + homunculus_db.reload(); clif_displaymessage(fd, msg_txt(sd,99)); // Skill database has been reloaded. } else if (strstr(command, "atcommand") || strncmp(message, "atcommand", 4) == 0) { atcommand_doload(); @@ -8119,7 +8119,7 @@ ACMD_FUNC(hominfo) ACMD_FUNC(homstats) { struct homun_data *hd; - struct s_homunculus_db *db; + std::shared_ptr db; struct s_homunculus *hom; int lv, min, max, evo; diff --git a/src/map/clif.cpp b/src/map/clif.cpp index 9444efc00d..99408aafd7 100644 --- a/src/map/clif.cpp +++ b/src/map/clif.cpp @@ -1864,7 +1864,7 @@ void clif_send_homdata(map_session_data *sd, int state, int param) int fd = sd->fd; if ( (state == SP_INTIMATE) && (param >= 910) && (sd->hd->homunculus.class_ == sd->hd->homunculusDB->evo_class) ) - hom_calc_skilltree(sd->hd, 0); + hom_calc_skilltree(sd->hd); WFIFOHEAD(fd, packet_len(0x230)); WFIFOW(fd,0)=0x230; diff --git a/src/map/homunculus.cpp b/src/map/homunculus.cpp index 7043af1b7e..8f2d70de81 100644 --- a/src/map/homunculus.cpp +++ b/src/map/homunculus.cpp @@ -25,11 +25,9 @@ #include "pc.hpp" #include "trade.hpp" -struct s_homunculus_db homunculus_db[MAX_HOMUNCULUS_CLASS]; //[orn] -struct homun_skill_tree_entry hskill_tree[MAX_HOMUNCULUS_CLASS][MAX_HOM_SKILL_TREE]; +using namespace rathena; static TIMER_FUNC(hom_hungry); -static uint16 homunculus_count; //For holding the view data of npc classes. [Skotlex] static struct view_data hom_viewdb[MAX_HOMUNCULUS_CLASS]; @@ -123,17 +121,6 @@ short hom_skill_get_index(uint16 skill_id) { return skill_id; } -/** -* Check homunculus class for array look up -* @param class_ -* @return Class index or -1 if invalid class -*/ -static short hom_class2index(int class_) { - if (homdb_checkid(class_)) - return class_ - HM_CLASS_BASE; - return -1; -} - /** * Get homunculus view data * @param class_ Homunculus class @@ -343,76 +330,69 @@ int hom_delete(struct homun_data *hd, int emote) } /** -* Calculates homunculus skill tree -* @param hd -* @param flag_envolve -*/ -void hom_calc_skilltree(struct homun_data *hd, bool flag_evolve) { - uint8 i; - short c = 0; + * Calculates homunculus skill tree for specific evolve/class. + * @param hd: Homunculus data + * @param skill_tree: Homunculus db skill tree + */ +void hom_calc_skilltree_sub(homun_data &hd, std::vector &skill_tree) { + bool evolved = false; - nullpo_retv(hd); + if (hd.homunculus.class_ == hd.homunculusDB->evo_class) + evolved = true; - /* load previous homunculus form skills first. */ - if (hd->homunculus.prev_class != 0 && (c = hom_class2index(hd->homunculus.prev_class)) >= 0) { - for (i = 0; i < MAX_HOM_SKILL_TREE; i++) { - uint16 skill_id; - short idx = -1; - bool fail = false; - if (!(skill_id = hskill_tree[c][i].id) || (idx = hom_skill_get_index(skill_id)) == -1) - continue; - if (hd->homunculus.hskill[idx].id) - continue; //Skill already known. - if (!battle_config.skillfree) { - uint8 j; - if (hskill_tree[c][i].need_level > hd->homunculus.level) - continue; - for (j = 0; j < MAX_HOM_SKILL_REQUIRE; j++) { - if (hskill_tree[c][i].need[j].id && - hom_checkskill(hd,hskill_tree[c][i].need[j].id) < hskill_tree[c][i].need[j].lv) - { - fail = true; - break; - } - } - } - if (!fail) - hd->homunculus.hskill[idx].id = skill_id; - } - } + for (const auto &skit : skill_tree) { + uint16 skill_id = skit.id; + short idx = hom_skill_get_index(skill_id); - - if ((c = hom_class2index(hd->homunculus.class_)) < 0) - return; - - for (i = 0; i < MAX_HOM_SKILL_TREE; i++) { - unsigned int intimacy = 0; - uint16 skill_id; - short idx = -1; - bool fail = false; - if (!(skill_id = hskill_tree[c][i].id) || (idx = hom_skill_get_index(skill_id)) == -1) + if (skill_id == 0 || idx == -1) continue; - if (hd->homunculus.hskill[idx].id) + if (hd.homunculus.hskill[idx].id) continue; //Skill already known. - intimacy = (flag_evolve) ? 10 : hd->homunculus.intimacy; - if (intimacy < hskill_tree[c][i].intimacy * 100) - continue; + + bool fail = false; + if (!battle_config.skillfree) { - uint8 j; - if (hskill_tree[c][i].need_level > hd->homunculus.level) + if (skit.intimacy > 0 && hd.homunculus.intimacy < skit.intimacy) { continue; - for (j = 0; j < MAX_HOM_SKILL_REQUIRE; j++) { - if (hskill_tree[c][i].need[j].id && - hom_checkskill(hd,hskill_tree[c][i].need[j].id) < hskill_tree[c][i].need[j].lv) - { + } + if (skit.evolution && !evolved) { + continue; + } + if (skit.need_level > hd.homunculus.level) + continue; + for (const auto &needit : skit.need) { + if (needit.first > 0 && hom_checkskill(&hd, needit.first) < needit.second) { fail = true; break; } } } if (!fail) - hd->homunculus.hskill[idx].id = skill_id; + hd.homunculus.hskill[idx].id = skill_id; } +} + +/** +* Calculates homunculus skill tree +* @param hd: Homunculus data +*/ +void hom_calc_skilltree(homun_data *hd) { + nullpo_retv(hd); + + std::shared_ptr homun_current = homunculus_db.homun_search(hd->homunculus.class_); + + // If the current class can't be loaded, then for sure there's no prev_class! + if (homun_current == nullptr) + return; + + std::shared_ptr homun = homunculus_db.homun_search(hd->homunculus.prev_class); + + /* load previous homunculus form skills first. */ + if (homun != nullptr) { + hom_calc_skilltree_sub(*hd, homun->skill_tree); + } + + hom_calc_skilltree_sub(*hd, homun_current->skill_tree); if (hd->master) clif_homskillinfoblock(hd->master); @@ -446,14 +426,17 @@ short hom_checkskill(struct homun_data *hd,uint16 skill_id) * @return Skill Level */ int hom_skill_tree_get_max(int skill_id, int b_class){ - uint8 i; + std::shared_ptr homun = homunculus_db.homun_search(b_class); - if ((b_class = hom_class2index(b_class)) < 0) + if (homun == nullptr) return 0; - ARR_FIND(0, MAX_HOM_SKILL_TREE, i, hskill_tree[b_class][i].id == skill_id); - if (i < MAX_HOM_SKILL_TREE) - return hskill_tree[b_class][i].max; - return skill_get_max(skill_id); + + for (const auto &skit : homun->skill_tree) { + if (skit.id == skill_id) + return skit.max; + } + + return 0; } /** @@ -462,17 +445,18 @@ int hom_skill_tree_get_max(int skill_id, int b_class){ * @param skill_id Homunculus skill ID * @return Level required or 0 if invalid **/ -uint8 hom_skill_get_min_level(int class_, uint16 skill_id) { - short class_idx = hom_class2index(class_), skill_idx = -1; - uint8 i; +uint16 hom_skill_get_min_level(int class_, uint16 skill_id) { + std::shared_ptr homun = homunculus_db.homun_search(class_); - if (class_idx == -1 || (skill_idx = hom_skill_get_index(skill_id)) == -1) - return 0; - ARR_FIND(0, MAX_HOM_SKILL_REQUIRE, i, hskill_tree[class_idx][i].id == skill_id); - if (i == MAX_HOM_SKILL_REQUIRE) + if (homun == nullptr) return 0; - return hskill_tree[class_idx][i].need_level; + for (const auto &skit : homun->skill_tree) { + if (skit.id == skill_id) + return skit.need_level; + } + + return 0; } /** @@ -514,30 +498,26 @@ void hom_skillup(struct homun_data *hd, uint16 skill_id) */ int hom_levelup(struct homun_data *hd) { - struct s_homunculus *hom; - struct h_stats *min = NULL, *max = NULL; - int growth_str, growth_agi, growth_vit, growth_int, growth_dex, growth_luk ; - int growth_max_hp, growth_max_sp ; int m_class; if ((m_class = hom_class2mapid(hd->homunculus.class_)) == -1) { - ShowError("hom_levelup: Invalid class %d. \n", hd->homunculus.class_); + ShowError("hom_levelup: Invalid class %d.\n", hd->homunculus.class_); return 0; } + struct s_hom_stats *min = nullptr, *max = nullptr; + /// When homunculus is homunculus S, we check to see if we need to apply previous class stats if(m_class&HOM_S && hd->homunculus.level < battle_config.hom_S_growth_level) { - int i; - if (!hd->homunculus.prev_class) { - /// We also need to be sure that the previous class exists, otherwise give it something to work with - hd->homunculus.prev_class = 6001; - } - // Give the homunculus the level up stats database it needs - i = hom_search(hd->homunculus.prev_class,HOMUNCULUS_CLASS); - if (i < 0) // Nothing should go wrong here, but check anyways + std::shared_ptr homun_s_db = homunculus_db.homun_search(hd->homunculus.prev_class); + + if (homun_s_db == nullptr) { + ShowError("hom_levelup: Failed to find database entry for %d.\n", hd->homunculus.prev_class); return 0; - max = &homunculus_db[i].gmax; - min = &homunculus_db[i].gmin; + } + + max = &homun_s_db->gmax; + min = &homun_s_db->gmin; } if (((m_class&HOM_REG) && hd->homunculus.level >= battle_config.hom_max_level) @@ -545,27 +525,28 @@ int hom_levelup(struct homun_data *hd) || !hd->exp_next || hd->homunculus.exp < hd->exp_next) return 0; - hom = &hd->homunculus; - hom->level++ ; - if (!(hom->level % 3)) - hom->skillpts++ ; //1 skillpoint each 3 base level + s_homunculus &hom = hd->homunculus; - hom->exp -= hd->exp_next ; - hd->exp_next = homun_exp_db.get_nextexp(hom->level); + hom.level++; + if (!(hom.level % 3)) + hom.skillpts++; //1 skillpoint each 3 base level + + hom.exp -= hd->exp_next; + hd->exp_next = homun_exp_db.get_nextexp(hom.level); if (!max) { max = &hd->homunculusDB->gmax; min = &hd->homunculusDB->gmin; } - growth_max_hp = rnd_value(min->HP, max->HP); - growth_max_sp = rnd_value(min->SP, max->SP); - growth_str = rnd_value(min->str, max->str); - growth_agi = rnd_value(min->agi, max->agi); - growth_vit = rnd_value(min->vit, max->vit); - growth_dex = rnd_value(min->dex, max->dex); - growth_int = rnd_value(min->int_,max->int_); - growth_luk = rnd_value(min->luk, max->luk); + int growth_max_hp = rnd_value(min->HP, max->HP); + int growth_max_sp = rnd_value(min->SP, max->SP); + int growth_str = rnd_value(min->str, max->str); + int growth_agi = rnd_value(min->agi, max->agi); + int growth_vit = rnd_value(min->vit, max->vit); + int growth_dex = rnd_value(min->dex, max->dex); + int growth_int = rnd_value(min->int_,max->int_); + int growth_luk = rnd_value(min->luk, max->luk); //Aegis discards the decimals in the stat growth values! growth_str-=growth_str%10; @@ -575,14 +556,14 @@ int hom_levelup(struct homun_data *hd) growth_int-=growth_int%10; growth_luk-=growth_luk%10; - hom->max_hp += growth_max_hp; - hom->max_sp += growth_max_sp; - hom->str += growth_str; - hom->agi += growth_agi; - hom->vit += growth_vit; - hom->dex += growth_dex; - hom->int_+= growth_int; - hom->luk += growth_luk; + hom.max_hp += growth_max_hp; + hom.max_sp += growth_max_sp; + hom.str += growth_str; + hom.agi += growth_agi; + hom.vit += growth_vit; + hom.dex += growth_dex; + hom.int_+= growth_int; + hom.luk += growth_luk; APPLY_HOMUN_LEVEL_STATWEIGHT(); @@ -605,19 +586,19 @@ int hom_levelup(struct homun_data *hd) /** * Changes homunculus class -* @param hd -* @param class_ old class -* @reutrn Fals if the class cannot be changed, True if otherwise +* @param hd: Homunculus data +* @param class_: New class +* @reutrn Fails if the class cannot be changed, otherwise true */ -static bool hom_change_class(struct homun_data *hd, short class_) { - int i; - i = hom_search(class_,HOMUNCULUS_CLASS); - if (i < 0) +static bool hom_change_class(struct homun_data *hd, int32 class_) { + std::shared_ptr homun = homunculus_db.homun_search(class_); + + if (homun == nullptr) return false; - hd->homunculusDB = &homunculus_db[i]; + + hd->homunculusDB = homun; hd->homunculus.class_ = class_; status_set_viewdata(&hd->bl, class_); - hom_calc_skilltree(hd, 1); return true; } @@ -628,28 +609,28 @@ static bool hom_change_class(struct homun_data *hd, short class_) { */ int hom_evolution(struct homun_data *hd) { - struct s_homunculus *hom; - struct h_stats *max, *min; - map_session_data *sd; nullpo_ret(hd); if(!hd->homunculusDB->evo_class || hd->homunculus.class_ == hd->homunculusDB->evo_class) { clif_emotion(&hd->bl, ET_SWEAT); return 0 ; } - sd = hd->master; + + map_session_data *sd = hd->master; + if (!sd) return 0; if (!hom_change_class(hd, hd->homunculusDB->evo_class)) { - ShowError("hom_evolution: Can't evolve homunc from %d to %d", hd->homunculus.class_, hd->homunculusDB->evo_class); + ShowError("hom_evolution: Can't evolve homunc from %d to %d\n", hd->homunculus.class_, hd->homunculusDB->evo_class); return 0; } //Apply evolution bonuses - hom = &hd->homunculus; - max = &hd->homunculusDB->emax; - min = &hd->homunculusDB->emin; + s_homunculus *hom = &hd->homunculus; + s_hom_stats *max = &hd->homunculusDB->emax; + s_hom_stats *min = &hd->homunculusDB->emin; + hom->max_hp += rnd_value(min->HP, max->HP); hom->max_sp += rnd_value(min->SP, max->SP); hom->str += 10*rnd_value(min->str, max->str); @@ -660,6 +641,8 @@ int hom_evolution(struct homun_data *hd) hom->luk += 10*rnd_value(min->luk, max->luk); hom->intimacy = battle_config.homunculus_evo_intimacy_reset; + hom_calc_skilltree(hd); + unit_remove_map(&hd->bl, CLR_OUTSIGHT); if (map_addblock(&hd->bl)) return 0; @@ -707,10 +690,12 @@ int hom_mutate(struct homun_data *hd, int homun_id) prev_class = hd->homunculus.class_; if (!hom_change_class(hd, homun_id)) { - ShowError("hom_mutate: Can't evolve homunc from %d to %d", hd->homunculus.class_, homun_id); + ShowError("hom_mutate: Can't evolve homunc from %d to %d\n", hd->homunculus.class_, homun_id); return 0; } + hom_calc_skilltree(hd); + unit_remove_map(&hd->bl, CLR_OUTSIGHT); if(map_addblock(&hd->bl)) return 0; @@ -1042,36 +1027,6 @@ void hom_change_name_ack(map_session_data *sd, char* name, int flag) clif_hominfo(sd,hd,0); } -/** -* Search homunculus info (food or next class) -* @param key -* @param type see enum e_hom_search_type -* @return info found -*/ -int hom_search(int key, int type) -{ - int i; - - for (i = 0; i < homunculus_count; i++) { - if (homunculus_db[i].base_class <= 0) - continue; - switch (type) { - case HOMUNCULUS_CLASS: - if (homunculus_db[i].base_class == key || - homunculus_db[i].evo_class == key) - return i; - break; - case HOMUNCULUS_FOOD: - if (homunculus_db[i].foodID == key) - return i; - break; - default: - return -1; - } - } - return -1; -} - /** * Create homunc structure * @param sd @@ -1079,27 +1034,28 @@ int hom_search(int key, int type) */ void hom_alloc(map_session_data *sd, struct s_homunculus *hom) { - struct homun_data *hd; - int i = 0; - t_tick tick = gettick(); - nullpo_retv(sd); Assert((sd->status.hom_id == 0 || sd->hd == 0) || sd->hd->master == sd); - i = hom_search(hom->class_,HOMUNCULUS_CLASS); - if(i < 0) { + std::shared_ptr homun_db = homunculus_db.homun_search(hom->class_); + + if (homun_db == nullptr) { ShowError("hom_alloc: unknown class [%d] for homunculus '%s', requesting deletion.\n", hom->class_, hom->name); sd->status.hom_id = 0; intif_homunculus_requestdelete(hom->hom_id); return; } + + struct homun_data *hd; + t_tick tick = gettick(); + sd->hd = hd = (struct homun_data*)aCalloc(1,sizeof(struct homun_data)); hd->bl.type = BL_HOM; hd->bl.id = npc_get_new_npc_id(); hd->master = sd; - hd->homunculusDB = &homunculus_db[i]; + hd->homunculusDB = homun_db; memcpy(&hd->homunculus, hom, sizeof(struct s_homunculus)); hd->exp_next = homun_exp_db.get_nextexp(hd->homunculus.level); @@ -1262,35 +1218,36 @@ int hom_recv_data(uint32 account_id, struct s_homunculus *sh, int flag) */ bool hom_create_request(map_session_data *sd, int class_) { - struct s_homunculus homun; - struct h_stats *base; - int i; - nullpo_ret(sd); - i = hom_search(class_,HOMUNCULUS_CLASS); - if(i < 0) + std::shared_ptr homun_db = homunculus_db.homun_search(class_); + + if (homun_db == nullptr) return false; + struct s_homunculus homun; + memset(&homun, 0, sizeof(struct s_homunculus)); //Initial data - safestrncpy(homun.name, homunculus_db[i].name, NAME_LENGTH-1); + safestrncpy(homun.name, homun_db->name, NAME_LENGTH-1); homun.class_ = class_; homun.level = 1; homun.hunger = 32; //32% homun.intimacy = 2100; //21/1000 homun.char_id = sd->status.char_id; - homun.hp = 10 ; - base = &homunculus_db[i].base; - homun.max_hp = base->HP; - homun.max_sp = base->SP; - homun.str = base->str *10; - homun.agi = base->agi *10; - homun.vit = base->vit *10; - homun.int_= base->int_*10; - homun.dex = base->dex *10; - homun.luk = base->luk *10; + homun.hp = 10; + + s_hom_stats base = homun_db->base; + + homun.max_hp = base.HP; + homun.max_sp = base.SP; + homun.str = base.str *10; + homun.agi = base.agi *10; + homun.vit = base.vit *10; + homun.int_= base.int_*10; + homun.dex = base.dex *10; + homun.luk = base.luk *10; // Request homunculus creation intif_homunculus_create(sd->status.account_id, &homun); @@ -1371,12 +1328,9 @@ void hom_revive(struct homun_data *hd, unsigned int hp, unsigned int sp) */ void hom_reset_stats(struct homun_data *hd) { //Resets a homunc stats back to zero (but doesn't touches hunger or intimacy) - struct s_homunculus_db *db; - struct s_homunculus *hom; - struct h_stats *base; - hom = &hd->homunculus; - db = hd->homunculusDB; - base = &db->base; + struct s_homunculus *hom = &hd->homunculus; + struct s_hom_stats *base = &hd->homunculusDB->base; + hom->level = 1; hom->hp = 10; hom->max_hp = base->HP; @@ -1425,7 +1379,8 @@ int hom_shuffle(struct homun_data *hd) if(hd->homunculus.class_ == hd->homunculusDB->evo_class) { //Evolved bonuses struct s_homunculus *hom = &hd->homunculus; - struct h_stats *max = &hd->homunculusDB->emax, *min = &hd->homunculusDB->emin; + struct s_hom_stats *max = &hd->homunculusDB->emax, *min = &hd->homunculusDB->emin; + hom->max_hp += rnd_value(min->HP, max->HP); hom->max_sp += rnd_value(min->SP, max->SP); hom->str += 10*rnd_value(min->str, max->str); @@ -1483,216 +1438,583 @@ uint8 hom_get_intimacy_grade(struct homun_data *hd) { return hom_intimacy_intimacy2grade(hd->homunculus.intimacy); } -/** -* Read homunculus db -*/ -static bool read_homunculusdb_sub(char* str[], int columns, int current) -{ - int classid; - uint16 i; - struct s_homunculus_db *db; +const std::string HomunculusDatabase::getDefaultLocation() { + return std::string(db_path) + "/homunculus_db.yml"; +} - //Base Class,Evo Class - classid = atoi(str[0]); - if (classid < HM_CLASS_BASE || classid > HM_CLASS_MAX) - { - ShowError("read_homunculusdb : Invalid class %d\n", classid); +bool HomunculusDatabase::parseStatusNode(const std::string &nodeName, const std::string &subNodeName, const ryml::NodeRef &node, s_hom_stats &bonus) { + uint32 value; + + if (!this->asUInt32(node, nodeName, value)) return false; - } - //Find the ClassID, already exist or not in homunculus_db - ARR_FIND(0,homunculus_count,i,homunculus_db[i].base_class == classid); - if (i >= homunculus_count) - db = &homunculus_db[homunculus_count]; - else - db = &homunculus_db[i]; + if (subNodeName.compare("Hp") == 0) + bonus.HP = value; + else if (subNodeName.compare("Sp") == 0) + bonus.SP = value; + else if (subNodeName.compare("Str") == 0) + bonus.str = static_cast(value); + else if (subNodeName.compare("Agi") == 0) + bonus.agi = static_cast(value); + else if (subNodeName.compare("Vit") == 0) + bonus.vit = static_cast(value); + else if (subNodeName.compare("Int") == 0) + bonus.int_ = static_cast(value); + else if (subNodeName.compare("Dex") == 0) + bonus.dex = static_cast(value); + else if (subNodeName.compare("Luk") == 0) + bonus.luk = static_cast(value); - db->base_class = classid; - classid = atoi(str[1]); - if (classid < HM_CLASS_BASE || classid > HM_CLASS_MAX) - { - db->base_class = 0; - ShowError("read_homunculusdb : Invalid class %d\n", classid); - return false; - } - db->evo_class = classid; - //Name, Food, Hungry Delay, Base Size, Evo Size, Race, Element, ASPD - safestrncpy(db->name,str[2],NAME_LENGTH-1); - db->foodID = atoi(str[3]); - db->hungryDelay = atoi(str[4]); - db->base_size = atoi(str[5]); - db->evo_size = atoi(str[6]); - db->race = atoi(str[7]); - db->element = atoi(str[8]); - db->baseASPD = atoi(str[9]); - //base HP, SP, str, agi, vit, int, dex, luk - db->base.HP = atoi(str[10]); - db->base.SP = atoi(str[11]); - db->base.str = atoi(str[12]); - db->base.agi = atoi(str[13]); - db->base.vit = atoi(str[14]); - db->base.int_= atoi(str[15]); - db->base.dex = atoi(str[16]); - db->base.luk = atoi(str[17]); - //Growth Min/Max HP, SP, str, agi, vit, int, dex, luk - db->gmin.HP = atoi(str[18]); - db->gmax.HP = atoi(str[19]); - db->gmin.SP = atoi(str[20]); - db->gmax.SP = atoi(str[21]); - db->gmin.str = atoi(str[22]); - db->gmax.str = atoi(str[23]); - db->gmin.agi = atoi(str[24]); - db->gmax.agi = atoi(str[25]); - db->gmin.vit = atoi(str[26]); - db->gmax.vit = atoi(str[27]); - db->gmin.int_= atoi(str[28]); - db->gmax.int_= atoi(str[29]); - db->gmin.dex = atoi(str[30]); - db->gmax.dex = atoi(str[31]); - db->gmin.luk = atoi(str[32]); - db->gmax.luk = atoi(str[33]); - //Evolution Min/Max HP, SP, str, agi, vit, int, dex, luk - db->emin.HP = atoi(str[34]); - db->emax.HP = atoi(str[35]); - db->emin.SP = atoi(str[36]); - db->emax.SP = atoi(str[37]); - db->emin.str = atoi(str[38]); - db->emax.str = atoi(str[39]); - db->emin.agi = atoi(str[40]); - db->emax.agi = atoi(str[41]); - db->emin.vit = atoi(str[42]); - db->emax.vit = atoi(str[43]); - db->emin.int_= atoi(str[44]); - db->emax.int_= atoi(str[45]); - db->emin.dex = atoi(str[46]); - db->emax.dex = atoi(str[47]); - db->emin.luk = atoi(str[48]); - db->emax.luk = atoi(str[49]); - - //Check that the min/max values really are below the other one. - if(db->gmin.HP > db->gmax.HP) - db->gmin.HP = db->gmax.HP; - if(db->gmin.SP > db->gmax.SP) - db->gmin.SP = db->gmax.SP; - if(db->gmin.str > db->gmax.str) - db->gmin.str = db->gmax.str; - if(db->gmin.agi > db->gmax.agi) - db->gmin.agi = db->gmax.agi; - if(db->gmin.vit > db->gmax.vit) - db->gmin.vit = db->gmax.vit; - if(db->gmin.int_> db->gmax.int_) - db->gmin.int_= db->gmax.int_; - if(db->gmin.dex > db->gmax.dex) - db->gmin.dex = db->gmax.dex; - if(db->gmin.luk > db->gmax.luk) - db->gmin.luk = db->gmax.luk; - - if(db->emin.HP > db->emax.HP) - db->emin.HP = db->emax.HP; - if(db->emin.SP > db->emax.SP) - db->emin.SP = db->emax.SP; - if(db->emin.str > db->emax.str) - db->emin.str = db->emax.str; - if(db->emin.agi > db->emax.agi) - db->emin.agi = db->emax.agi; - if(db->emin.vit > db->emax.vit) - db->emin.vit = db->emax.vit; - if(db->emin.int_> db->emax.int_) - db->emin.int_= db->emax.int_; - if(db->emin.dex > db->emax.dex) - db->emin.dex = db->emax.dex; - if(db->emin.luk > db->emax.luk) - db->emin.luk = db->emax.luk; - - if (i >= homunculus_count) - homunculus_count++; return true; } /** -* Read homunculus db (check the files) -*/ -void read_homunculusdb(void) { - uint8 i; - const char *filename[] = { - DBPATH"homunculus_db.txt", - DBIMPORT"/homunculus_db.txt", - }; - homunculus_count = 0; - memset(homunculus_db,0,sizeof(homunculus_db)); - for(i = 0; i 0); + * Reads and parses an entry from the homunculus_db. + * @param node: YAML node containing the entry. + * @return count of successfully parsed rows + */ +uint64 HomunculusDatabase::parseBodyNode(const ryml::NodeRef &node) { + std::string class_name; + + if (!this->asString(node, "Class", class_name)) + return 0; + + std::string class_name_constant = "MER_" + class_name; + int64 class_tmp; + + if (!script_get_constant(class_name_constant.c_str(), &class_tmp)) { + this->invalidWarning(node["Class"], "Invalid homunculus Class \"%s\", skipping.\n", class_name.c_str()); + return 0; } + + int32 class_id = static_cast(class_tmp); + std::shared_ptr hom = this->find(class_id); + bool exists = hom != nullptr; + + if (!exists) { + if (!this->nodesExist(node, { "Name", "Status", "SkillTree" })) + return 0; + + hom = std::make_shared(); + hom->base_class = class_id; + hom->base = { 1 }; + hom->gmin = {}; + hom->gmax = {}; + hom->emin = {}; + hom->emax = {}; + } + + if (this->nodeExists(node, "Name")) { + std::string name; + + if (!this->asString(node, "Name", name)) + return 0; + + safestrncpy(hom->name, name.c_str(), sizeof(hom->name)); + } + + if (this->nodeExists(node, "EvolutionClass")) { + std::string evo_class_name; + + if (!this->asString(node, "EvolutionClass", evo_class_name)) + return 0; + + std::string evo_class_name_constant = "MER_" + evo_class_name; + int64 constant; + + if (!script_get_constant(evo_class_name_constant.c_str(), &constant)) { + this->invalidWarning(node["EvolutionClass"], "Invalid homunculus Evolution Class %s, skipping.\n", evo_class_name.c_str()); + return 0; + } + + hom->evo_class = static_cast(constant); + } else { + if (!exists) + hom->evo_class = class_id; + } + + if (this->nodeExists(node, "Food")) { + std::string food; + + if (!this->asString(node, "Food", food)) + return 0; + + std::shared_ptr item = item_db.search_aegisname(food.c_str()); + + if (item == nullptr) { + this->invalidWarning(node["Food"], "Invalid homunculus Food %s, skipping.\n", food.c_str()); + return 0; + } + + hom->foodID = item->nameid; + } else { + if (!exists) + hom->foodID = ITEMID_PET_FOOD; + } + + if (this->nodeExists(node, "HungryDelay")) { + int32 delay; + + if (!this->asInt32(node, "HungryDelay", delay)) + return 0; + + hom->hungryDelay = delay; + } else { + if (!exists) + hom->hungryDelay = 60000; + } + + 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)) { + this->invalidWarning(node["Race"], "Invalid homunculus Race %s, skipping.\n", race.c_str()); + return 0; + } + + hom->race = static_cast(constant); + } else { + if (!exists) + hom->race = RC_DEMIHUMAN; + } + + if (this->nodeExists(node, "Element")) { + std::string element; + + if (!this->asString(node, "Element", element)) + return 0; + + std::string element_constant = "ELE_" + element; + int64 constant; + + if (!script_get_constant(element_constant.c_str(), &constant)) { + this->invalidWarning(node["Element"], "Invalid homunculus Element %s, skipping.\n", element.c_str()); + return 0; + } + + hom->element = static_cast(constant); + } else { + if (!exists) + hom->element = ELE_NEUTRAL; + } + + 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)) { + this->invalidWarning(node["Size"], "Invalid homunculus Size %s, skipping.\n", size.c_str()); + return 0; + } + + hom->base_size = static_cast(constant); + } else { + if (!exists) + hom->base_size = SZ_SMALL; + } + + if (this->nodeExists(node, "EvolutionSize")) { + std::string size; + + if (!this->asString(node, "EvolutionSize", size)) + return 0; + + std::string size_constant = "SIZE_" + size; + int64 constant; + + if (!script_get_constant(size_constant.c_str(), &constant)) { + this->invalidWarning(node["EvolutionSize"], "Invalid homunculus EvolutionSize %s, skipping.\n", size.c_str()); + return 0; + } + + hom->base_size = static_cast(constant); + } else { + if (!exists) + hom->base_size = SZ_MEDIUM; + } + + if (this->nodeExists(node, "AttackDelay")) { + uint16 aspd; + + if (!this->asUInt16(node, "AttackDelay", aspd)) + return 0; + + if (aspd > 2000) { + this->invalidWarning(node["AttackDelay"], "Homunculus AttackDelay %hu exceeds 2000, capping.\n", aspd); + aspd = 2000; + } + + hom->baseASPD = aspd; + } else { + if (!exists) + hom->baseASPD = 700; + } + + if (this->nodeExists(node, "Status")) { + std::vector stat_list = { "Hp", "Sp", "Str", "Agi", "Vit", "Int", "Dex", "Luk" }; + + for (const auto &statusNode : node["Status"]) { + if (!this->nodeExists(statusNode, "Type")) + return 0; + + std::string stat_name; + + if (!this->asString(statusNode, "Type", stat_name)) + return 0; + + if (!util::vector_exists(stat_list, stat_name)) { + this->invalidWarning(statusNode["Type"], "Invalid Status Type %s, skipping.\n", stat_name.c_str()); + return 0; + } + + if (this->nodeExists(statusNode, "Base")) { + if (!this->parseStatusNode("Base", stat_name, statusNode, hom->base)) { + return 0; + } + } else { + if (!exists) { + hom->base = { 1 }; + } + } + + if (this->nodeExists(statusNode, "GrowthMinimum")) { + if (!this->parseStatusNode("GrowthMinimum", stat_name, statusNode, hom->gmin)) { + return 0; + } + } else { + if (!exists) { + hom->gmin = {}; + } + } + + if (this->nodeExists(statusNode, "GrowthMaximum")) { + if (!this->parseStatusNode("GrowthMaximum", stat_name, statusNode, hom->gmax)) { + return 0; + } + } else { + if (!exists) { + hom->gmax = {}; + } + } + + if (this->nodeExists(statusNode, "EvolutionMinimum")) { + if (!this->parseStatusNode("EvolutionMinimum", stat_name, statusNode, hom->emin)) { + return 0; + } + } else { + if (!exists) { + hom->emin = {}; + } + } + + if (this->nodeExists(statusNode, "EvolutionMaximum")) { + if (!this->parseStatusNode("EvolutionMaximum", stat_name, statusNode, hom->emax)) { + return 0; + } + } else { + if (!exists) { + hom->emax = {}; + } + } + } + + // Cap values + if (hom->gmin.HP > hom->gmax.HP) { + hom->gmin.HP = hom->gmax.HP; + this->invalidWarning(node, "GrowthMinimum HP %d is greater than GrowthMaximum HP %d for homunculus %s, capping minimum to maximum.\n", hom->gmin.HP, hom->gmax.HP, class_name.c_str()); + } + if (hom->gmin.SP > hom->gmax.SP) { + hom->gmin.SP = hom->gmax.SP; + this->invalidWarning(node, "GrowthMinimum SP %d is greater than GrowthMaximum SP %d for homunculus %s, capping minimum to maximum.\n", hom->gmin.SP, hom->gmax.SP, class_name.c_str()); + } + if (hom->gmin.str > hom->gmax.str) { + hom->gmin.str = hom->gmax.str; + this->invalidWarning(node, "GrowthMinimum STR %d is greater than GrowthMaximum STR %d for homunculus %s, capping minimum to maximum.\n", hom->gmin.str, hom->gmax.str, class_name.c_str()); + } + if (hom->gmin.agi > hom->gmax.agi) { + hom->gmin.agi = hom->gmax.agi; + this->invalidWarning(node, "GrowthMinimum AGI %d is greater than GrowthMaximum AGI %d for homunculus %s, capping minimum to maximum.\n", hom->gmin.agi, hom->gmax.agi, class_name.c_str()); + } + if (hom->gmin.vit > hom->gmax.vit) { + hom->gmin.vit = hom->gmax.vit; + this->invalidWarning(node, "GrowthMinimum VIT %d is greater than GrowthMaximum VIT %d for homunculus %s, capping minimum to maximum.\n", hom->gmin.vit, hom->gmax.vit, class_name.c_str()); + } + if (hom->gmin.int_ > hom->gmax.int_) { + hom->gmin.int_ = hom->gmax.int_; + this->invalidWarning(node, "GrowthMinimum INT %d is greater than GrowthMaximum INT %d for homunculus %s, capping minimum to maximum.\n", hom->gmin.int_, hom->gmax.int_, class_name.c_str()); + } + if (hom->gmin.dex > hom->gmax.dex) { + hom->gmin.dex = hom->gmax.dex; + this->invalidWarning(node, "GrowthMinimum DEX %d is greater than GrowthMaximum DEX %d for homunculus %s, capping minimum to maximum.\n", hom->gmin.dex, hom->gmax.dex, class_name.c_str()); + } + if (hom->gmin.luk > hom->gmax.luk) { + hom->gmin.luk = hom->gmax.luk; + this->invalidWarning(node, "GrowthMinimum LUK %d is greater than GrowthMaximum LUK %d for homunculus %s, capping minimum to maximum.\n", hom->gmin.luk, hom->gmax.luk, class_name.c_str()); + } + if (hom->emin.HP > hom->emax.HP) { + hom->emin.HP = hom->emax.HP; + this->invalidWarning(node, "EvolutionMinimum HP %d is greater than EvolutionMaximum HP %d for homunculus %s, capping minimum to maximum.\n", hom->emin.HP, hom->emax.HP, class_name.c_str()); + } + if (hom->emin.SP > hom->emax.SP) { + hom->emin.SP = hom->emax.SP; + this->invalidWarning(node, "EvolutionMinimum SP %d is greater than EvolutionMaximum SP %d for homunculus %s, capping minimum to maximum.\n", hom->emin.SP, hom->emax.SP, class_name.c_str()); + } + if (hom->emin.str > hom->emax.str) { + hom->emin.str = hom->emax.str; + this->invalidWarning(node, "EvolutionMinimum STR %d is greater than EvolutionMaximum STR %d for homunculus %s, capping minimum to maximum.\n", hom->emin.str, hom->emax.str, class_name.c_str()); + } + if (hom->emin.agi > hom->emax.agi) { + hom->emin.agi = hom->emax.agi; + this->invalidWarning(node, "EvolutionMinimum AGI %d is greater than EvolutionMaximum AGI %d for homunculus %s, capping minimum to maximum.\n", hom->emin.agi, hom->emax.agi, class_name.c_str()); + } + if (hom->emin.vit > hom->emax.vit) { + hom->emin.vit = hom->emax.vit; + this->invalidWarning(node, "EvolutionMinimum VIT %d is greater than EvolutionMaximum VIT %d for homunculus %s, capping minimum to maximum.\n", hom->emin.vit, hom->emax.vit, class_name.c_str()); + } + if (hom->emin.int_ > hom->emax.int_) { + hom->emin.int_ = hom->emax.int_; + this->invalidWarning(node, "EvolutionMinimum INT %d is greater than EvolutionMaximum INT %d for homunculus %s, capping minimum to maximum.\n", hom->emin.int_, hom->emax.int_, class_name.c_str()); + } + if (hom->emin.dex > hom->emax.dex) { + hom->emin.dex = hom->emax.dex; + this->invalidWarning(node, "EvolutionMinimum DEX %d is greater than EvolutionMaximum DEX %d for homunculus %s, capping minimum to maximum.\n", hom->emin.dex, hom->emax.dex, class_name.c_str()); + } + if (hom->emin.luk > hom->emax.luk) { + hom->emin.luk = hom->emax.luk; + this->invalidWarning(node, "EvolutionMinimum LUK %d is greater than EvolutionMaximum LUK %d for homunculus %s, capping minimum to maximum.\n", hom->emin.luk, hom->emax.luk, class_name.c_str()); + } + } + + if (this->nodeExists(node, "SkillTree")) { + const ryml::NodeRef &skillsNode = node["SkillTree"]; + + for (const ryml::NodeRef &skill : skillsNode) { + s_homun_skill_tree_entry entry; + + if (this->nodeExists(skill, "Skill")) { + std::string skill_name; + + if (!this->asString(skill, "Skill", skill_name)) + return 0; + + uint16 skill_id = skill_name2id(skill_name.c_str()); + + if (skill_id == 0) { + this->invalidWarning(skill["Skill"], "Invalid homunculus skill %s, skipping.\n", skill_name.c_str()); + return 0; + } + + if (!SKILL_CHK_HOMUN(skill_id)) { + this->invalidWarning(skill["Skill"], "Homunculus skill %s (%u) is out of the homunculus skill range [%u-%u], skipping.\n", skill_name.c_str(), skill_id, HM_SKILLBASE, HM_SKILLBASE + MAX_HOMUNSKILL - 1); + return 0; + } + + entry.id = skill_id; + } + + if (this->nodeExists(skill, "Clear")) { + std::vector::iterator it = hom->skill_tree.begin(); + bool found = false; + + while (it != hom->skill_tree.end()) { + if (it->id == entry.id) { // Skill found, remove it from the skill tree. + it = hom->skill_tree.erase(it); + found = true; + } else { + it++; + } + } + + if (!found) + this->invalidWarning(skill["Clear"], "Failed to remove nonexistent skill %s from homunuculus %s.\n", skill_db.find(entry.id)->name, class_name.c_str()); + continue; + } + + if (this->nodeExists(skill, "MaxLevel")) { + uint16 level; + + if (!this->asUInt16(skill, "MaxLevel", level)) + return 0; + + uint16 db_level = skill_get_max(entry.id); + + if (level > db_level) { + this->invalidWarning(skill["MaxLevel"], "Skill %s exceeds maximum defined skill level %d from homunuculus %s, capping.\n", skill_db.find(entry.id)->name, db_level, class_name.c_str()); + level = db_level; + } + + entry.max = level; + } + + if (this->nodeExists(skill, "RequiredLevel")) { + uint16 level; + + if (!this->asUInt16(skill, "RequiredLevel", level)) + return 0; + + uint16 config_max = battle_config.hom_max_level; + + if ((hom_class2type(class_id) == HT_S)) + config_max = battle_config.hom_S_max_level; + + if (level > config_max) { + this->invalidWarning(skill["RequiredLevel"], "Homunculus Required Skill level %u exceeds maximum level %u, capping.\n", level, config_max); + level = config_max; + } + + entry.need_level = level; + } else { + if (!exists) + entry.need_level = 0; + } + + if (this->nodeExists(skill, "RequiredIntimacy")) { + uint16 intimacy; + + if (!this->asUInt16(skill, "RequiredIntimacy", intimacy)) + return 0; + + if (intimacy > 1000) { + this->invalidWarning(skill["RequiredIntimacy"], "Homunculus Required Intimacy %u exceeds maximum intimacy 1000, capping.\n", intimacy); + intimacy = 1000; + } + + entry.intimacy = intimacy * 100; + } else { + if (!exists) + entry.intimacy = 0; + } + + if (this->nodeExists(skill, "RequireEvolution")) { + bool evo; + + if (!this->asBool(skill, "RequireEvolution", evo)) + return 0; + + if (evo && hom->base_class == hom->evo_class) { + this->invalidWarning(skill["RequireEvolution"], "Homunculus %s does not have any evolution making skill %s unobtainable, skipping.\n", class_name.c_str(), skill_db.find(entry.id)->name); + return 0; + } + + entry.evolution = evo; + } else { + if (!exists) + entry.evolution = false; + } + + if (this->nodeExists(skill, "Required")) { + const ryml::NodeRef &required = skill["Required"]; + + for (const ryml::NodeRef &prereqskill : required) { + uint16 skill_id = 0, skill_lv = 0; + + if (this->nodeExists(prereqskill, "Skill")) { + std::string skill_name; + + if (!this->asString(prereqskill, "Skill", skill_name)) + return 0; + + skill_id = skill_name2id(skill_name.c_str()); + + if (skill_id == 0) { + this->invalidWarning(prereqskill["Skill"], "Invalid homunculus skill %s, skipping.\n", skill_name.c_str()); + return 0; + } + + if (!SKILL_CHK_HOMUN(skill_id)) { + this->invalidWarning(prereqskill["Skill"], "Homunculus skill %s (%u) is out of the homunculus skill range [%u-%u], skipping.\n", skill_name.c_str(), skill_id, HM_SKILLBASE, HM_SKILLBASE + MAX_HOMUNSKILL - 1); + return 0; + } + } + + if (this->nodeExists(prereqskill, "Clear")) { + bool found = false; + + for (auto &skit : hom->skill_tree) { + std::unordered_map::iterator it = skit.need.begin(); + + while (it != skit.need.end()) { + if (it->first == skill_id) { // Skill found, remove it from the skill tree. + it = skit.need.erase(it); + found = true; + } else { + it++; + } + } + } + + if (!found) + this->invalidWarning(prereqskill["Clear"], "Failed to remove nonexistent prerequisite skill %s from homunuculus %s.\n", skill_db.find(skill_id)->name, class_name.c_str()); + continue; + } + + if (this->nodeExists(prereqskill, "Level")) { + if (!this->asUInt16(prereqskill, "Level", skill_lv)) + return 0; + } + + if (skill_id > 0 && skill_lv > 0) + entry.need.emplace(skill_id, skill_lv); + } + } + + hom->skill_tree.push_back(entry); + } + } + + if (!exists) + this->put(class_id, hom); + + return 1; } /** -* Read homunculus skill db -* ,,,,,,,,,,,,,, -*/ -static bool read_homunculus_skilldb_sub(char* split[], int columns, int current) { - uint16 skill_id; - int8 i; - short class_idx, idx = -1; + * Since evolved homunculus share a database entry, use this search. + * !TODO: Clean this up so evolved homunculus have their own entry + * @param class_: Homun class to look up + * @return Shared pointer of homunculus on success, otherwise nullptr + */ +std::shared_ptr HomunculusDatabase::homun_search(int32 class_) { + std::shared_ptr hom = homunculus_db.find(class_); - // check for bounds [celest] - if ((class_idx = hom_class2index(atoi(split[0]))) == -1) { - ShowWarning("read_homunculus_skilldb: Invalid homunculus class %d.\n", atoi(split[0])); - return false; + if (hom != nullptr) { + return hom; } - skill_id = atoi(split[1]); - if (hom_skill_get_index(skill_id) == -1) { - ShowError("read_homunculus_skilldb: Invalid Homunculus skill '%s'.\n", split[1]); - return false; + for (const auto &homit : homunculus_db) { + hom = homit.second; + + if (hom->evo_class == class_) { + return hom; + } } - // Search an empty line or a line with the same skill_id (stored in idx) - ARR_FIND(0, MAX_HOM_SKILL_TREE, idx, !hskill_tree[class_idx][idx].id || hskill_tree[class_idx][idx].id == skill_id); - if (idx == MAX_HOM_SKILL_TREE) { - ShowWarning("Unable to load skill %d into homunculus %d's tree. Maximum number of skills per class has been reached.\n", skill_id, atoi(split[0])); - return false; - } - - hskill_tree[class_idx][idx].id = skill_id; - hskill_tree[class_idx][idx].max = atoi(split[2]); - hskill_tree[class_idx][idx].need_level = atoi(split[3]); - - for (i = 0; i < MAX_HOM_SKILL_REQUIRE; i++) { - hskill_tree[class_idx][idx].need[i].id = atoi(split[4+i*2]); - hskill_tree[class_idx][idx].need[i].lv = atoi(split[4+i*2+1]); - } - - hskill_tree[class_idx][idx].intimacy = atoi(split[14]); - return true; + return nullptr; } -/** -* Read homunculus skill db (check the files) -*/ -static void read_homunculus_skilldb(void) { - const char *filename[] = { "homun_skill_tree.txt", DBIMPORT"/homun_skill_tree.txt"}; - int i; - memset(hskill_tree,0,sizeof(hskill_tree)); - for (i = 0; i 0); - } -} +HomunculusDatabase homunculus_db; void hom_reload(void){ - read_homunculusdb(); + homunculus_db.load(); homun_exp_db.reload(); } -void hom_reload_skill(void){ - read_homunculus_skilldb(); -} - void do_init_homunculus(void){ int class_; - read_homunculusdb(); + homunculus_db.load(); homun_exp_db.load(); - read_homunculus_skilldb(); // Add homunc timer function to timer func list [Toms] add_timer_func_list(hom_hungry, "hom_hungry"); diff --git a/src/map/homunculus.hpp b/src/map/homunculus.hpp index 65b3edf732..6f05c0d09e 100644 --- a/src/map/homunculus.hpp +++ b/src/map/homunculus.hpp @@ -5,18 +5,20 @@ #define HOMUNCULUS_HPP #include + #include #include +#include "mob.hpp" #include "status.hpp" // struct status_data, struct status_change #include "unit.hpp" // struct unit_data #ifdef RENEWAL #define HOMUN_LEVEL_STATWEIGHT_VALUE 0 #define APPLY_HOMUN_LEVEL_STATWEIGHT()( \ - hom->str_value = hom->agi_value = \ - hom->vit_value = hom->int_value = \ - hom->dex_value = hom->luk_value = hom->level / 10 - HOMUN_LEVEL_STATWEIGHT_VALUE \ + hom.str_value = hom.agi_value = \ + hom.vit_value = hom.int_value = \ + hom.dex_value = hom.luk_value = hom.level / 10 - HOMUN_LEVEL_STATWEIGHT_VALUE \ ) #else #define APPLY_HOMUN_LEVEL_STATWEIGHT() @@ -40,23 +42,33 @@ public: t_exp get_nextexp(uint16 level); }; -struct h_stats { +struct s_hom_stats { unsigned int HP, SP; unsigned short str, agi, vit, int_, dex, luk; }; +/// Homunculus skill entry [Celest] +struct s_homun_skill_tree_entry { + uint16 id; ///< Skill ID + uint16 max; ///< Max level for this tree + uint16 need_level; ///< Homunculus level required + uint32 intimacy; ///< Intimacy required (n/100) + bool evolution; ///< Require evolution to show on skill tree + std::unordered_map need; ///< Skills needed +}; + struct s_homunculus_db { int base_class, evo_class; char name[NAME_LENGTH]; - struct h_stats base, gmin, gmax, emin, emax; + struct s_hom_stats base, gmin, gmax, emin, emax; int foodID; - int baseASPD; - long hungryDelay; - unsigned char element, race, base_size, evo_size; + uint16 baseASPD; + int hungryDelay; + e_element element; + e_race race; + e_size base_size, evo_size; + std::vector skill_tree; }; -extern struct s_homunculus_db homunculus_db[MAX_HOMUNCULUS_CLASS]; - -enum e_hom_search_type : uint8 { HOMUNCULUS_CLASS, HOMUNCULUS_FOOD }; enum e_hom_mode : uint8 { MH_MD_FIGHTING = 1, MH_MD_GRAPPLING }; @@ -79,7 +91,7 @@ struct homun_data { struct status_data base_status, battle_status; status_change sc; struct regen_data regen; - struct s_homunculus_db *homunculusDB; //[orn] + std::shared_ptr homunculusDB; //[orn] struct s_homunculus homunculus; //[orn] int masterteleport_timer; @@ -89,21 +101,6 @@ struct homun_data { std::vector blockskill; // [orn] }; -#define MAX_HOM_SKILL_REQUIRE 5 -#define MAX_HOM_SKILL_TREE 10 - -/// Homunculus skill entry [Celest] -struct homun_skill_tree_entry { - uint16 id; ///< Skill ID - uint8 max; ///< Max level for this tree - uint8 need_level; ///< Homunculus level required - uint16 intimacy; ///< Intimacy required (n/100) - struct { - uint16 id; ///< Skill ID - uint8 lv; ///< Level of skill - } need[MAX_HOM_SKILL_REQUIRE]; ///< Skills needed -}; - #define HOM_EVO 0x100 //256 #define HOM_S 0x200 //512 #define HOM_REG 0x1000 //4096 @@ -128,6 +125,33 @@ enum homun_mapid { MAPID_ELANOR, }; +/// Homunculus class constants +enum e_homun_classid : uint16 { + MER_LIF = 6001, + MER_AMISTR, + MER_FILIR, + MER_VANILMIRTH, + MER_LIF2, + MER_AMISTR2, + MER_FILIR2, + MER_VANILMIRTH2, + MER_LIF_H, + MER_AMISTR_H, + MER_FILIR_H, + MER_VANILMIRTH_H, + MER_LIF_H2, + MER_AMISTR_H2, + MER_FILIR_H2, + MER_VANILMIRTH_H2, + + // Homunculus S + MER_EIRA = 6048, + MER_BAYERI, + MER_SERA, + MER_DIETER, + MER_ELEANOR, +}; + /// Homunculus type enum homun_type : int8 { HT_REG = 0x1, @@ -158,6 +182,24 @@ enum e_homun_grade : uint8 { HOMGRADE_LOYAL, }; +class HomunculusDatabase : public TypesafeYamlDatabase { +private: + bool parseStatusNode(const std::string &nodeName, const std::string &subNodeName, const ryml::NodeRef &node, s_hom_stats &bonus); + +public: + HomunculusDatabase() : TypesafeYamlDatabase("HOMUNCULUS_DB", 1) { + + } + + const std::string getDefaultLocation(); + uint64 parseBodyNode(const ryml::NodeRef& node); + + // Additional + std::shared_ptr homun_search(int32 class_); +}; + +extern HomunculusDatabase homunculus_db; + /// Check Homunculus Class ID #define homdb_checkid(id) ((id) >= HM_CLASS_BASE && (id) <= HM_CLASS_MAX) @@ -170,9 +212,9 @@ enum homun_type hom_class2type(int class_); void hom_damage(struct homun_data *hd); int hom_dead(struct homun_data *hd); void hom_skillup(struct homun_data *hd,uint16 skill_id); -void hom_calc_skilltree(struct homun_data *hd, bool flag_evolve); +void hom_calc_skilltree(homun_data *hd); short hom_checkskill(struct homun_data *hd,uint16 skill_id); -uint8 hom_skill_get_min_level(int class_, uint16 skill_id); +uint16 hom_skill_get_min_level(int class_, uint16 skill_id); void hom_gainexp(struct homun_data *hd,t_exp exp); int hom_levelup(struct homun_data *hd); int hom_evolution(struct homun_data *hd); @@ -186,7 +228,6 @@ int hom_shuffle(struct homun_data *hd); // [Zephyrus] void hom_save(struct homun_data *hd); bool hom_call(map_session_data *sd); bool hom_create_request(map_session_data *sd, int class_); -int hom_search(int key,int type); void hom_menu(map_session_data *sd,int type); int hom_food(map_session_data *sd, struct homun_data *hd); int hom_hungry_timer_delete(struct homun_data *hd); @@ -198,7 +239,6 @@ int hom_increase_intimacy(struct homun_data * hd, unsigned int value); int hom_decrease_intimacy(struct homun_data * hd, unsigned int value); int hom_skill_tree_get_max(int skill_id, int b_class); void hom_init_timers(struct homun_data * hd); -void hom_reload_skill(void); void hom_reload(void); void hom_addspiritball(TBL_HOM *hd, int max); diff --git a/src/map/itemdb.hpp b/src/map/itemdb.hpp index 8aa638bfa4..4a7abd8d32 100644 --- a/src/map/itemdb.hpp +++ b/src/map/itemdb.hpp @@ -48,6 +48,7 @@ enum item_itemid : t_itemid ITEMID_APPLE = 512, ITEMID_HOLY_WATER = 523, ITEMID_PUMPKIN = 535, + ITEMID_PET_FOOD = 537, ITEMID_RED_SLIM_POTION = 545, ITEMID_YELLOW_SLIM_POTION = 546, ITEMID_WHITE_SLIM_POTION = 547, diff --git a/src/map/map-server.vcxproj b/src/map/map-server.vcxproj index 1211c821a7..7367e8263e 100644 --- a/src/map/map-server.vcxproj +++ b/src/map/map-server.vcxproj @@ -334,8 +334,7 @@ - - + diff --git a/src/map/script_constants.hpp b/src/map/script_constants.hpp index 879e82407d..665d11caa0 100644 --- a/src/map/script_constants.hpp +++ b/src/map/script_constants.hpp @@ -10098,6 +10098,29 @@ export_constant(WOE_SECOND_EDITION); export_constant(WOE_THIRD_EDITION); + /* homunculus view IDs */ + export_constant(MER_LIF); + export_constant(MER_AMISTR); + export_constant(MER_FILIR); + export_constant(MER_VANILMIRTH); + export_constant(MER_LIF2); + export_constant(MER_AMISTR2); + export_constant(MER_FILIR2); + export_constant(MER_VANILMIRTH2); + export_constant(MER_LIF_H); + export_constant(MER_AMISTR_H); + export_constant(MER_FILIR_H); + export_constant(MER_VANILMIRTH_H); + export_constant(MER_LIF_H2); + export_constant(MER_AMISTR_H2); + export_constant(MER_FILIR_H2); + export_constant(MER_VANILMIRTH_H2); + export_constant(MER_EIRA); + export_constant(MER_BAYERI); + export_constant(MER_SERA); + export_constant(MER_DIETER); + export_constant(MER_ELEANOR); + #undef export_constant #undef export_constant2 #undef export_parameter diff --git a/src/map/status.cpp b/src/map/status.cpp index 2282d343df..b6ef6f9fdf 100644 --- a/src/map/status.cpp +++ b/src/map/status.cpp @@ -4898,26 +4898,27 @@ int status_calc_mercenary_(s_mercenary_data *md, uint8 opt) int status_calc_homunculus_(struct homun_data *hd, uint8 opt) { struct status_data *status = &hd->base_status; - struct s_homunculus *hom = &hd->homunculus; + struct s_homunculus &hom = hd->homunculus; int skill_lv; int amotion; - status->str = hom->str / 10; - status->agi = hom->agi / 10; - status->vit = hom->vit / 10; - status->dex = hom->dex / 10; - status->int_ = hom->int_ / 10; - status->luk = hom->luk / 10; + status->str = hom.str / 10; + status->agi = hom.agi / 10; + status->vit = hom.vit / 10; + status->dex = hom.dex / 10; + status->int_ = hom.int_ / 10; + status->luk = hom.luk / 10; APPLY_HOMUN_LEVEL_STATWEIGHT(); if (opt&SCO_FIRST) { - const struct s_homunculus_db *db = hd->homunculusDB; + const std::shared_ptr db = hd->homunculusDB; + status->def_ele = db->element; status->ele_lv = 1; status->race = db->race; status->class_ = CLASS_NORMAL; - status->size = (hom->class_ == db->evo_class) ? db->evo_size : db->base_size; + status->size = (hom.class_ == db->evo_class) ? db->evo_size : db->base_size; status->rhw.range = 1 + status->size; status->mode = static_cast(MD_CANMOVE|MD_CANATTACK); status->speed = DEFAULT_WALK_SPEED; @@ -4932,13 +4933,13 @@ int status_calc_homunculus_(struct homun_data *hd, uint8 opt) #ifdef RENEWAL amotion = hd->homunculusDB->baseASPD; - amotion = amotion - amotion * (status->dex + hom->dex_value) / 1000 - (status->agi + hom->agi_value) * amotion / 250; + amotion = amotion - amotion * (status->dex + hom.dex_value) / 1000 - (status->agi + hom.agi_value) * amotion / 250; status->def = status->mdef = 0; #else - skill_lv = hom->level / 10 + status->vit / 5; + skill_lv = hom.level / 10 + status->vit / 5; status->def = cap_value(skill_lv, 0, 99); - skill_lv = hom->level / 10 + status->int_ / 5; + skill_lv = hom.level / 10 + status->int_ / 5; status->mdef = cap_value(skill_lv, 0, 99); amotion = (1000 - 4 * status->agi - status->dex) * hd->homunculusDB->baseASPD / 1000; @@ -4947,10 +4948,10 @@ int status_calc_homunculus_(struct homun_data *hd, uint8 opt) status->amotion = cap_value(amotion, battle_config.max_aspd, 2000); status->adelay = status->amotion; //It seems adelay = amotion for Homunculus. - status->max_hp = hom->max_hp; - status->max_sp = hom->max_sp; + status->max_hp = hom.max_hp; + status->max_sp = hom.max_sp; - hom_calc_skilltree(hd, 0); + hom_calc_skilltree(hd); if((skill_lv = hom_checkskill(hd, HAMI_SKIN)) > 0) status->def += skill_lv * 4; @@ -4992,18 +4993,18 @@ int status_calc_homunculus_(struct homun_data *hd, uint8 opt) } if (opt&SCO_FIRST) { - hd->battle_status.hp = hom->hp; - hd->battle_status.sp = hom->sp; - if(hom->class_ == 6052) // Eleanor + hd->battle_status.hp = hom.hp; + hd->battle_status.sp = hom.sp; + if(hom.class_ == 6052) // Eleanor sc_start(&hd->bl,&hd->bl, SC_STYLE_CHANGE, 100, MH_MD_FIGHTING, INFINITE_TICK); } #ifndef RENEWAL status->rhw.atk = status->dex; - status->rhw.atk2 = status->str + hom->level; + status->rhw.atk2 = status->str + hom.level; #endif - status_calc_misc(&hd->bl, status, hom->level); + status_calc_misc(&hd->bl, status, hom.level); status_cpy(&hd->battle_status, status); return 1; diff --git a/src/tool/csv2yaml.cpp b/src/tool/csv2yaml.cpp index aadbe6e3a6..e2d53bcdc1 100644 --- a/src/tool/csv2yaml.cpp +++ b/src/tool/csv2yaml.cpp @@ -56,6 +56,14 @@ static void item_txt_data(const std::string& modePath, const std::string& fixedP sv_readdb(modePath.c_str(), "item_trade.txt", ',', 3, 3, -1, &itemdb_read_itemtrade, false); } +// Homunculus database data to memory +static void homunculus_txt_data(const std::string& modePath, const std::string& fixedPath) { + hom_skill_tree.clear(); + + if (fileExists(modePath + "/homun_skill_tree.txt")) + sv_readdb(modePath.c_str(), "homun_skill_tree.txt", ',', 15, 15, -1, read_homunculus_skilldb, false); +} + // Mob database data to memory static void mob_txt_data(const std::string &modePath, const std::string &fixedPath) { mob_race2.clear(); @@ -546,6 +554,20 @@ bool Csv2YamlTool::initialize( int argc, char* argv[] ){ return 0; } + homunculus_txt_data(path_db, path_db); + if (!process("HOMUNCULUS_DB", 1, { path_db_mode }, "homunculus_db", [](const std::string& path, const std::string& name_ext) -> bool { + return sv_readdb(path.c_str(), name_ext.c_str(), ',', 50, 50, MAX_HOMUNCULUS_CLASS, read_homunculusdb, false); + })) { + return 0; + } + + homunculus_txt_data(path_db_import, path_db_import); + if (!process("HOMUNCULUS_DB", 1, { path_db_import }, "homunculus_db", [](const std::string& path, const std::string& name_ext) -> bool { + return sv_readdb(path.c_str(), name_ext.c_str(), ',', 50, 50, MAX_HOMUNCULUS_CLASS, read_homunculusdb, false); + })) { + return 0; + } + // TODO: add implementations ;-) return true; @@ -4970,6 +4992,292 @@ static bool cashshop_parse_dbrow( char* fields[], int columns, int current ){ return true; } +// homunculus_db.yml function +//--------------------------- +static bool read_homunculus_skilldb(char* split[], int columns, int current) { + s_homun_skill_tree_entry entry = {}; + + entry.id = atoi(split[1]); + entry.max = atoi(split[2]); + entry.need_level = atoi(split[3]); + entry.intimacy = cap_value(atoi(split[14]), 0, 1000); + + for (int i = 0; i < MAX_HOM_SKILL_REQUIRE; i++) { + if (atoi(split[4 + i * 2]) > 0) + entry.need.emplace(atoi(split[4 + i * 2]), atoi(split[4 + i * 2 + 1])); + } + + if (util::umap_find(hom_skill_tree, atoi(split[0]))) + hom_skill_tree[(uint16)atoi(split[0])].push_back(entry); + else { + hom_skill_tree[(uint16)atoi(split[0])] = std::vector(); + hom_skill_tree[(uint16)atoi(split[0])].push_back(entry); + } + + return true; +} + +static bool compareHomSkillId(const s_homun_skill_tree_entry &a, const s_homun_skill_tree_entry &b) { + return a.id < b.id; +} + +// Copied and adjusted from homunculus.cpp +static bool read_homunculusdb(char* str[], int columns, int current) { + bool has_evo = false; + + body << YAML::BeginMap; + body << YAML::Key << "Class" << YAML::Value << name2Upper(constant_lookup(atoi(str[0]), "MER_") + 4); + body << YAML::Key << "Name" << YAML::Value << str[2]; + if (atoi(str[0]) != atoi(str[1])) { + body << YAML::Key << "EvolutionClass" << YAML::Value << name2Upper(constant_lookup(atoi(str[1]), "MER_") + 4); + has_evo = true; + } + if (atoi(str[3]) != ITEMID_PET_FOOD) + body << YAML::Key << "Food" << YAML::Value << name2Upper(*util::umap_find(aegis_itemnames, (t_itemid)atoi(str[3]))); + if (atoi(str[4]) != 60000) + body << YAML::Key << "HungryDelay" << YAML::Value << atoi(str[4]); + + if (atoi(str[7]) != RC_DEMIHUMAN) + body << YAML::Key << "Race" << YAML::Value << name2Upper(constant_lookup(atoi(str[7]), "RC_") + 3); + if (atoi(str[8]) != ELE_NEUTRAL) + body << YAML::Key << "Element" << YAML::Value << name2Upper(constant_lookup(atoi(str[8]), "ELE_") + 4); + if (atoi(str[5]) != SZ_SMALL) + body << YAML::Key << "Size" << YAML::Value << name2Upper(constant_lookup(atoi(str[5]), "Size_") + 5); + if (atoi(str[6]) != SZ_MEDIUM) + body << YAML::Key << "EvolutionSize" << YAML::Value << name2Upper(constant_lookup(atoi(str[6]), "Size_") + 5); + if (atoi(str[9]) != 700) + body << YAML::Key << "AttackDelay" << YAML::Value << atoi(str[9]); + + body << YAML::Key << "Status"; + body << YAML::BeginSeq; + + body << YAML::BeginMap; + body << YAML::Key << "Type" << YAML::Value << "Hp"; + body << YAML::Key << "Base" << YAML::Value << atoi(str[10]); + body << YAML::Key << "GrowthMinimum" << YAML::Value << atoi(str[18]); + body << YAML::Key << "GrowthMaximum" << YAML::Value << atoi(str[19]); + if (has_evo) { + body << YAML::Key << "EvolutionMinimum" << YAML::Value << atoi(str[34]); + body << YAML::Key << "EvolutionMaximum" << YAML::Value << atoi(str[35]); + } + body << YAML::EndMap; + + body << YAML::BeginMap; + body << YAML::Key << "Type" << YAML::Value << "Sp"; + body << YAML::Key << "Base" << YAML::Value << atoi(str[11]); + body << YAML::Key << "GrowthMinimum" << YAML::Value << atoi(str[20]); + body << YAML::Key << "GrowthMaximum" << YAML::Value << atoi(str[21]); + if (has_evo) { + body << YAML::Key << "EvolutionMinimum" << YAML::Value << atoi(str[36]); + body << YAML::Key << "EvolutionMaximum" << YAML::Value << atoi(str[37]); + } + body << YAML::EndMap; + + body << YAML::BeginMap; + body << YAML::Key << "Type" << YAML::Value << "Str"; + body << YAML::Key << "Base" << YAML::Value << atoi(str[12]); + body << YAML::Key << "GrowthMinimum" << YAML::Value << atoi(str[22]); + body << YAML::Key << "GrowthMaximum" << YAML::Value << atoi(str[23]); + if (has_evo) { + body << YAML::Key << "EvolutionMinimum" << YAML::Value << atoi(str[38]); + body << YAML::Key << "EvolutionMaximum" << YAML::Value << atoi(str[39]); + } + body << YAML::EndMap; + + body << YAML::BeginMap; + body << YAML::Key << "Type" << YAML::Value << "Agi"; + body << YAML::Key << "Base" << YAML::Value << atoi(str[13]); + body << YAML::Key << "GrowthMinimum" << YAML::Value << atoi(str[24]); + body << YAML::Key << "GrowthMaximum" << YAML::Value << atoi(str[25]); + if (has_evo) { + body << YAML::Key << "EvolutionMinimum" << YAML::Value << atoi(str[40]); + body << YAML::Key << "EvolutionMaximum" << YAML::Value << atoi(str[41]); + } + body << YAML::EndMap; + + body << YAML::BeginMap; + body << YAML::Key << "Type" << YAML::Value << "Vit"; + body << YAML::Key << "Base" << YAML::Value << atoi(str[14]); + body << YAML::Key << "GrowthMinimum" << YAML::Value << atoi(str[26]); + body << YAML::Key << "GrowthMaximum" << YAML::Value << atoi(str[27]); + if (has_evo) { + body << YAML::Key << "EvolutionMinimum" << YAML::Value << atoi(str[42]); + body << YAML::Key << "EvolutionMaximum" << YAML::Value << atoi(str[43]); + } + body << YAML::EndMap; + + body << YAML::BeginMap; + body << YAML::Key << "Type" << YAML::Value << "Int"; + body << YAML::Key << "Base" << YAML::Value << atoi(str[15]); + body << YAML::Key << "GrowthMinimum" << YAML::Value << atoi(str[28]); + body << YAML::Key << "GrowthMaximum" << YAML::Value << atoi(str[29]); + if (has_evo) { + body << YAML::Key << "EvolutionMinimum" << YAML::Value << atoi(str[44]); + body << YAML::Key << "EvolutionMaximum" << YAML::Value << atoi(str[45]); + } + body << YAML::EndMap; + + body << YAML::BeginMap; + body << YAML::Key << "Type" << YAML::Value << "Dex"; + body << YAML::Key << "Base" << YAML::Value << atoi(str[16]); + body << YAML::Key << "GrowthMinimum" << YAML::Value << atoi(str[30]); + body << YAML::Key << "GrowthMaximum" << YAML::Value << atoi(str[31]); + if (has_evo) { + body << YAML::Key << "EvolutionMinimum" << YAML::Value << atoi(str[46]); + body << YAML::Key << "EvolutionMaximum" << YAML::Value << atoi(str[47]); + } + body << YAML::EndMap; + + body << YAML::BeginMap; + body << YAML::Key << "Type" << YAML::Value << "Luk"; + body << YAML::Key << "Base" << YAML::Value << atoi(str[17]); + body << YAML::Key << "GrowthMinimum" << YAML::Value << atoi(str[32]); + body << YAML::Key << "GrowthMaximum" << YAML::Value << atoi(str[33]); + if (has_evo) { + body << YAML::Key << "EvolutionMinimum" << YAML::Value << atoi(str[48]); + body << YAML::Key << "EvolutionMaximum" << YAML::Value << atoi(str[49]); + } + body << YAML::EndMap; + + body << YAML::EndSeq; + + // Gather and sort skill tree data + std::vector *skill_tree_base = nullptr, *skill_tree_evo = nullptr; + + skill_tree_base = util::umap_find(hom_skill_tree, atoi(str[0])); + std::sort(skill_tree_base->begin(), skill_tree_base->end(), compareHomSkillId); + + if (has_evo) { + skill_tree_evo = util::umap_find(hom_skill_tree, atoi(str[1])); + std::sort(skill_tree_evo->begin(), skill_tree_evo->end(), compareHomSkillId); + } + + // Get a difference between the base class and evo class skill tree to find skills granted through evolution + std::vector tree_diff; + + if (skill_tree_evo != nullptr) { + for (const auto &evoit : *skill_tree_evo) { + bool contains = false; + + for (const auto &baseit : *skill_tree_base) { + if (baseit.id == evoit.id) { + contains = true; + break; + } + } + + if (!contains) { + // Skill is not part of the base tree, we can only assume it's an evolution granted skill thanks to brilliant formatting of our TXT homun_db! + tree_diff.push_back(evoit); + } + } + } + + if (skill_tree_base != nullptr) { + body << YAML::Key << "SkillTree"; + body << YAML::BeginSeq; + + for (const auto &skillit : *skill_tree_base) { + std::string *skill_name = util::umap_find(aegis_skillnames, skillit.id); + + if (skill_name == nullptr) { + ShowError("Skill name for homunculus skill ID %hu is not known.\n", skillit.id); + return false; + } + + body << YAML::BeginMap; + body << YAML::Key << "Skill" << YAML::Value << *skill_name; + body << YAML::Key << "MaxLevel" << YAML::Value << (int)skillit.max; + if (skillit.need_level > 0) + body << YAML::Key << "RequiredLevel" << YAML::Value << (int)skillit.need_level; + if (skillit.intimacy > 0) + body << YAML::Key << "RequiredIntimacy" << YAML::Value << skillit.intimacy; + + if (!skillit.need.empty()) { + body << YAML::Key << "Required"; + body << YAML::BeginSeq; + + for (const auto &it : skillit.need) { + uint16 required_skill_id = it.first; + uint16 required_skill_level = it.second; + + if (required_skill_id == 0 || required_skill_level == 0) + continue; + + std::string *required_name = util::umap_find(aegis_skillnames, required_skill_id); + + if (required_name == nullptr) { + ShowError("Skill name for required skill id %hu is not known.\n", required_skill_id); + return false; + } + + body << YAML::BeginMap; + body << YAML::Key << "Skill" << YAML::Value << *required_name; + body << YAML::Key << "Level" << YAML::Value << required_skill_level; + body << YAML::EndMap; + } + + body << YAML::EndSeq; + } + + body << YAML::EndMap; + } + + for (const auto &skillit : tree_diff) { + std::string *skill_name = util::umap_find(aegis_skillnames, skillit.id); + + if (skill_name == nullptr) { + ShowError("Skill name for homunculus skill ID %hu is not known.\n", skillit.id); + return false; + } + + body << YAML::BeginMap; + body << YAML::Key << "Skill" << YAML::Value << *skill_name; + body << YAML::Key << "MaxLevel" << YAML::Value << (int)skillit.max; + if (skillit.need_level > 0) + body << YAML::Key << "RequiredLevel" << YAML::Value << (int)skillit.need_level; + if (skillit.intimacy > 0) + body << YAML::Key << "RequiredIntimacy" << YAML::Value << skillit.intimacy; + body << YAML::Key << "RequireEvolution" << YAML::Value << "true"; + + if (!skillit.need.empty()) { + body << YAML::Key << "Required"; + body << YAML::BeginSeq; + + for (const auto &it : skillit.need) { + uint16 required_skill_id = it.first; + uint16 required_skill_level = it.second; + + if (required_skill_id == 0 || required_skill_level == 0) + continue; + + std::string *required_name = util::umap_find(aegis_skillnames, required_skill_id); + + if (required_name == nullptr) { + ShowError("Skill name for required skill id %hu is not known.\n", required_skill_id); + return false; + } + + body << YAML::BeginMap; + body << YAML::Key << "Skill" << YAML::Value << *required_name; + body << YAML::Key << "Level" << YAML::Value << required_skill_level; + body << YAML::EndMap; + } + + body << YAML::EndSeq; + } + + body << YAML::EndMap; + } + + body << YAML::EndSeq; + } + + body << YAML::EndMap; + + return true; +} + int main( int argc, char *argv[] ){ return main_core( argc, argv ); } diff --git a/src/tool/csv2yaml.hpp b/src/tool/csv2yaml.hpp index 10cf28de48..7d8317f1e5 100644 --- a/src/tool/csv2yaml.hpp +++ b/src/tool/csv2yaml.hpp @@ -41,6 +41,7 @@ namespace rathena{ #define MAX_PC_SKILL_REQUIRE 5 /// Max skill tree requirement ///Maximum amount of items a combo may require #define MAX_ITEMS_PER_COMBO 6 +#define MAX_HOM_SKILL_REQUIRE 5 struct s_skill_tree_entry_csv { std::string skill_name; @@ -62,6 +63,8 @@ std::unordered_map skill_unit; std::unordered_map skill_copyable; std::unordered_map skill_nearnpc; +std::unordered_map> hom_skill_tree; + static unsigned int level_penalty[3][CLASS_MAX][MAX_LEVEL * 2 + 1]; struct s_item_flag_csv2yaml { @@ -531,5 +534,7 @@ static bool pc_readdb_skilltree(char* str[], int columns, int current); static bool pc_readdb_skilltree_yaml(void); static bool itemdb_read_combos(const char* file); static bool cashshop_parse_dbrow( char* fields[], int columns, int current ); +static bool read_homunculus_skilldb(char* split[], int columns, int current); +static bool read_homunculusdb(char* str[], int columns, int current); #endif /* CSV2YAML_HPP */ diff --git a/src/tool/yaml.hpp b/src/tool/yaml.hpp index b1a8034128..8be5b7a137 100644 --- a/src/tool/yaml.hpp +++ b/src/tool/yaml.hpp @@ -45,8 +45,9 @@ #include "../map/channel.hpp" #include "../map/chat.hpp" #include "../map/date.hpp" -#include "../map/instance.hpp" #include "../map/elemental.hpp" +#include "../map/homunculus.hpp" +#include "../map/instance.hpp" #include "../map/mercenary.hpp" #include "../map/mob.hpp" #include "../map/npc.hpp" diff --git a/src/tool/yaml2sql.cpp b/src/tool/yaml2sql.cpp index a3c3437f3d..a0fd3be4c8 100644 --- a/src/tool/yaml2sql.cpp +++ b/src/tool/yaml2sql.cpp @@ -43,6 +43,7 @@ #include "../map/chat.hpp" #include "../map/date.hpp" #include "../map/elemental.hpp" +#include "../map/homunculus.hpp" #include "../map/instance.hpp" #include "../map/mercenary.hpp" #include "../map/mob.hpp"