From a2797c275542791decb2a0c47dd79ca718f7d24e Mon Sep 17 00:00:00 2001
From: skotlex <skotlex@54d463be-8e91-2dee-dedb-b68131a5f0ec>
Date: Wed, 8 Mar 2006 16:03:28 +0000
Subject: [PATCH] - Items now store the top MAX_SEARCH drops from mobs and
 lists them through the new atcommand @whodrops. All merged from my server, of
 course =D

git-svn-id: https://svn.code.sf.net/p/rathena/svn/trunk@5515 54d463be-8e91-2dee-dedb-b68131a5f0ec
---
 Changelog-Trunk.txt             |  3 ++
 conf-tmpl/atcommand_athena.conf |  3 ++
 src/map/atcommand.c             | 53 +++++++++++++++++++++++++++++++++
 src/map/atcommand.h             |  1 +
 src/map/itemdb.h                |  5 ++++
 src/map/mob.c                   | 32 ++++++++++++++++----
 6 files changed, 91 insertions(+), 6 deletions(-)

diff --git a/Changelog-Trunk.txt b/Changelog-Trunk.txt
index 867cf5cbe4..34c32e21e3 100644
--- a/Changelog-Trunk.txt
+++ b/Changelog-Trunk.txt
@@ -5,6 +5,9 @@ IF YOU HAVE A WORKING AND TESTED BUGFIX PUT IT INTO STABLE AS WELL AS TRUNK.  EV
 GOES INTO TRUNK AND WILL BE MERGED INTO STABLE BY VALARIS AND WIZPUTER. -- VALARIS
 
 2006/03/08
+	* Added atcommand @whodrops which lists mobs with the top drop-rates for a
+	  given item. List of stored mobs and their dropchances is also defined by
+	  MAX_SEARCH on map.h [Skotlex]
 	* Modified atcommands @mobinfo and @iteminfo to display multiple matches
 	  instead of just one. Max number of matches to display is set in map.h
 	  (MAX_SEARCH) which is currently 5. [Skotlex]
diff --git a/conf-tmpl/atcommand_athena.conf b/conf-tmpl/atcommand_athena.conf
index 549d237857..4cb096fff0 100644
--- a/conf-tmpl/atcommand_athena.conf
+++ b/conf-tmpl/atcommand_athena.conf
@@ -71,6 +71,9 @@ mi: 1
 iteminfo: 1
 ii: 1
 
+// Show who drops an item (mobs with highest drop rate)
+whodrops: 1
+
 // Syncs the position of the player on the client with the one stored in the server.
 refresh: 1
 
diff --git a/src/map/atcommand.c b/src/map/atcommand.c
index f02f974c1b..97682fc5d7 100644
--- a/src/map/atcommand.c
+++ b/src/map/atcommand.c
@@ -266,6 +266,7 @@ ACMD_FUNC(shuffle); // by MouseJstr
 ACMD_FUNC(rates); // by MouseJstr
 
 ACMD_FUNC(iteminfo); // Lupus
+ACMD_FUNC(whodrops); //Skotlex 
 ACMD_FUNC(mapflag); // Lupus
 ACMD_FUNC(me); //added by massdriller, code by lordalfa
 ACMD_FUNC(monsterignore); // [Valaris]
@@ -577,6 +578,7 @@ static AtCommandInfo atcommand_info[] = {
 
 	{ AtCommand_ItemInfo,			"@iteminfo",		 1, atcommand_iteminfo }, // [Lupus]
 	{ AtCommand_ItemInfo,			"@ii",			 1, atcommand_iteminfo }, // [Lupus]
+	{ AtCommand_WhoDrops,			"@whodrops",	 1, atcommand_whodrops }, // [Skotlex]
 	{ AtCommand_MapFlag,			"@mapflag",			99, atcommand_mapflag }, // [Lupus]
 
 	{ AtCommand_Me,					"@me",			20, atcommand_me }, //added by massdriller, code by lordalfa
@@ -9412,6 +9414,57 @@ int atcommand_iteminfo(
 	return -1;
 }
 
+/*==========================================
+ * Show who drops the item.
+ *------------------------------------------
+ */
+int atcommand_whodrops(
+	const int fd, struct map_session_data* sd,
+	const char* command, const char* message)
+{
+	struct item_data *item_data, *item_array[MAX_SEARCH];
+	int i, count = 1;
+
+	if (!message || !*message) {
+		clif_displaymessage(fd, "Please, enter Item name or its ID (usage: @whodrops <item_name_or_ID>).");
+		return -1;
+	}
+	if ((item_array[0] = itemdb_exists(atoi(message))) == NULL)
+		count = itemdb_searchname_array(item_array, MAX_SEARCH, message);
+
+	if (!count) {
+		clif_displaymessage(fd, "Item not found.");
+		return -1;
+	}
+
+	if (count > MAX_SEARCH) {
+		sprintf(atcmd_output, msg_table[269], MAX_SEARCH, count);
+		clif_displaymessage(fd, atcmd_output);
+		count = MAX_SEARCH;
+	}
+	for (i = 0; i < MAX_SEARCH; i++) {
+		item_data = item_array[i];
+		sprintf(atcmd_output, "Item: '%s'[%d]",
+			item_data->jname,item_data->slot);
+		clif_displaymessage(fd, atcmd_output);
+
+		if (item_data->mob[0].chance == 0) {
+			strcpy(atcmd_output, " - Item is not dropped by mobs.");
+			clif_displaymessage(fd, atcmd_output);
+		} else {
+			sprintf(atcmd_output, "- Common mobs with highest drop chance (only max %d are listed):", MAX_SEARCH);
+			clif_displaymessage(fd, atcmd_output);
+		
+			for (i=0; i < MAX_SEARCH && item_data->mob[i].chance > 0; i++)
+			{
+				sprintf(atcmd_output, "- %s (%02.02f%%)", mob_db(item_data->mob[i].id)->jname, item_data->mob[i].chance/100.);
+				clif_displaymessage(fd, atcmd_output);
+			}
+		}
+	}
+	return 0;
+}
+
 /*==========================================
  * @adopt by [Veider]
  *
diff --git a/src/map/atcommand.h b/src/map/atcommand.h
index a84b8ba8c0..cd90468aa6 100644
--- a/src/map/atcommand.h
+++ b/src/map/atcommand.h
@@ -245,6 +245,7 @@ enum AtCommandType {
 	AtCommand_Rates, // MouseJstr
 
 	AtCommand_ItemInfo, // Lupus
+	AtCommand_WhoDrops, // Skotlex
 	AtCommand_MapFlag, // Lupus
 	AtCommand_MonsterIgnore, // [Valaris]
 	AtCommand_FakeName, // [Valaris]
diff --git a/src/map/itemdb.h b/src/map/itemdb.h
index c438631551..d95d64a43e 100644
--- a/src/map/itemdb.h
+++ b/src/map/itemdb.h
@@ -15,6 +15,11 @@ struct item_data {
 	int value_sell;
 	int type;
 	int maxchance; //For logs, for external game info, for scripts: Max drop chance of this item (e.g. 0.01% , etc.. if it = 0, then monsters don't drop it) [Lupus]
+	struct {
+		unsigned short chance;
+		int id;
+	} mob[MAX_SEARCH]; //Holds the mobs that have the highest drop rate for this item. [Skotlex]
+
 	int sex;
 	int equip;
 	int weight;
diff --git a/src/map/mob.c b/src/map/mob.c
index c17079add8..3995eba310 100644
--- a/src/map/mob.c
+++ b/src/map/mob.c
@@ -4296,7 +4296,7 @@ static int mob_readdb(void)
 	FILE *fp;
 	char line[1024];
 	char *filename[]={ "mob_db.txt","mob_db2.txt" };
-	int class_, i, fi;
+	int class_, i, fi, k;
 
 	for(fi=0;fi<2;fi++){
 		sprintf(line, "%s/%s", db_path, filename[fi]);
@@ -4433,12 +4433,22 @@ static int mob_readdb(void)
 				mob_db_data[class_]->dropitem[i].p = mob_drop_adjust(rate, rate_adjust, ratemin, ratemax);
 
 				//calculate and store Max available drop chance of the item
-				id = itemdb_search(mob_db_data[class_]->dropitem[i].nameid);
 				if (mob_db_data[class_]->dropitem[i].p) {
+					id = itemdb_search(mob_db_data[class_]->dropitem[i].nameid);
 					if (id->maxchance==10000 || (id->maxchance < mob_db_data[class_]->dropitem[i].p) ) {
 					//item has bigger drop chance or sold in shops
 						id->maxchance = mob_db_data[class_]->dropitem[i].p;
-					}			
+					}
+					for (k = 0; k< MAX_SEARCH; k++) {
+						if (id->mob[k].chance < mob_db_data[class_]->dropitem[i].p && id->mob[k].id != class_)
+							break;
+					}
+					if (k == MAX_SEARCH)
+						continue;
+				
+					memmove(&id->mob[k+1], &id->mob[k], (MAX_SEARCH-k-1)*sizeof(id->mob[0]));
+					id->mob[k].chance = mob_db_data[class_]->dropitem[i].p;
+					id->mob[k].id = class_;
 				}
 			}
 			// MVP EXP Bonus, Chance: MEXP,ExpPer
@@ -4470,8 +4480,8 @@ static int mob_readdb(void)
 					battle_config.item_drop_mvp_min, battle_config.item_drop_mvp_max);
 
 				//calculate and store Max available drop chance of the MVP item
-				id = itemdb_search(mob_db_data[class_]->mvpitem[i].nameid);
 				if (mob_db_data[class_]->mvpitem[i].p) {
+					id = itemdb_search(mob_db_data[class_]->mvpitem[i].nameid);
 					if (id->maxchance==10000 || (id->maxchance < mob_db_data[class_]->mvpitem[i].p/10+1) ) {
 					//item has bigger drop chance or sold in shops
 						id->maxchance = mob_db_data[class_]->mvpitem[i].p/10+1; //reduce MVP drop info to not spoil common drop rate
@@ -4910,7 +4920,7 @@ static int mob_readdb_race(void)
 static int mob_read_sqldb(void)
 {
 	const char unknown_str[NAME_LENGTH] ="unknown";
-	int i, fi, class_;
+	int i, fi, class_, k;
 	double exp, maxhp;
 	long unsigned int ln = 0;
 	char *mob_db_name[] = { mob_db_db, mob_db2_db };
@@ -5039,12 +5049,22 @@ static int mob_read_sqldb(void)
 					mob_db_data[class_]->dropitem[i].p = mob_drop_adjust(rate, rate_adjust, ratemin, ratemax);
 
 					//calculate and store Max available drop chance of the item
-					id = itemdb_search(mob_db_data[class_]->dropitem[i].nameid);
 					if (mob_db_data[class_]->dropitem[i].p) {
+						id = itemdb_search(mob_db_data[class_]->dropitem[i].nameid);
 						if (id->maxchance==10000 || (id->maxchance < mob_db_data[class_]->dropitem[i].p) ) {
 						//item has bigger drop chance or sold in shops
 							id->maxchance = mob_db_data[class_]->dropitem[i].p;
 						}			
+						for (k = 0; k< MAX_SEARCH; k++) {
+							if (id->mob[k].chance < mob_db_data[class_]->dropitem[i].p && id->mob[k].id != class_)
+								break;
+						}
+						if (k == MAX_SEARCH)
+							continue;
+					
+						memmove(&id->mob[k+1], &id->mob[k], (MAX_SEARCH-k-1)*sizeof(id->mob[0]));
+						id->mob[k].chance = mob_db_data[class_]->dropitem[i].p;
+						id->mob[k].id = class_;
 					}
 				}
 				// MVP EXP Bonus, Chance: MEXP,ExpPer