diff --git a/db/import-tmpl/improvise_db.yml b/db/import-tmpl/improvise_db.yml new file mode 100644 index 0000000000..2c0afce465 --- /dev/null +++ b/db/import-tmpl/improvise_db.yml @@ -0,0 +1,31 @@ +# This file is a part of rAthena. +# Copyright(C) 2019 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 . +# +########################################################################### +# Improvised Song Database +########################################################################### +# +# Improvised Song Settings +# +########################################################################### +# - Skill Skill to be casted by Improvised Song. +# Probability Probability of skill compared to others in database (1 = 0.01%, 10000 = 100%). +########################################################################### + +Header: + Type: IMPROVISED_SONG_DB + Version: 1 diff --git a/db/improvise_db.yml b/db/improvise_db.yml new file mode 100644 index 0000000000..0e53f56229 --- /dev/null +++ b/db/improvise_db.yml @@ -0,0 +1,37 @@ +# This file is a part of rAthena. +# Copyright(C) 2019 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 . +# +########################################################################### +# Improvised Song Database +########################################################################### +# +# Improvised Song Settings +# +########################################################################### +# - Skill Skill to be casted by Improvised Song. +# Probability Probability of skill compared to others in database (1 = 0.01%, 10000 = 100%). +########################################################################### + +Header: + Type: IMPROVISED_SONG_DB + Version: 1 + +Footer: + Imports: + - Path: db/re/improvise_db.yml + Mode: Renewal + - Path: db/import/improvise_db.yml diff --git a/db/re/improvise_db.yml b/db/re/improvise_db.yml new file mode 100644 index 0000000000..4961d20f8d --- /dev/null +++ b/db/re/improvise_db.yml @@ -0,0 +1,65 @@ +# This file is a part of rAthena. +# Copyright(C) 2019 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 . +# +########################################################################### +# Improvised Song Database +########################################################################### +# +# Improvised Song Settings +# +########################################################################### +# - Skill Skill to be casted by Improvised Song. +# Probability Probability of skill compared to others in database (1 = 0.01%, 10000 = 100%). +########################################################################### + +Header: + Type: IMPROVISED_SONG_DB + Version: 1 + +Body: + - Skill: MG_NAPALMBEAT + Probability: 6000 + - Skill: MG_SAFETYWALL + Probability: 4000 + - Skill: MG_SOULSTRIKE + Probability: 6000 + - Skill: MG_COLDBOLT + Probability: 6000 + - Skill: MG_FROSTDIVER + Probability: 6000 + - Skill: MG_FIREBALL + Probability: 6000 + - Skill: MG_FIREWALL + Probability: 4000 + - Skill: MG_FIREBOLT + Probability: 6000 + - Skill: MG_LIGHTNINGBOLT + Probability: 6000 + - Skill: MG_THUNDERSTORM + Probability: 4000 + - Skill: WZ_FIREPILLAR + Probability: 4000 + - Skill: WZ_METEOR + Probability: 4000 + - Skill: WZ_JUPITEL + Probability: 6000 + - Skill: WZ_VERMILION + Probability: 4000 + - Skill: WZ_WATERBALL + Probability: 6000 + - Skill: WZ_STORMGUST + Probability: 4000 diff --git a/db/skill_improvise_db.txt b/db/skill_improvise_db.txt deleted file mode 100644 index cfd2a3c518..0000000000 --- a/db/skill_improvise_db.txt +++ /dev/null @@ -1,24 +0,0 @@ -// Improvise Database -// Database for skills that can be summoned trough Randomize Spell/Improvised Song (Minstrel/Wanderer Skill). -// -// Structure of Database: -// SkillID,Rate -// -// - To remove entry by importing, put 0 on 'Rate' - -11,60000 // Napalm Beat -12,40000 // Safety Wall -13,60000 // Soul Strike -14,60000 // Cold Bolt -15,60000 // Frost Diver -17,60000 // Fire Ball -18,40000 // Fire Wall -19,60000 // Fire Bolt -20,60000 // Lightning Bolt -21,40000 // Thunderstorm -80,40000 // Fire Pillar -83,40000 // Meteor Storm -84,60000 // Jupitel Thunder -85,40000 // Lord of Vermilion -86,60000 // Water Ball -89,40000 // Storm Gust diff --git a/doc/yaml/db/improvise_db.yml b/doc/yaml/db/improvise_db.yml new file mode 100644 index 0000000000..aa84b08bd3 --- /dev/null +++ b/doc/yaml/db/improvise_db.yml @@ -0,0 +1,10 @@ +########################################################################### +# Improvised Song Database +########################################################################### +# +# Improvised Song Settings +# +########################################################################### +# - Skill Skill to be casted by Improvised Song. +# Probability Probability of skill compared to others in database (1 = 0.01%, 10000 = 100%). +########################################################################### diff --git a/src/map/map-server.vcxproj b/src/map/map-server.vcxproj index dca3993ecb..35d6146dde 100644 --- a/src/map/map-server.vcxproj +++ b/src/map/map-server.vcxproj @@ -305,6 +305,7 @@ + @@ -363,7 +364,6 @@ - diff --git a/src/map/skill.cpp b/src/map/skill.cpp index f50c774132..6fb993d8f2 100755 --- a/src/map/skill.cpp +++ b/src/map/skill.cpp @@ -89,12 +89,7 @@ static unsigned short skill_arrow_count; AbraDatabase abra_db; -struct s_skill_improvise_db { - uint16 skill_id; - unsigned short per;//1-10000 -}; -struct s_skill_improvise_db skill_improvise_db[MAX_SKILL_IMPROVISE_DB]; -static unsigned short skill_improvise_count; +ImprovisedSongDatabase improvised_song_db; #define MAX_SKILL_CHANGEMATERIAL_DB 75 #define MAX_SKILL_CHANGEMATERIAL_SET 3 @@ -10214,16 +10209,22 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui break; case WM_RANDOMIZESPELL: - if (!skill_improvise_count) { + if (improvised_song_db.empty()) { clif_skill_nodamage (src, bl, skill_id, skill_lv, 1); break; } else { - int improv_skill_id = 0, improv_skill_lv, checked = 0, checked_max = MAX_SKILL_IMPROVISE_DB*3; + int improv_skill_id = 0, improv_skill_lv, checked = 0, checked_max = improvised_song_db.size() * 3; + do { - i = rnd() % MAX_SKILL_IMPROVISE_DB; - improv_skill_id = skill_improvise_db[i].skill_id; - } while( checked++ < checked_max && (improv_skill_id == 0 || rnd()%10000 >= skill_improvise_db[i].per) ); + auto improvise_spell = improvised_song_db.random(); + + improv_skill_id = improvise_spell->skill_id; + + if( rnd() % 10000 >= improvise_spell->per ){ + break; + } + } while (checked++ < checked_max); if (!skill_get_index(improv_skill_id)) { if (sd) @@ -10231,7 +10232,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui break; } - improv_skill_lv = 4 + skill_lv; + improv_skill_lv = min(4 + skill_lv, skill_get_max(improv_skill_id)); clif_skill_nodamage (src, bl, skill_id, skill_lv, 1); if( sd ) { @@ -21455,40 +21456,58 @@ static bool skill_parse_row_spellbookdb(char* split[], int columns, int current) return false; } -/** Reads improvise db - * Structure: SkillID,Rate - */ -static bool skill_parse_row_improvisedb(char* split[], int columns, int current) -{ - unsigned short skill_id = atoi(split[0]), per = atoi(split[1]), i; - if( !skill_get_index(skill_id) || !skill_get_max(skill_id) ) { - ShowError("skill_parse_row_improvisedb: Invalid skill ID %d\n", skill_id); - return false; - } - if ( !skill_get_inf(skill_id) ) { - ShowError("skill_parse_row_improvisedb: Passive skills cannot be casted (%d/%s)\n", skill_id, skill_get_name(skill_id)); - return false; - } - ARR_FIND(0, skill_improvise_count, i, skill_improvise_db[i].skill_id == skill_id); - if (i >= ARRAYLENGTH(skill_improvise_db)) { - ShowError("skill_parse_row_improvisedb: Maximum amount of entries reached (%d), increase MAX_SKILL_IMPROVISE_DB\n",MAX_SKILL_IMPROVISE_DB); - return false; - } - // Import just for clearing/disabling from original data - if (per == 0) { - memset(&skill_improvise_db[i], 0, sizeof(skill_improvise_db[i])); - //ShowInfo("skill_parse_row_improvisedb: Skill %d removed from list.\n", skill_id); - return true; +const std::string ImprovisedSongDatabase::getDefaultLocation() { + return std::string(db_path) + "/improvise_db.yml"; +} + +/** +* Reads and parses an entry from the improvise_db. +* @param node: YAML node containing the entry. +* @return count of successfully parsed rows +*/ +uint64 ImprovisedSongDatabase::parseBodyNode(const YAML::Node &node) { + std::string skill_name; + + if (!this->asString(node, "Skill", skill_name)) + return 0; + + uint16 skill_id = skill_name2id(skill_name.c_str()); + + if (!skill_id) { + this->invalidWarning(node["Skill"], "Invalid Improvised Song skill name \"%s\", skipping.\n", skill_name.c_str()); + return 0; } - skill_improvise_db[i].skill_id = skill_id; - skill_improvise_db[i].per = per; // Still need confirm it. + if (!skill_get_inf(skill_id)) { + this->invalidWarning(node["Skill"], "Passive skill %s cannot be casted by Improvised Song.\n", skill_name.c_str()); + return 0; + } - if (i == skill_improvise_count) - skill_improvise_count++; + std::shared_ptr improvise = this->find(skill_id); + bool exists = improvise != nullptr; - return true; + if (!exists) { + if (!this->nodesExist(node, { "Probability" })) + return 0; + + improvise = std::make_shared(); + improvise->skill_id = skill_id; + } + + if (this->nodeExists(node, "Probability")) { + uint16 probability; + + if (!this->asUInt16Rate(node, "Probability", probability)) + return 0; + + improvise->per = probability; + } + + if (!exists) + this->put(skill_id, improvise); + + return 1; } const std::string MagicMushroomDatabase::getDefaultLocation() { @@ -21826,7 +21845,7 @@ static void skill_readdb(void) memset(skill_arrow_db,0,sizeof(skill_arrow_db)); memset(skill_spellbook_db,0,sizeof(skill_spellbook_db)); memset(skill_changematerial_db,0,sizeof(skill_changematerial_db)); - skill_produce_count = skill_arrow_count = skill_improvise_count = + skill_produce_count = skill_arrow_count = skill_changematerial_count = skill_spellbook_count = 0; for(i=0; i 0); sv_readdb(dbsubpath1, "spellbook_db.txt" , ',', 3, 3, MAX_SKILL_SPELLBOOK_DB, skill_parse_row_spellbookdb, i > 0); sv_readdb(dbsubpath1, "skill_copyable_db.txt" , ',', 2, 4, -1, skill_parse_row_copyabledb, i > 0); - sv_readdb(dbsubpath1, "skill_improvise_db.txt" , ',', 2, 2, MAX_SKILL_IMPROVISE_DB, skill_parse_row_improvisedb, i > 0); sv_readdb(dbsubpath1, "skill_changematerial_db.txt" , ',', 5, 5+2*MAX_SKILL_CHANGEMATERIAL_SET, MAX_SKILL_CHANGEMATERIAL_DB, skill_parse_row_changematerialdb, i > 0); sv_readdb(dbsubpath1, "skill_nonearnpc_db.txt" , ',', 2, 3, -1, skill_parse_row_nonearnpcrangedb, i > 0); sv_readdb(dbsubpath1, "skill_damage_db.txt" , ',', 4, 3+SKILLDMG_MAX, -1, skill_parse_row_skilldamage, i > 0); @@ -21864,6 +21882,7 @@ static void skill_readdb(void) } abra_db.load(); + improvised_song_db.load(); magic_mushroom_db.load(); skill_init_unit_layout(); diff --git a/src/map/skill.hpp b/src/map/skill.hpp index 1ff32424bd..e427f03fc2 100644 --- a/src/map/skill.hpp +++ b/src/map/skill.hpp @@ -28,7 +28,6 @@ struct status_change_entry; #define MAX_PRODUCE_RESOURCE 12 /// Max Produce requirements #define MAX_SKILL_ARROW_DB 150 /// Max Arrow Creation DB #define MAX_ARROW_RESULT 5 /// Max Arrow results/created -#define MAX_SKILL_IMPROVISE_DB 30 /// Max Skill for Improvise #define MAX_SKILL_LEVEL 13 /// Max Skill Level (for skill_db storage) #define MAX_MOBSKILL_LEVEL 100 /// Max monster skill level (on skill usage) #define MAX_SKILL_CRIMSON_MARKER 3 /// Max Crimson Marker targets (RL_C_MARKER) @@ -385,6 +384,20 @@ public: uint64 parseBodyNode(const YAML::Node& node); }; +struct s_skill_improvise_db { + uint16 skill_id, per; +}; + +class ImprovisedSongDatabase : public TypesafeYamlDatabase { +public: + ImprovisedSongDatabase() : TypesafeYamlDatabase("IMPROVISED_SONG_DB", 1) { + + } + + const std::string getDefaultLocation(); + uint64 parseBodyNode(const YAML::Node& node); +}; + void do_init_skill(void); void do_final_skill(void); diff --git a/src/tool/csv2yaml.cpp b/src/tool/csv2yaml.cpp index f65564beb8..e88f87001c 100644 --- a/src/tool/csv2yaml.cpp +++ b/src/tool/csv2yaml.cpp @@ -71,6 +71,7 @@ static bool guild_read_guildskill_tree_db( char* split[], int columns, int curre static size_t pet_read_db( const char* file ); static bool skill_parse_row_magicmushroomdb(char* split[], int column, int current); static bool skill_parse_row_abradb(char* split[], int columns, int current); +static bool skill_parse_row_improvisedb(char* split[], int columns, int current); // Constants for conversion std::unordered_map aegis_itemnames; @@ -155,11 +156,11 @@ void finalizeBody(void) { } template -bool process( const std::string& type, uint32 version, const std::vector& paths, const std::string& name, Func lambda ){ +bool process( const std::string& type, uint32 version, const std::vector& paths, const std::string& name, Func lambda, const std::string& rename = "" ){ for( const std::string& path : paths ){ const std::string name_ext = name + ".txt"; const std::string from = path + "/" + name_ext; - const std::string to = path + "/" + name + ".yml"; + const std::string to = path + "/" + (rename.size() > 0 ? rename : name) + ".yml"; if( fileExists( from ) ){ if( !askConfirmation( "Found the file \"%s\", which requires migration to yml.\nDo you want to convert it now? (Y/N)\n", from.c_str() ) ){ @@ -181,7 +182,7 @@ bool process( const std::string& type, uint32 version, const std::vector 0 ? rename : name)); prepareBody(); if( !lambda( path, name_ext ) ){ @@ -195,7 +196,7 @@ bool process( const std::string& type, uint32 version, const std::vector bool { + return sv_readdb(path.c_str(), name_ext.c_str(), ',', 2, 2, -1, &skill_parse_row_improvisedb, false); + }, "improvise_db")) { + return 0; + } + // TODO: add implementations ;-) return 0; @@ -714,7 +721,7 @@ static bool skill_parse_row_abradb(char* split[], int columns, int current) std::string *skill_name = util::umap_find(aegis_skillnames, skill_id); if (skill_name == nullptr) { - ShowError("Skill name for Abra skill ID &hu is not known.\n", skill_id); + ShowError("Skill name for Abra skill ID %hu is not known.\n", skill_id); return false; } @@ -746,3 +753,21 @@ static bool skill_parse_row_abradb(char* split[], int columns, int current) return true; } + +static bool skill_parse_row_improvisedb(char* split[], int columns, int current) +{ + uint16 skill_id = atoi(split[0]); + std::string *skill_name = util::umap_find(aegis_skillnames, skill_id); + + if (skill_name == nullptr) { + ShowError("Skill name for Improvised Song skill ID %hu is not known.\n", skill_id); + return false; + } + + body << YAML::BeginMap; + body << YAML::Key << "Skill" << YAML::Value << *skill_name; + body << YAML::Key << "Probability" << YAML::Value << atoi(split[1]) / 10; + body << YAML::EndMap; + + return true; +}