diff --git a/db/import-tmpl/mob_item_ratio.txt b/db/import-tmpl/mob_item_ratio.txt deleted file mode 100644 index d085939c5b..0000000000 --- a/db/import-tmpl/mob_item_ratio.txt +++ /dev/null @@ -1,30 +0,0 @@ -// Specific Item Drop Ratio Database -// Overrides for global item_rate* values from conf/battle/drops.conf -// -// Structure of Database: -// ItemID,Ratio{,MonsterID1,...,MonsterID10} -// -// Result: -// ItemID base drop rates defined in mob_db will not get multiplied -// by global item_rate* values (aka drop rates) from -// conf/battle/drops.conf. Instead Ratio will be used (100 = 1x). -// If no MonsterID is specified, all monsters will be affected, -// otherwise only listed ones. -// -// Examples: -// 909,100 // Jellopies from monsters will drop with 1x drop rate regardless of global drop rate -// 909,1000 // Jellopies from monsters will drop with 10x drop rate regardless of global drop rate -// 909,100,1002 // Jellopies from Porings will drop with 1x drop rate. Other monsters that drop Jellopies are unaffected (use global drop rate). -// -// Notes: -// - By default you can list up to 10 MonsterIDs per ItemID. -// It can be changed in src/map/mob.c by adjusting MAX_ITEMRATIO_MOBS. -// - Only ItemIDs up to MAX_ITEMDB are supported (default: 32768). -// - Does not override item_drop_*_min/max settings. -// - Does not affect card/item-granted drops. To adjust card/item-granted -// drops, edit them in item_db. -// - Does affect MVP prizes and Treasure Boxes. -// - You can add only ONE line per ItemID. If you need various ratios -// for different monsters, override drop rate with Ratio=100 and edit -// base drop rates in mob_db. -// - This file is reloaded by @reloadmobdb. diff --git a/db/import-tmpl/mob_item_ratio.yml b/db/import-tmpl/mob_item_ratio.yml new file mode 100644 index 0000000000..083e942cc2 --- /dev/null +++ b/db/import-tmpl/mob_item_ratio.yml @@ -0,0 +1,33 @@ +# Specific Item Drop Ratio Database +# This file is a part of rAthena. +# Copyright(C) 2021 rAthena Development Team +# https://rathena.org - https://github.com/rathena +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +########################################################################### +# Mob Item Ratio Database +########################################################################### +# +# Mob Item Ratio Settings +# +########################################################################### +# - Item AegisName of item to adjust. +# Ratio Drop rate. +# List: List of monster(s) affected (format : true/false). (Defaulted to all monsters) +########################################################################### + +Header: + Type: MOB_ITEM_RATIO_DB + Version: 1 diff --git a/db/mob_item_ratio.txt b/db/mob_item_ratio.txt deleted file mode 100644 index 53f3ce74cd..0000000000 --- a/db/mob_item_ratio.txt +++ /dev/null @@ -1,30 +0,0 @@ -// Specific Item Drop Ratio Database -// Overrides for global item_rate* values from conf/battle/drops.conf -// -// Structure of Database: -// ItemID,Ratio{,MonsterID1,...,MonsterID10} -// -// Result: -// ItemID base drop rates defined in mob_db will not get multiplied -// by global item_rate* values (aka drop rates) from -// conf/battle/drops.conf. Instead Ratio will be used (100 = 1x). -// If no MonsterID is specified, all monsters will be affected, -// otherwise only listed ones. -// -// Examples: -// 909,100 // Jellopies from monsters will drop with 1x drop rate regardless of global drop rate -// 909,1000 // Jellopies from monsters will drop with 10x drop rate regardless of global drop rate -// 909,100,1002 // Jellopies from Porings will drop with 1x drop rate. Other monsters that drop Jellopies are unaffected (use global drop rate). -// -// Notes: -// - By default you can list up to 10 MonsterIDs per ItemID. -// It can be changed in src/map/mob.cpp by adjusting MAX_ITEMRATIO_MOBS. -// - Only ItemIDs up to MAX_ITEMDB are supported (default: 32768). -// - Does not override item_drop_*_min/max settings. -// - Does not affect card/item-granted drops. To adjust card/item-granted -// drops, edit them in item_db. -// - Does affect MVP prizes and Treasure Boxes. -// - You can add only ONE line per ItemID. If you need various ratios -// for different monsters, override drop rate with Ratio=100 and edit -// base drop rates in mob_db. -// - This file is reloaded by @reloadmobdb. diff --git a/db/mob_item_ratio.yml b/db/mob_item_ratio.yml new file mode 100644 index 0000000000..b5a0a0810d --- /dev/null +++ b/db/mob_item_ratio.yml @@ -0,0 +1,37 @@ +# Specific Item Drop Ratio Database +# This file is a part of rAthena. +# Copyright(C) 2021 rAthena Development Team +# https://rathena.org - https://github.com/rathena +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +########################################################################### +# Mob Item Ratio Database +########################################################################### +# +# Mob Item Ratio Settings +# +########################################################################### +# - Item AegisName of item to adjust. +# Ratio Drop rate. +# List: List of monster(s) affected (format : true/false). (Defaulted to all monsters) +########################################################################### + +Header: + Type: MOB_ITEM_RATIO_DB + Version: 1 + +Footer: + Imports: + - Path: db/import/mob_item_ratio.yml diff --git a/doc/mob_item_ratio.txt b/doc/mob_item_ratio.txt new file mode 100644 index 0000000000..782dabf730 --- /dev/null +++ b/doc/mob_item_ratio.txt @@ -0,0 +1,43 @@ +//===== rAthena Documentation ================================ +//= Mob Item Ratio Database Structure +//===== By: ================================================== +//= rAthena Dev Team +//===== Last Updated: ======================================== +//= 20210624 +//===== Description: ========================================= +//= Explanation of the mob_item_ratio.yml file and structure. +//============================================================ + +Item base drop rates defined in mob_db.yml will not get multiplied by global item_rate* values (aka drop rates) from +conf/battle/drops.conf and instead, Ratio will be used (100 = 1x). +If no Mob is specified, all monsters will be affected, otherwise only the ones listed. + +Examples: +Jellopies from monsters will drop with 1x drop rate regardless of global drop rate +Body: + - Item: Jellopy + Ratio: 100 + +Jellopies from monsters will drop with 10x drop rate regardless of global drop rate +Body: + - Item: Jellopy + Ratio: 1000 + +Removes Jellopies from monsters drop +Body: + - Item: Jellopy + Ratio: 0 + +Jellopies from Porings will drop with 1x drop rate. Other monsters that drop Jellopies are unaffected (use global drop rate). +Body: + - Item: Jellopy + Ratio: 100 + List: + PORING: true + +Notes: +- Does not override item_drop_*_min/max settings. +- Does not affect card/item-granted drops. To adjust card/item-granted drops, edit them in item_db. +- Does affect MVP prizes and Treasure Boxes. +- You can add only ONE Ratio per Item. If you need various ratios for different monsters, override drop rate with Ratio=100 and edit base drop rates in mob_db. +- This file is reloaded by @reloadmobdb. diff --git a/doc/yaml/db/mob_item_ratio.yml b/doc/yaml/db/mob_item_ratio.yml new file mode 100644 index 0000000000..f50d348aac --- /dev/null +++ b/doc/yaml/db/mob_item_ratio.yml @@ -0,0 +1,29 @@ +# Specific Item Drop Ratio Database +# This file is a part of rAthena. +# Copyright(C) 2021 rAthena Development Team +# https://rathena.org - https://github.com/rathena +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +########################################################################### +# Mob Item Ratio Database +########################################################################### +# +# Mob Item Ratio Settings +# +########################################################################### +# - Item AegisName of item to adjust. +# Ratio Drop rate. +# List: List of monster(s) affected (format : true/false). (Defaulted to all monsters) +########################################################################### diff --git a/src/map/map-server.vcxproj b/src/map/map-server.vcxproj index fb2fcf87d9..3db8db282b 100644 --- a/src/map/map-server.vcxproj +++ b/src/map/map-server.vcxproj @@ -334,7 +334,7 @@ - + diff --git a/src/map/mob.cpp b/src/map/mob.cpp index ff5ac9e6eb..48798c0c86 100644 --- a/src/map/mob.cpp +++ b/src/map/mob.cpp @@ -73,14 +73,7 @@ const t_tick MOB_MAX_DELAY = 24 * 3600 * 1000; // holds Monster Spawn informations std::unordered_map> mob_spawn_data; -//Dynamic item drop ratio database for per-item drop ratio modifiers overriding global drop ratios. -#define MAX_ITEMRATIO_MOBS 10 -struct s_mob_item_drop_ratio { - t_itemid nameid; - int drop_ratio; - unsigned short mob_id[MAX_ITEMRATIO_MOBS]; -}; -static DBMap *mob_item_drop_ratio; +MobItemRatioDatabase mob_item_drop_ratio; /// Mob skill struct for temporary storage struct s_mob_skill_db { @@ -4158,22 +4151,17 @@ static unsigned int mob_drop_adjust(int baserate, int rate_adjust, unsigned shor /** * Check if global item drop rate is overriden for given item - * in db/mob_item_ratio.txt + * in db/mob_item_ratio.yml * @param nameid ID of the item * @param mob_id ID of the monster * @param rate_adjust pointer to store ratio if found */ static void item_dropratio_adjust(t_itemid nameid, int mob_id, int *rate_adjust) { - struct s_mob_item_drop_ratio *item_ratio = (struct s_mob_item_drop_ratio *)uidb_get(mob_item_drop_ratio, nameid); + std::shared_ptr item_ratio = mob_item_drop_ratio.find(nameid); if( item_ratio) { - if( item_ratio->mob_id[0] ) { // only for listed mobs - int i; - ARR_FIND(0, MAX_ITEMRATIO_MOBS, i, item_ratio->mob_id[i] == mob_id); - if( i < MAX_ITEMRATIO_MOBS ) // found - *rate_adjust = item_ratio->drop_ratio; - } - else // for all mobs + // If it is empty it is applied to all monsters, if not it is only applied if the monster is in the vector + if( item_ratio->mob_ids.empty() || util::vector_exists( item_ratio->mob_ids, static_cast( mob_id ) ) ) *rate_adjust = item_ratio->drop_ratio; } } @@ -5931,51 +5919,83 @@ static int mob_read_sqlskilldb(void) return 0; } -/** - * Read mob_item_ratio.txt - */ -static bool mob_readdb_itemratio(char* str[], int columns, int current) -{ - t_itemid nameid; - int ratio, i; - struct s_mob_item_drop_ratio *item_ratio; - nameid = strtoul(str[0], nullptr, 10); - - if (itemdb_exists(nameid) == NULL) { - ShowWarning("mob_readdb_itemratio: Invalid item id %u.\n", nameid); - return false; - } - - ratio = atoi(str[1]); - - if (!(item_ratio = (struct s_mob_item_drop_ratio *)uidb_get(mob_item_drop_ratio,nameid))) - CREATE(item_ratio, struct s_mob_item_drop_ratio, 1); - - item_ratio->drop_ratio = ratio; - memset(item_ratio->mob_id, 0, sizeof(item_ratio->mob_id)); - for (i = 0; i < columns-2; i++) { - uint16 mob_id = atoi(str[i+2]); - if (mob_db.find(mob_id) == nullptr) - ShowError("mob_readdb_itemratio: Invalid monster with ID %hu (Item:%u Col:%d).\n", mob_id, nameid, columns); - else - item_ratio->mob_id[i] = atoi(str[i+2]); - } - - if (!item_ratio->nameid) { - item_ratio->nameid = nameid; - uidb_put(mob_item_drop_ratio, nameid, item_ratio); - } - - return true; +const std::string MobItemRatioDatabase::getDefaultLocation() { + return std::string(db_path) + "/mob_item_ratio.yml"; } /** - * Free drop ratio data - **/ -static int mob_item_drop_ratio_free(DBKey key, DBData *data, va_list ap) { - struct s_mob_item_drop_ratio *item_ratio = (struct s_mob_item_drop_ratio *)db_data2ptr(data); - aFree(item_ratio); - return 0; + * Reads and parses an entry from the mob_item_ratio. + * @param node: YAML node containing the entry. + * @return count of successfully parsed rows + */ +uint64 MobItemRatioDatabase::parseBodyNode(const YAML::Node &node) { + std::string item_name; + + if (!this->asString(node, "Item", item_name)) + return 0; + + item_data *item = itemdb_search_aegisname(item_name.c_str()); + + if (item == nullptr) { + this->invalidWarning(node["Item"], "Item %s does not exist, skipping.\n", item_name.c_str()); + return 0; + } + + t_itemid nameid = item->nameid; + + std::shared_ptr data = this->find(nameid); + bool exists = data != nullptr; + + if (!exists) { + if (!this->nodesExist(node, { "Ratio" })) { + return 0; + } + + data = std::make_shared(); + data->nameid = nameid; + } + + if (this->nodeExists(node, "Ratio")) { + uint16 ratio; + + if (!this->asUInt16(node, "Ratio", ratio)) + return 0; + + data->drop_ratio = ratio; + } + + if (this->nodeExists(node, "List")) { + const YAML::Node &MobNode = node["List"]; + + for (const auto mobit : MobNode) { + std::string mob_name = mobit.first.as(); + + std::shared_ptr mob = mobdb_search_aegisname(mob_name.c_str()); + + if (mob == nullptr) { + this->invalidWarning(node["List"], "Unknown mob %s, skipping.\n", mob_name.c_str()); + continue; + } + uint16 mob_id = mob->id; + + bool active; + + if (!this->asBool(node["List"], mob_name, active)) + return 0; + + if (!active) { + util::vector_erase_if_exists(data->mob_ids, mob_id); + continue; + } + + data->mob_ids.push_back(mob_id); + } + } + + if (!exists) + this->put(nameid, data); + + return 1; } /** @@ -6005,6 +6025,13 @@ static void mob_drop_ratio_adjust(void){ // Adjust the rate if there is an entry in mob_item_ratio item_dropratio_adjust( nameid, mob_id, &rate_adjust ); + // remove the item if the rate of item_dropratio_adjust is 0 + if (rate_adjust == 0) { + mob->mvpitem[j].nameid = 0; + mob->mvpitem[j].rate = 0; + continue; + } + // Adjust rate with given algorithms rate = mob_drop_adjust( rate, rate_adjust, battle_config.item_drop_mvp_min, battle_config.item_drop_mvp_max ); @@ -6101,6 +6128,14 @@ static void mob_drop_ratio_adjust(void){ } item_dropratio_adjust( nameid, mob_id, &rate_adjust ); + + // remove the item if the rate of item_dropratio_adjust is 0 + if (rate_adjust == 0) { + mob->dropitem[j].nameid = 0; + mob->dropitem[j].rate = 0; + continue; + } + rate = mob_drop_adjust( rate, rate_adjust, ratemin, ratemax ); // calculate and store Max available drop chance of the item @@ -6133,7 +6168,7 @@ static void mob_drop_ratio_adjust(void){ } // Now that we are done we can delete the stored item ratios - mob_item_drop_ratio->clear( mob_item_drop_ratio, mob_item_drop_ratio_free ); + mob_item_drop_ratio.clear(); } /** @@ -6239,12 +6274,12 @@ static void mob_load(void) else mob_readskilldb(dbsubpath2, silent); - sv_readdb(dbsubpath1, "mob_item_ratio.txt", ',', 2, 2+MAX_ITEMRATIO_MOBS, -1, &mob_readdb_itemratio, silent); aFree(dbsubpath1); aFree(dbsubpath2); } + mob_item_drop_ratio.load(); mob_avail_db.load(); mob_summon_db.load(); @@ -6262,7 +6297,6 @@ void mob_db_load(bool is_reload){ item_drop_ers = ers_new(sizeof(struct item_drop),"mob.cpp::item_drop_ers",ERS_OPT_CLEAN); item_drop_list_ers = ers_new(sizeof(struct item_drop_list),"mob.cpp::item_drop_list_ers",ERS_OPT_NONE); } - mob_item_drop_ratio = uidb_alloc(DB_OPT_BASE); mob_load(); } @@ -6411,7 +6445,7 @@ void do_final_mob(bool is_reload){ mob_chat_db.clear(); mob_skill_db.clear(); - mob_item_drop_ratio->destroy(mob_item_drop_ratio,mob_item_drop_ratio_free); + mob_item_drop_ratio.clear(); mob_summon_db.clear(); if( !is_reload ) { ers_destroy(item_drop_ers); diff --git a/src/map/mob.hpp b/src/map/mob.hpp index b81e6e3bcd..aac3990ef9 100644 --- a/src/map/mob.hpp +++ b/src/map/mob.hpp @@ -206,6 +206,22 @@ public: uint64 parseBodyNode(const YAML::Node &node); }; +struct s_mob_item_drop_ratio { + t_itemid nameid; + uint16 drop_ratio; + std::vector mob_ids; +}; + +class MobItemRatioDatabase : public TypesafeYamlDatabase { +public: + MobItemRatioDatabase() : TypesafeYamlDatabase("MOB_ITEM_RATIO_DB", 1) { + + } + + const std::string getDefaultLocation(); + uint64 parseBodyNode(const YAML::Node &node); +}; + struct spawn_info { unsigned short mapindex; unsigned short qty; diff --git a/src/tool/csv2yaml.cpp b/src/tool/csv2yaml.cpp index ba84627126..2162ca3f11 100644 --- a/src/tool/csv2yaml.cpp +++ b/src/tool/csv2yaml.cpp @@ -408,6 +408,12 @@ int do_init( int argc, char** argv ){ return 0; } + if (!process("MOB_ITEM_RATIO_DB", 1, root_paths, "mob_item_ratio", [](const std::string& path, const std::string& name_ext) -> bool { + return sv_readdb(path.c_str(), name_ext.c_str(), ',', 2, 2+MAX_ITEMRATIO_MOBS, -1, &mob_readdb_itemratio, false); + })) { + return 0; + } + // TODO: add implementations ;-) return 0; @@ -3941,3 +3947,39 @@ static bool itemdb_read_group_yaml(void) { } return true; } + +// Copied and adjusted from mob.cpp +static bool mob_readdb_itemratio(char* str[], int columns, int current) { + t_itemid nameid = strtoul(str[0], nullptr, 10); + + std::string *item_name = util::umap_find(aegis_itemnames, nameid); + + if (!item_name) { + ShowWarning("mob_readdb_itemratio: Invalid item id %u.\n", nameid); + return false; + } + + body << YAML::BeginMap; + body << YAML::Key << "Item" << YAML::Value << *item_name; + body << YAML::Key << "Ratio" << YAML::Value << strtoul(str[1], nullptr, 10); + + if (columns-2 > 0) { + body << YAML::Key << "List"; + body << YAML::BeginMap; + for (int i = 0; i < columns-2; i++) { + uint16 mob_id = static_cast(strtoul(str[i+2], nullptr, 10)); + std::string* mob_name = util::umap_find( aegis_mobnames, mob_id ); + + if (mob_name == nullptr) { + ShowWarning( "mob_readdb_itemratio: Invalid monster with ID %hu.\n", mob_id ); + continue; + } + body << YAML::Key << *mob_name << YAML::Value << "true"; + } + body << YAML::EndMap; + } + + body << YAML::EndMap; + + return true; +} diff --git a/src/tool/csv2yaml.hpp b/src/tool/csv2yaml.hpp index 7bcbb7616b..e4056a1eaa 100644 --- a/src/tool/csv2yaml.hpp +++ b/src/tool/csv2yaml.hpp @@ -15,6 +15,7 @@ #define MAX_MAP_PER_INSTANCE 255 #define MAX_ARROW_RESULT 5 /// Max Arrow results/created #define MAX_SKILL_ARROW_DB 150 /// Max Arrow Creation DB +#define MAX_ITEMRATIO_MOBS 10 // Database to memory maps struct s_skill_unit_csv : s_skill_db { @@ -446,5 +447,6 @@ static bool guild_read_castledb(char* str[], int columns, int current); static bool exp_guild_parse_row(char* split[], int column, int current); static bool itemdb_read_group(char* fields[], int columns, int current); static bool itemdb_read_group_yaml(void); +static bool mob_readdb_itemratio(char* fields[], int columns, int current); #endif /* CSV2YAML_HPP */