Procházet zdrojové kódy

Changed mob drop array to STL (#8923)

Lemongrass3110 před 2 měsíci
rodič
revize
e3bcf2436c
7 změnil soubory, kde provedl 302 přidání a 248 odebrání
  1. 2 2
      conf/msg_conf/map_msg.conf
  2. 8 1
      src/common/random.hpp
  3. 54 45
      src/map/atcommand.cpp
  4. 135 125
      src/map/mob.cpp
  5. 28 23
      src/map/mob.hpp
  6. 31 14
      src/map/pc.cpp
  7. 44 38
      src/map/script.cpp

+ 2 - 2
conf/msg_conf/map_msg.conf

@@ -1400,8 +1400,8 @@
 1245:  Drops:
 1246: This monster has no drops.
 1247:  MVP Bonus EXP:%llu
-1248:  MVP Items:
-1249: This monster has no MVP prizes.
+1248:  MVP drops:
+1249: This monster has no MVP drops.
 
 // @showmobs
 1250: Invalid mob id %s!

+ 8 - 1
src/common/random.hpp

@@ -4,8 +4,10 @@
 #ifndef RANDOM_HPP
 #define RANDOM_HPP
 
-#include <type_traits>
+#include <algorithm>
 #include <random>
+#include <type_traits>
+#include <vector>
 
 #include "cbasetypes.hpp"
 
@@ -48,4 +50,9 @@ typename std::enable_if<std::is_integral<T>::value, bool>::type rnd_chance_offic
 	return rnd_value<T>(0, 20000)%base < chance;
 }
 
+template <typename T>
+void rnd_vector_order( std::vector<T>& vec ){
+	std::shuffle( std::begin( vec ), std::end( vec ), generator );
+}
+
 #endif /* RANDOM_HPP */

+ 54 - 45
src/map/atcommand.cpp

@@ -8052,74 +8052,83 @@ ACMD_FUNC(mobinfo)
 		// drops
 		clif_displaymessage(fd, msg_txt(sd,1245)); //  Drops:
 		strcpy(atcmd_output, " ");
-		uint32 j = 0;
-		int32 drop_modifier = 100;
+
+		if( mob->dropitem.empty() ){
+			clif_displaymessage(fd, msg_txt(sd,1246)); // This monster has no drops.
+		}else{
+			uint32 j = 0;
+			int32 drop_modifier = 100;
 #ifdef RENEWAL_DROP
-		if( battle_config.atcommand_mobinfo_type ){
-			drop_modifier = pc_level_penalty_mod( sd, PENALTY_DROP, mob );
-		}
+			if( battle_config.atcommand_mobinfo_type ){
+				drop_modifier = pc_level_penalty_mod( sd, PENALTY_DROP, mob );
+			}
 #endif
 
-		for (i = 0; i < MAX_MOB_DROP_TOTAL; i++) {
+			for( const std::shared_ptr<s_mob_drop>& entry : mob->dropitem ){
+				if (entry->nameid == 0 || entry->rate < 1)
+					continue;
 
-			if (mob->dropitem[i].nameid == 0 || mob->dropitem[i].rate < 1)
-				continue;
+				std::shared_ptr<item_data> id = item_db.find(entry->nameid);
 
-			std::shared_ptr<item_data> id = item_db.find(mob->dropitem[i].nameid);
+				if (id == nullptr)
+					continue;
 
-			if (id == nullptr)
-				continue;
+				int32 droprate = mob_getdroprate( &sd->bl, mob, entry->rate, drop_modifier );
 
-			int32 droprate = mob_getdroprate( &sd->bl, mob, mob->dropitem[i].rate, drop_modifier );
+				sprintf(atcmd_output2, " - %s  %02.02f%%", item_db.create_item_link( id ).c_str(), (float)droprate / 100);
+				strcat(atcmd_output, atcmd_output2);
+				if (++j % 3 == 0) {
+					clif_displaymessage(fd, atcmd_output);
+					strcpy(atcmd_output, " ");
+				}
+			}
 
-			sprintf(atcmd_output2, " - %s  %02.02f%%", item_db.create_item_link( id ).c_str(), (float)droprate / 100);
-			strcat(atcmd_output, atcmd_output2);
-			if (++j % 3 == 0) {
+			if( j % 3 != 0 ){
 				clif_displaymessage(fd, atcmd_output);
-				strcpy(atcmd_output, " ");
 			}
 		}
-		if (j == 0)
-			clif_displaymessage(fd, msg_txt(sd,1246)); // This monster has no drops.
-		else if (j % 3 != 0)
-			clif_displaymessage(fd, atcmd_output);
+
 		// mvp
 		if( mob->get_bosstype() == BOSSTYPE_MVP ){
-			float mvppercent, mvpremain;
 			sprintf(atcmd_output, msg_txt(sd,1247), mob->mexp); //  MVP Bonus EXP:%llu
 			clif_displaymessage(fd, atcmd_output);
-			strcpy(atcmd_output, msg_txt(sd,1248)); //  MVP Items:
-			mvpremain = 100.0; //Remaining drop chance for official mvp drop mode
-			j = 0;
-			for (i = 0; i < MAX_MVP_DROP_TOTAL; i++) {
+			clif_displaymessage(fd, msg_txt(sd,1248)); //  MVP drops:
+			strcpy(atcmd_output, " ");
 
-				if (mob->mvpitem[i].nameid == 0)
-					continue;
+			if( mob->mvpitem.empty() ){
+				clif_displaymessage(fd, msg_txt(sd,1249)); // This monster has no MVP drops.
+			}else{
+				float mvpremain = 100.0; //Remaining drop chance for official mvp drop mode
+				uint32 j = 0;
 
-				std::shared_ptr<item_data> id = item_db.find(mob->mvpitem[i].nameid);
+				for( const std::shared_ptr<s_mob_drop>& entry : mob->mvpitem ){
+					if (entry->nameid == 0)
+						continue;
 
-				if (id == nullptr)
-					continue;
+					std::shared_ptr<item_data> id = item_db.find(entry->nameid);
 
-				//Because if there are 3 MVP drops at 50%, the first has a chance of 50%, the second 25% and the third 12.5%
-				mvppercent = (float)mob->mvpitem[i].rate * mvpremain / 10000.0f;
-				if(battle_config.item_drop_mvp_mode == 0) {
-					mvpremain -= mvppercent;
-				}
-				if (mvppercent > 0) {
-					j++;
-					if (j == 1) {
-						sprintf(atcmd_output2, " %s  %02.02f%%", item_db.create_item_link( id ).c_str(), mvppercent);
-					} else {
+					if (id == nullptr)
+						continue;
+
+					//Because if there are 3 MVP drops at 50%, the first has a chance of 50%, the second 25% and the third 12.5%
+					float mvppercent = (float)entry->rate * mvpremain / 10000.0f;
+					if(battle_config.item_drop_mvp_mode == 0) {
+						mvpremain -= mvppercent;
+					}
+					if (mvppercent > 0) {
 						sprintf(atcmd_output2, " - %s  %02.02f%%", item_db.create_item_link( id ).c_str(), mvppercent);
+						strcat(atcmd_output, atcmd_output2);
+						if (++j % 3 == 0) {
+							clif_displaymessage(fd, atcmd_output);
+							strcpy(atcmd_output, " ");
+						}
 					}
-					strcat(atcmd_output, atcmd_output2);
+				}
+
+				if( j % 3 != 0 ){
+					clif_displaymessage(fd, atcmd_output);
 				}
 			}
-			if (j == 0)
-				clif_displaymessage(fd, msg_txt(sd,1249)); // This monster has no MVP prizes.
-			else
-				clif_displaymessage(fd, atcmd_output);
 		}
 	}
 	return 0;

+ 135 - 125
src/map/mob.cpp

@@ -2378,8 +2378,8 @@ static TIMER_FUNC(mob_ai_hard){
  * @param mobdrop: Drop data
  * @author [Cydh]
  **/
-void mob_setdropitem_option( item& item, s_mob_drop& mobdrop ){
-	std::shared_ptr<s_random_opt_group> group = random_option_group.find( mobdrop.randomopt_group );
+void mob_setdropitem_option( item& item, const std::shared_ptr<s_mob_drop>& mobdrop ){
+	std::shared_ptr<s_random_opt_group> group = random_option_group.find( mobdrop->randomopt_group );
 
 	if (group != nullptr) {
 		group->apply( item );
@@ -2389,13 +2389,13 @@ void mob_setdropitem_option( item& item, s_mob_drop& mobdrop ){
 /*==========================================
  * Initializes the delay drop structure for mob-dropped items.
  *------------------------------------------*/
-static std::shared_ptr<s_item_drop> mob_setdropitem( s_mob_drop& mobdrop, int32 qty, uint16 mob_id ){
+static std::shared_ptr<s_item_drop> mob_setdropitem( const std::shared_ptr<s_mob_drop>& mobdrop, int32 qty, uint16 mob_id ){
 	std::shared_ptr<s_item_drop> drop = std::make_shared<s_item_drop>();
 
 	drop->item_data = { 0 };
-	drop->item_data.nameid = mobdrop.nameid;
+	drop->item_data.nameid = mobdrop->nameid;
 	drop->item_data.amount = qty;
-	drop->item_data.identify = itemdb_isidentified( mobdrop.nameid );
+	drop->item_data.identify = itemdb_isidentified( mobdrop->nameid );
 	mob_setdropitem_option( drop->item_data, mobdrop );
 	drop->mob_id = mob_id;
 
@@ -3133,21 +3133,22 @@ int32 mob_dead(struct mob_data *md, struct block_list *src, int32 type)
 					if (rnd()%10000 >= drop_rate)
 						continue;
 
-					s_mob_drop mobdrop = {};
+					std::shared_ptr<s_mob_drop> mobdrop = std::make_shared<s_mob_drop>();
+
 					if (it.nameid > 0) {
-						mobdrop.nameid = it.nameid;
-						mobdrop.rate = drop_rate;
+						mobdrop->nameid = it.nameid;
+						mobdrop->rate = drop_rate;
 					}
 					else {
 						std::shared_ptr<s_item_group_entry> entry = itemdb_group.get_random_entry(it.group, 1, GROUP_ALGORITHM_DROP);
 						if (entry == nullptr) continue;
-						mobdrop.nameid = entry->nameid;
-						mobdrop.rate = entry->adj_rate * drop_rate / 10000;
+						mobdrop->nameid = entry->nameid;
+						mobdrop->rate = entry->adj_rate * drop_rate / 10000;
 					}
 
 					std::shared_ptr<s_item_drop> ditem = mob_setdropitem(mobdrop, 1, md->mob_id);
 
-					mob_item_drop(md, dlist, ditem, 0, mobdrop.rate, homkillonly || merckillonly);
+					mob_item_drop(md, dlist, ditem, 0, mobdrop->rate, homkillonly || merckillonly);
 				}
 			}
 
@@ -3160,30 +3161,30 @@ int32 mob_dead(struct mob_data *md, struct block_list *src, int32 type)
 		}
 
 		// Regular mob drops drop after script-granted drops
-		for (i = 0; i < MAX_MOB_DROP_TOTAL; i++) {
-			if (md->db->dropitem[i].nameid == 0)
+		for( const std::shared_ptr<s_mob_drop>& entry : md->db->dropitem ){
+			if (entry->nameid == 0)
 				continue;
 
-			std::shared_ptr<item_data> it = item_db.find(md->db->dropitem[i].nameid);
+			std::shared_ptr<item_data> it = item_db.find(entry->nameid);
 
 			if (it == nullptr)
 				continue;
 
-			drop_rate = mob_getdroprate(src, md->db, md->db->dropitem[i].rate, drop_modifier, md);
+			drop_rate = mob_getdroprate(src, md->db, entry->rate, drop_modifier, md);
 
 			// attempt to drop the item
 			if (rnd() % 10000 >= drop_rate)
 				continue;
 
 			if (mvp_sd && it->type == IT_PETEGG) {
-				pet_create_egg(mvp_sd, md->db->dropitem[i].nameid);
+				pet_create_egg(mvp_sd, entry->nameid);
 				continue;
 			}
 
-			std::shared_ptr<s_item_drop> ditem = mob_setdropitem(md->db->dropitem[i], 1, md->mob_id);
+			std::shared_ptr<s_item_drop> ditem = mob_setdropitem(entry, 1, md->mob_id);
 
 			//A Rare Drop Global Announce by Lupus
-			if (mvp_sd && md->db->dropitem[i].rate <= battle_config.rare_drop_announce) {
+			if (mvp_sd && entry->rate <= battle_config.rare_drop_announce) {
 				char message[128];
 				sprintf(message, msg_txt(nullptr, 541), mvp_sd->status.name, md->name, it->ename.c_str(), (float)drop_rate / 100);
 				//MSG: "'%s' won %s's %s (chance: %0.02f%%)"
@@ -3191,20 +3192,21 @@ int32 mob_dead(struct mob_data *md, struct block_list *src, int32 type)
 			}
 			// Announce first, or else ditem will be freed. [Lance]
 			// By popular demand, use base drop rate for autoloot code. [Skotlex]
-			mob_item_drop(md, dlist, ditem, 0, battle_config.autoloot_adjust ? drop_rate : md->db->dropitem[i].rate, homkillonly || merckillonly);
+			mob_item_drop(md, dlist, ditem, 0, battle_config.autoloot_adjust ? drop_rate : entry->rate, homkillonly || merckillonly);
 		}
 
 		// Ore Discovery (triggers if owner has loot priority, does not require to be the killer)
 		if (mvp_sd && pc_checkskill(mvp_sd, BS_FINDINGORE) > 0) {
 			std::shared_ptr<s_item_group_entry> entry = itemdb_group.get_random_entry(IG_ORE, 1, GROUP_ALGORITHM_DROP);
 			if (entry != nullptr) {
-				s_mob_drop mobdrop = {};
-				mobdrop.nameid = entry->nameid;
-				mobdrop.rate = entry->adj_rate;
+				std::shared_ptr<s_mob_drop> mobdrop = std::make_shared<s_mob_drop>();
+
+				mobdrop->nameid = entry->nameid;
+				mobdrop->rate = entry->adj_rate;
 
 				std::shared_ptr<s_item_drop> ditem = mob_setdropitem(mobdrop, 1, md->mob_id);
 
-				mob_item_drop(md, dlist, ditem, 0, mobdrop.rate, homkillonly || merckillonly);
+				mob_item_drop(md, dlist, ditem, 0, mobdrop->rate, homkillonly || merckillonly);
 			}
 		}
 
@@ -3224,7 +3226,7 @@ int32 mob_dead(struct mob_data *md, struct block_list *src, int32 type)
 				if( rnd_chance( it.second->rate, 100000u ) ){
 					// 'Cheat' for autoloot command: rate is changed from n/100000 to n/10000
 					int32 map_drops_rate = max(1, (it.second->rate / 10));
-					std::shared_ptr<s_item_drop> ditem = mob_setdropitem(*it.second, 1, md->mob_id);
+					std::shared_ptr<s_item_drop> ditem = mob_setdropitem( it.second, 1, md->mob_id );
 					mob_item_drop( md, dlist, ditem, 0, map_drops_rate, homkillonly || merckillonly );
 				}
 			}
@@ -3237,7 +3239,7 @@ int32 mob_dead(struct mob_data *md, struct block_list *src, int32 type)
 					if( rnd_chance( it.second->rate, 100000u ) ){
 						// 'Cheat' for autoloot command: rate is changed from n/100000 to n/10000
 						int32 map_drops_rate = max(1, (it.second->rate / 10));
-						std::shared_ptr<s_item_drop> ditem = mob_setdropitem(*it.second, 1, md->mob_id);
+						std::shared_ptr<s_item_drop> ditem = mob_setdropitem( it.second, 1, md->mob_id );
 						mob_item_drop( md, dlist, ditem, 0, map_drops_rate, homkillonly || merckillonly );
 					}
 				}
@@ -3288,45 +3290,29 @@ int32 mob_dead(struct mob_data *md, struct block_list *src, int32 type)
 		}
 
 		if( !(map_getmapflag(m, MF_NOMVPLOOT) || type&1) ) {
-			//Order might be random depending on item_drop_mvp_mode config setting
-			struct s_mob_drop mdrop[MAX_MVP_DROP_TOTAL];
-
-			memset(&mdrop,0,sizeof(mdrop));
+			// Create a copy of the MVP drops vector
+			std::vector<std::shared_ptr<s_mob_drop>> mdrop = md->db->mvpitem;
 
+			// Order might be random depending on item_drop_mvp_mode config setting
 			if(battle_config.item_drop_mvp_mode == 1) {
 				//Random order
-				for(i = 0; i < MAX_MVP_DROP_TOTAL; i++) {
-					while( 1 ) {
-						uint8 va = rnd()%MAX_MVP_DROP_TOTAL;
-						if (mdrop[va].nameid == 0) {
-							if (md->db->mvpitem[i].nameid > 0)
-								memcpy(&mdrop[va],&md->db->mvpitem[i],sizeof(mdrop[va]));
-							break;
-						}
-					}
-				}
-			} else {
-				//Normal order
-				for(i = 0; i < MAX_MVP_DROP_TOTAL; i++) {
-					if (md->db->mvpitem[i].nameid > 0)
-						memcpy(&mdrop[i],&md->db->mvpitem[i],sizeof(mdrop[i]));
-				}
+				rnd_vector_order( mdrop );
 			}
 
 #if defined(RENEWAL_DROP)
 			int32 penalty = pc_level_penalty_mod( mvp_sd, PENALTY_MVP_DROP, nullptr, md );
 #endif
 
-			for(i = 0; i < MAX_MVP_DROP_TOTAL; i++) {
-				if(mdrop[i].nameid == 0)
+			for( const std::shared_ptr<s_mob_drop>& entry : mdrop ){
+				if(entry->nameid == 0)
 					continue;
 
-				std::shared_ptr<item_data> i_data = item_db.find(mdrop[i].nameid);
+				std::shared_ptr<item_data> i_data = item_db.find(entry->nameid);
 
 				if (i_data == nullptr)
 					continue;
 
-				temp = mdrop[i].rate;
+				temp = entry->rate;
 
 #if defined(RENEWAL_DROP)
 				temp = cap_value( apply_rate( temp, penalty ), 0, 10000 );
@@ -3340,7 +3326,7 @@ int32 mob_dead(struct mob_data *md, struct block_list *src, int32 type)
 				}
 
 				struct item item = {};
-				item.nameid=mdrop[i].nameid;
+				item.nameid=entry->nameid;
 				item.identify= itemdb_isidentified(item.nameid);
 				clif_mvp_item(mvp_sd,item.nameid);
 				log_mvp_nameid = item.nameid;
@@ -3353,7 +3339,7 @@ int32 mob_dead(struct mob_data *md, struct block_list *src, int32 type)
 					intif_broadcast(message,strlen(message)+1,BC_DEFAULT);
 				}
 
-				mob_setdropitem_option( item, mdrop[i] );
+				mob_setdropitem_option( item, entry );
 
 				if((temp = pc_additem(mvp_sd,&item,1,LOG_TYPE_PICKDROP_PLAYER)) != 0) {
 					clif_additem(mvp_sd,0,0,temp);
@@ -4664,21 +4650,14 @@ const std::string MobDatabase::getDefaultLocation() {
 	return std::string(db_path) + "/mob_db.yml";
 }
 
-bool MobDatabase::parseDropNode(std::string nodeName, const ryml::NodeRef& node, uint8 max, s_mob_drop *drops) {
-	const auto& dropNode = node[c4::to_csubstr(nodeName)];
-	uint16 i;
-
-	// Find first empty spot
-	for( i = 0; i < max; i++ ){
-		if( drops[i].nameid == 0 ){
-			break;
-		}
-	}
-
-	for (const auto& dropit : dropNode) {
-		uint16 index;
+bool MobDatabase::parseDropNode( std::string nodeName, const ryml::NodeRef& node, uint8 max, std::vector<std::shared_ptr<s_mob_drop>>& drops ){
+	for( const auto& dropit : node[c4::to_csubstr(nodeName)] ){
+		std::shared_ptr<s_mob_drop> drop;
+		bool exist;
 
 		if (this->nodeExists(dropit, "Index")) {
+			uint16 index;
+
 			if (!this->asUInt16(dropit, "Index", index))
 				return false;
 
@@ -4686,13 +4665,27 @@ bool MobDatabase::parseDropNode(std::string nodeName, const ryml::NodeRef& node,
 				this->invalidWarning(dropit["Index"], "Invalid monster %s index %hu. Must be between 0~%hu, skipping.\n", nodeName.c_str(), index, max - 1);
 				continue;
 			}
-		} else {
-			index = i++;
 
-			if (index >= max) {
+			if( index == drops.size() ){
+				// Trying to add the next entry (just manually assigned the index)
+				drop = std::make_shared<s_mob_drop>();
+				exist = false;
+			}else if( index > drops.size() ){
+				// TODO: warning
+				continue;
+			}else{
+				// Overwrite existing entry
+				drop = drops[index];
+				exist = true;
+			}
+		} else {
+			if (drops.size() >= max) {
 				this->invalidWarning(dropit, "Maximum of %d monster %s met, skipping.\n", max, nodeName.c_str());
 				continue;
 			}
+
+			drop = std::make_shared<s_mob_drop>();
+			exist = false;
 		}
 
 		std::string item_name;
@@ -4731,10 +4724,14 @@ bool MobDatabase::parseDropNode(std::string nodeName, const ryml::NodeRef& node,
 				this->invalidWarning(dropit["RandomOptionGroup"], "Unknown random option group %s for monster %s, defaulting to no group.\n", group_name.c_str(), nodeName.c_str());
 		}
 
-		drops[index].nameid = item->nameid;
-		drops[index].rate = rate;
-		drops[index].steal_protected = steal;
-		drops[index].randomopt_group = group;
+		drop->nameid = item->nameid;
+		drop->rate = rate;
+		drop->steal_protected = steal;
+		drop->randomopt_group = group;
+
+		if( !exist ){
+			drops.push_back( drop );
+		}
 	}
 
 	return true;
@@ -4745,22 +4742,38 @@ bool MobDatabase::parseDropNode(std::string nodeName, const ryml::NodeRef& node,
  */
 s_mob_db::s_mob_db()
 {
-	status.max_hp = 1;
-	status.max_sp = 1;
-	status.str = 1;
-	status.agi = 1;
-	status.vit = 1;
-	status.int_ = 1;
-	status.dex = 1;
-	status.luk = 1;
-	status.ele_lv = 1;
-	status.speed = DEFAULT_WALK_SPEED;
-	status.adelay = MAX_ASPD_NOPC;
-	status.amotion = MAX_ASPD_NOPC/AMOTION_DIVIDER_NOPC;
-	status.clientamotion = cap_value(status.amotion, 1, USHRT_MAX);
-	status.mode = static_cast<e_mode>(MONSTER_TYPE_06);
-
-	vd.class_ = id;
+	this->id = {};
+	this->sprite = {};
+	this->name = {};
+	this->jname = {};
+	this->base_exp = {};
+	this->job_exp = {};
+	this->mexp = {};
+	this->range2 = {};
+	this->range3 = {};
+	this->race2 = {};
+	this->lv = 1;
+	this->dropitem = {};
+	this->mvpitem = {};
+	this->status = {};
+	this->status.max_hp = 1;
+	this->status.max_sp = 1;
+	this->status.str = 1;
+	this->status.agi = 1;
+	this->status.vit = 1;
+	this->status.int_ = 1;
+	this->status.dex = 1;
+	this->status.luk = 1;
+	this->status.ele_lv = 1;
+	this->status.speed = DEFAULT_WALK_SPEED;
+	this->status.adelay = MAX_ASPD_NOPC;
+	this->status.amotion = MAX_ASPD_NOPC/AMOTION_DIVIDER_NOPC;
+	this->status.clientamotion = cap_value(status.amotion, 1, USHRT_MAX);
+	this->status.mode = static_cast<e_mode>(MONSTER_TYPE_06);
+	this->vd = {};
+	this->option = {};
+	this->skill = {};
+	this->damagetaken = 100;
 }
 
 /**
@@ -6538,46 +6551,42 @@ uint64 MobItemRatioDatabase::parseBodyNode(const ryml::NodeRef& node) {
 static void mob_drop_ratio_adjust(void){
 	for( auto &pair : mob_db ){
 		std::shared_ptr<s_mob_db> mob = pair.second;
-		struct item_data *id;
-		t_itemid nameid;
-		int32 j, rate, rate_adjust = 0, mob_id = pair.first;
+		int32 mob_id = pair.first;
 
 		if( mob_is_clone( mob_id ) ){
 			continue;
 		}
 
-		for( j = 0; j < MAX_MVP_DROP_TOTAL; j++ ){
-			nameid = mob->mvpitem[j].nameid;
-			rate = mob->mvpitem[j].rate;
+		for( auto it = mob->mvpitem.begin(); it != mob->mvpitem.end(); ){
+			std::shared_ptr<s_mob_drop>& entry = *it;
 
-			if( nameid == 0 || rate == 0 ){
+			if( entry->nameid == 0 || entry->rate == 0 ){
+				it = mob->mvpitem.erase( it );
 				continue;
 			}
 
-			rate_adjust = battle_config.item_rate_mvp;
+			int32 rate_adjust = battle_config.item_rate_mvp;
 
 			// Adjust the rate if there is an entry in mob_item_ratio
-			item_dropratio_adjust( nameid, mob_id, &rate_adjust );
+			item_dropratio_adjust( entry->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;
+				it = mob->mvpitem.erase( it );
 				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 );
+			int32 rate = mob_drop_adjust( entry->rate, rate_adjust, battle_config.item_drop_mvp_min, battle_config.item_drop_mvp_max );
 
 			// calculate and store Max available drop chance of the MVP item
 			if( rate ){
-				id = itemdb_search( nameid );
+				item_data* id = itemdb_search( entry->nameid );
 
 				// Item is not known anymore(should never happen)
 				if( !id ){
-					ShowWarning( "Monster \"%s\"(id:%u) is dropping an unknown item(id: %u)\n", mob->name.c_str(), mob_id, nameid );
-					mob->mvpitem[j].nameid = 0;
-					mob->mvpitem[j].rate = 0;
+					ShowWarning( "Monster \"%s\"(id:%u) is dropping an unknown item(id: %u)\n", mob->name.c_str(), mob_id, entry->nameid );
+					it = mob->mvpitem.erase( it );
 					continue;
 				}
 
@@ -6587,30 +6596,32 @@ static void mob_drop_ratio_adjust(void){
 				}
 			}
 
-			mob->mvpitem[j].rate = rate;
+			entry->rate = rate;
+			it++;
 		}
 
-		for( j = 0; j < MAX_MOB_DROP_TOTAL; j++ ){
+		for( auto it = mob->dropitem.begin(); it != mob->dropitem.end(); ){
+			std::shared_ptr<s_mob_drop>& entry = *it;
 			uint16 ratemin, ratemax;
 			bool is_treasurechest;
 
-			nameid = mob->dropitem[j].nameid;
-			rate = mob->dropitem[j].rate;
-
-			if( nameid == 0 || rate == 0 ){
+			if( entry->nameid == 0 || entry->rate == 0 ){
+				it = mob->dropitem.erase( it );
 				continue;
 			}
 
-			id = itemdb_search( nameid );
+			item_data* id = itemdb_search( entry->nameid );
 
 			// Item is not known anymore(should never happen)
 			if( !id ){
-				ShowWarning( "Monster \"%s\"(id:%hu) is dropping an unknown item(id: %u)\n", mob->name.c_str(), mob_id, nameid );
-				mob->dropitem[j].nameid = 0;
-				mob->dropitem[j].rate = 0;
+				ShowWarning( "Monster \"%s\"(id:%hu) is dropping an unknown item(id: %u)\n", mob->name.c_str(), mob_id, entry->nameid );
+				it = mob->dropitem.erase( it );
 				continue;
 			}
 
+			int32 rate = entry->rate;
+			int32 rate_adjust;
+
 			if( battle_config.drop_rateincrease && rate < 5000 ){
 				rate++;
 			}
@@ -6661,12 +6672,11 @@ static void mob_drop_ratio_adjust(void){
 				}
 			}
 
-			item_dropratio_adjust( nameid, mob_id, &rate_adjust );
+			item_dropratio_adjust( entry->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;
+				it = mob->dropitem.erase( it );
 				continue;
 			}
 
@@ -6697,7 +6707,8 @@ static void mob_drop_ratio_adjust(void){
 				}
 			}
 
-			mob->dropitem[j].rate = rate;
+			entry->rate = rate;
+			it++;
 		}
 	}
 
@@ -7000,20 +7011,19 @@ void mob_db_load(bool is_reload){
  */
 void mob_reload_itemmob_data(void) {
 	for( auto const &pair : mob_db ){
-		int32 d, k;
-
 		if( mob_is_clone( pair.first ) ){
 			continue;
 		}
 
-		for(d = 0; d < MAX_MOB_DROP_TOTAL; d++) {
-			struct item_data *id;
-			if( !pair.second->dropitem[d].nameid )
+		for( const std::shared_ptr<s_mob_drop>& entry : pair.second->dropitem ){
+			if( entry->nameid )
 				continue;
-			id = itemdb_search(pair.second->dropitem[d].nameid);
+
+			item_data* id = itemdb_search(entry->nameid);
+			int32 k;
 
 			for (k = 0; k < MAX_SEARCH; k++) {
-				if (id->mob[k].chance <= pair.second->dropitem[d].rate)
+				if (id->mob[k].chance <= entry->rate)
 					break;
 			}
 
@@ -7022,7 +7032,7 @@ void mob_reload_itemmob_data(void) {
 
 			if (id->mob[k].id != pair.first)
 				memmove(&id->mob[k+1], &id->mob[k], (MAX_SEARCH-k-1)*sizeof(id->mob[0]));
-			id->mob[k].chance = pair.second->dropitem[d].rate;
+			id->mob[k].chance = entry->rate;
 			id->mob[k].id = pair.first;
 		}
 	}

+ 28 - 23
src/map/mob.hpp

@@ -22,13 +22,14 @@ struct guardian_data;
 //Note: The range is unlimited unless this define is set.
 //#define AUTOLOOT_DISTANCE AREA_SIZE
 
-//The number of drops all mobs have and the max drop-slot that the steal skill will attempt to steal from.
-#define MAX_MOB_DROP 10
-#define MAX_MVP_DROP 3
-#define MAX_MOB_DROP_ADD 5
-#define MAX_MVP_DROP_ADD 2
-#define MAX_MOB_DROP_TOTAL (MAX_MOB_DROP+MAX_MOB_DROP_ADD)
-#define MAX_MVP_DROP_TOTAL (MAX_MVP_DROP+MAX_MVP_DROP_ADD)
+// The number of drops all mobs can have
+#ifndef MAX_MOB_DROP
+	#define MAX_MOB_DROP 10
+#endif
+// The number of MVP drops all mobs can have
+#ifndef MAX_MVP_DROP
+	#define MAX_MVP_DROP 3
+#endif
 
 //Min time between AI executions
 const t_tick MIN_MOBTHINKTIME = 100;
@@ -255,20 +256,24 @@ struct s_mob_drop {
 };
 
 struct s_mob_db {
-	uint32 id{};
-	std::string sprite{}, name{}, jname{};
-	t_exp base_exp{};
-	t_exp job_exp{};
-	t_exp mexp{};
-	uint16 range2{}, range3{};
-	std::vector<e_race2> race2{};	// celest
-	uint16 lv{ 1 };
-	s_mob_drop dropitem[MAX_MOB_DROP_TOTAL]{}, mvpitem[MAX_MVP_DROP_TOTAL]{};
-	status_data status{};
-	view_data vd{};
-	uint32 option{};
-	std::vector<std::shared_ptr<s_mob_skill>> skill{};
-	uint16 damagetaken{ 100 };
+	uint32 id;
+	std::string sprite;
+	std::string name;
+	std::string jname;
+	t_exp base_exp;
+	t_exp job_exp;
+	t_exp mexp;
+	uint16 range2;
+	uint16 range3;
+	std::vector<e_race2> race2;
+	uint16 lv;
+	std::vector<std::shared_ptr<s_mob_drop>> dropitem;
+	std::vector<std::shared_ptr<s_mob_drop>> mvpitem;
+	status_data status;
+	view_data vd;
+	uint32 option;
+	std::vector<std::shared_ptr<s_mob_skill>> skill;
+	uint16 damagetaken;
 
 	e_mob_bosstype get_bosstype();
 	s_mob_db();
@@ -276,7 +281,7 @@ struct s_mob_db {
 
 class MobDatabase : public TypesafeCachedYamlDatabase <uint32, s_mob_db> {
 private:
-	bool parseDropNode(std::string nodeName, const ryml::NodeRef& node, uint8 max, s_mob_drop *drops);
+	bool parseDropNode( std::string nodeName, const ryml::NodeRef& node, uint8 max, std::vector<std::shared_ptr<s_mob_drop>>& drops );
 
 public:
 	MobDatabase() : TypesafeCachedYamlDatabase("MOB_DB", 4, 1) {
@@ -566,7 +571,7 @@ TIMER_FUNC(mvptomb_delayspawn);
 void mvptomb_create(struct mob_data *md, char *killer, time_t time);
 void mvptomb_destroy(struct mob_data *md);
 
-void mob_setdropitem_option( item& itm, s_mob_drop& mobdrop );
+void mob_setdropitem_option( item& itm, const std::shared_ptr<s_mob_drop>& mobdrop );
 
 #define CHK_MOBSIZE(size) ((size) >= SZ_SMALL && (size) < SZ_MAX) /// Check valid Monster Size
 

+ 31 - 14
src/map/pc.cpp

@@ -6672,7 +6672,6 @@ int32 pc_show_steal(struct block_list *bl,va_list ap)
  */
 bool pc_steal_item(map_session_data *sd,struct block_list *bl, uint16 skill_lv)
 {
-	int32 i;
 	t_itemid itemid;
 	double rate;
 	unsigned char flag = 0;
@@ -6709,25 +6708,43 @@ bool pc_steal_item(map_session_data *sd,struct block_list *bl, uint16 skill_lv)
 	)
 		return false;
 
-	// Try dropping one item, in the order from first to last possible slot.
-	// Droprate is affected by the skill success rate.
-	for( i = 0; i < MAX_MOB_DROP; i++ )
-		if( item_db.exists(md->db->dropitem[i].nameid) && !md->db->dropitem[i].steal_protected && rnd() % 10000 < md->db->dropitem[i].rate
-#ifndef RENEWAL
-		* rate/100.
-#endif
-		)
+	std::shared_ptr<s_mob_drop> drop = nullptr;
+
+	// Try dropping one item.
+	for( std::shared_ptr<s_mob_drop>& entry : md->db->dropitem ){
+		if( entry->steal_protected ){
+			continue;
+		}
+
+		if( !item_db.exists( entry->nameid ) ){
+			continue;
+		}
+
+#ifdef RENEWAL
+		if( rnd() % 10000 < entry->rate ){
+			drop = entry;
+			break;
+		}
+#else
+		// Droprate is affected by the skill success rate.
+		if( rnd() % 10000 < entry->rate * rate / 100. ){
+			drop = entry;
 			break;
-	if( i == MAX_MOB_DROP )
+		}
+#endif
+	}
+
+	if( drop == nullptr ){
 		return false;
+	}
 
-	itemid = md->db->dropitem[i].nameid;
+	itemid = drop->nameid;
 	struct item tmp_item = {};
 	tmp_item.nameid = itemid;
 	tmp_item.amount = 1;
 	tmp_item.identify = itemdb_isidentified(itemid);
 	if( battle_config.skill_steal_random_options ){
-		mob_setdropitem_option( tmp_item, md->db->dropitem[i] );
+		mob_setdropitem_option( tmp_item, drop );
 	}
 	flag = pc_additem(sd,&tmp_item,1,LOG_TYPE_PICKDROP_PLAYER);
 
@@ -6746,11 +6763,11 @@ bool pc_steal_item(map_session_data *sd,struct block_list *bl, uint16 skill_lv)
 	log_pick_mob(md, LOG_TYPE_STEAL, -1, &tmp_item);
 
 	//A Rare Steal Global Announce by Lupus
-	if(md->db->dropitem[i].rate <= battle_config.rare_drop_announce) {
+	if(drop->rate <= battle_config.rare_drop_announce) {
 		struct item_data *i_data;
 		char message[128];
 		i_data = itemdb_search(itemid);
-		sprintf (message, msg_txt(sd,542), (sd->status.name[0])?sd->status.name :"GM", md->db->jname.c_str(), i_data->ename.c_str(), (float)md->db->dropitem[i].rate / 100);
+		sprintf (message, msg_txt(sd,542), (sd->status.name[0])?sd->status.name :"GM", md->db->jname.c_str(), i_data->ename.c_str(), (float)drop->rate / 100);
 		//MSG: "'%s' stole %s's %s (chance: %0.02f%%)"
 		intif_broadcast(message, strlen(message) + 1, BC_DEFAULT);
 	}

+ 44 - 38
src/map/script.cpp

@@ -11320,7 +11320,7 @@ BUILDIN_FUNC(monster)
 BUILDIN_FUNC(getmobdrops)
 {
 	int32 class_ = script_getnum(st,2);
-	int32 i, j = 0;
+	int32 j = 0;
 
 	if( !mobdb_checkid(class_) )
 	{
@@ -11330,17 +11330,16 @@ BUILDIN_FUNC(getmobdrops)
 
 	std::shared_ptr<s_mob_db> mob = mob_db.find(class_);
 
-	for( i = 0; i < MAX_MOB_DROP_TOTAL; i++ )
-	{
-		if( mob->dropitem[i].nameid == 0 )
+	for( const std::shared_ptr<s_mob_drop>& entry : mob->dropitem ){
+		if( entry->nameid == 0 )
 			continue;
-		if( !item_db.exists(mob->dropitem[i].nameid) )
+		if( !item_db.exists(entry->nameid) )
 			continue;
 
-		mapreg_setreg(reference_uid(add_str("$@MobDrop_item"), j), mob->dropitem[i].nameid);
-		mapreg_setreg(reference_uid(add_str("$@MobDrop_rate"), j), mob->dropitem[i].rate);
-		mapreg_setreg(reference_uid(add_str("$@MobDrop_nosteal"), j), mob->dropitem[i].steal_protected);
-		mapreg_setreg(reference_uid(add_str("$@MobDrop_randomopt"), j), mob->dropitem[i].randomopt_group);
+		mapreg_setreg(reference_uid(add_str("$@MobDrop_item"), j), entry->nameid);
+		mapreg_setreg(reference_uid(add_str("$@MobDrop_rate"), j), entry->rate);
+		mapreg_setreg(reference_uid(add_str("$@MobDrop_nosteal"), j), entry->steal_protected);
+		mapreg_setreg(reference_uid(add_str("$@MobDrop_randomopt"), j), entry->randomopt_group);
 
 		j++;
 	}
@@ -18532,22 +18531,24 @@ BUILDIN_FUNC(addmonsterdrop)
 		return SCRIPT_CMD_FAILURE;
 	}
 
-	uint16 c = 0;
+	std::shared_ptr<s_mob_drop> drop = nullptr;
 
-	for (uint16 i = 0; i < MAX_MOB_DROP_TOTAL; i++) {
-		if (mob->dropitem[i].nameid > 0) {
-			if (mob->dropitem[i].nameid == item_id) { // If it equals item_id we update that drop
-				c = i;
-				break;
-			}
-			continue;
+	for( std::shared_ptr<s_mob_drop>& entry : mob->dropitem ){
+		// If it equals item_id we update that drop
+		if( entry->nameid == item_id ){
+			drop = entry;
+			break;
 		}
-		if (c == 0) // Accept first available slot only
-			c = i;
 	}
-	if (c == 0) { // No place to put the new drop
-		script_pushint(st, false);
-		return SCRIPT_CMD_SUCCESS;
+
+	if( drop == nullptr ){
+		// No place to put the new drop
+		if( mob->dropitem.size() == MAX_MOB_DROP ){
+			script_pushint(st, false);
+			return SCRIPT_CMD_SUCCESS;
+		}
+
+		drop = std::make_shared<s_mob_drop>();
 	}
 
 	int32 steal_protected = 0;
@@ -18571,10 +18572,10 @@ BUILDIN_FUNC(addmonsterdrop)
 	}
 
 	// Fill in the slot with the item and rate
-	mob->dropitem[c].nameid = item_id;
-	mob->dropitem[c].rate = rate;
-	mob->dropitem[c].steal_protected = steal_protected > 0;
-	mob->dropitem[c].randomopt_group = group;
+	drop->nameid = item_id;
+	drop->rate = rate;
+	drop->steal_protected = steal_protected > 0;
+	drop->randomopt_group = group;
 	mob_reload_itemmob_data(); // Reload the mob search data stored in the item_data
 
 	script_pushint(st, true);
@@ -18607,20 +18608,25 @@ BUILDIN_FUNC(delmonsterdrop)
 	}
 
 	if(mob) { //We got a valid monster, check for item drop on monster
-		unsigned char i;
-		for(i = 0; i < MAX_MOB_DROP_TOTAL; i++) {
-			if(mob->dropitem[i].nameid == item_id) {
-				mob->dropitem[i].nameid = 0;
-				mob->dropitem[i].rate = 0;
-				mob->dropitem[i].steal_protected = false;
-				mob->dropitem[i].randomopt_group = 0;
-				mob_reload_itemmob_data(); // Reload the mob search data stored in the item_data
-				script_pushint(st,1);
-				return SCRIPT_CMD_SUCCESS;
+		bool found = false;
+
+		for( auto it = mob->dropitem.begin(); it != mob->dropitem.end(); ){
+			if( (*it)->nameid == item_id) {
+				it = mob->dropitem.erase( it );
+				found = true;
+			}else{
+				it++;
 			}
 		}
-		//No drop on that monster
-		script_pushint(st,0);
+
+		if( found ){
+			// Reload the mob search data stored in the item_data
+			mob_reload_itemmob_data();
+			script_pushint(st,1);
+		}else{
+			//No drop on that monster
+			script_pushint(st,0);
+		}
 	} else {
 		ShowWarning("delmonsterdrop: bad mob id given %d\n",script_getnum(st,2));
 		return SCRIPT_CMD_FAILURE;