Skip to content

Commit

Permalink
Keep track of all mobs that drop an item
Browse files Browse the repository at this point in the history
Don't reload the entire db when adding or deleting drops
Fixes rathena#7845
  • Loading branch information
vstumpf committed Dec 26, 2023
1 parent 6750837 commit 87360db
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 69 deletions.
41 changes: 22 additions & 19 deletions src/map/atcommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8368,31 +8368,34 @@ ACMD_FUNC(whodrops)
sprintf(atcmd_output, msg_txt(sd,1285), item_db.create_item_link( id ).c_str(), id->nameid); // Item: '%s' (ID:%u)
clif_displaymessage(fd, atcmd_output);

if (id->mob[0].chance == 0) {
if (id->mobs.empty()) {
strcpy(atcmd_output, msg_txt(sd,1286)); // - Item is not dropped by mobs.
clif_displaymessage(fd, atcmd_output);
} else {
sprintf(atcmd_output, msg_txt(sd,1287), MAX_SEARCH); // - Common mobs with highest drop chance (only max %d are listed):
clif_displaymessage(fd, atcmd_output);
return 0;
}
sprintf(atcmd_output, msg_txt(sd,1287), MAX_SEARCH); // - Common mobs with highest drop chance (only max %d are listed):
clif_displaymessage(fd, atcmd_output);

for (uint16 j=0; j < MAX_SEARCH && id->mob[j].chance > 0; j++)
{
int dropchance = id->mob[j].chance;
std::shared_ptr<s_mob_db> mob = mob_db.find(id->mob[j].id);
if(!mob) continue;
// NB: We don't const-ref this because we modify dropchance, so let's just make a copy
int count = MAX_SEARCH;
for (auto [dropchance, mob_id] : id->mobs) {
std::shared_ptr<s_mob_db> mob = mob_db.find(mob_id);
if (!mob)
continue;

#ifdef RENEWAL_DROP
if( battle_config.atcommand_mobinfo_type ) {
dropchance = dropchance * pc_level_penalty_mod( sd, PENALTY_DROP, mob ) / 100;
if (dropchance <= 0 && !battle_config.drop_rate0item)
dropchance = 1;
}
#endif
if (pc_isvip(sd)) // Display item rate increase for VIP
dropchance += (dropchance * battle_config.vip_drop_increase) / 100;
sprintf(atcmd_output, "- %s (%d): %02.02f%%", mob->jname.c_str(), id->mob[j].id, dropchance/100.);
clif_displaymessage(fd, atcmd_output);
if (battle_config.atcommand_mobinfo_type) {
dropchance = dropchance * pc_level_penalty_mod(sd, PENALTY_DROP, mob) / 100;
if (dropchance <= 0 && !battle_config.drop_rate0item)
dropchance = 1;
}
#endif
if (pc_isvip(sd)) // Display item rate increase for VIP
dropchance += (dropchance * battle_config.vip_drop_increase) / 100;
sprintf(atcmd_output, "- %s (%d): %02.02f%%", mob->jname.c_str(), mob_id, dropchance/100.);
clif_displaymessage(fd, atcmd_output);
if (--count <= 0)
break;
}
}
return 0;
Expand Down
9 changes: 5 additions & 4 deletions src/map/itemdb.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2259,10 +2259,11 @@ struct item_data
// some script commands should be revised as well...
uint64 class_base[3]; //Specifies if the base can wear this item (split in 3 indexes per type: 1-1, 2-1, 2-2)
uint16 class_upper; //Specifies if the class-type can equip it (See e_item_job)
struct {
int chance;
int id;
} mob[MAX_SEARCH]; //Holds the mobs that have the highest drop rate for this item. [Skotlex]
struct drop_chance {
uint32 chance;
uint32 mob_id;
};
std::vector<drop_chance> mobs; // Sorted vector that holds the mobs that have the highest drop rate for this item.
struct script_code *script; //Default script for everything.
struct script_code *equip_script; //Script executed once when equipping.
struct script_code *unequip_script;//Script executed once when unequipping.
Expand Down
46 changes: 18 additions & 28 deletions src/map/mob.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6284,20 +6284,11 @@ static void mob_drop_ratio_adjust(void){
id->maxchance = rate; // item has bigger drop chance or sold in shops
}

for( k = 0; k < MAX_SEARCH; k++ ){
if( id->mob[k].chance <= rate ){
break;
}
}

if( k != MAX_SEARCH ){
if( id->mob[k].id != mob_id ){
memmove( &id->mob[k+1], &id->mob[k], (MAX_SEARCH-k-1)*sizeof(id->mob[0]) );
}
item_data::drop_chance drop{static_cast<uint32>(rate), pair.second->id};

id->mob[k].chance = rate;
id->mob[k].id = mob_id;
}
id->mobs.insert(std::lower_bound(id->mobs.begin(), id->mobs.end(), drop, [](const auto &drop, const auto &it) {
return drop.chance > it.chance;
}), drop);
}

mob->dropitem[j].rate = rate;
Expand Down Expand Up @@ -6609,30 +6600,29 @@ void mob_db_load(bool is_reload){
*/
void mob_reload_itemmob_data(void) {
for( auto const &pair : mob_db ){
int d, k;

if( mob_is_clone( pair.first ) ){
continue;
}

for(d = 0; d < MAX_MOB_DROP_TOTAL; d++) {
struct item_data *id;
for(int d = 0; d < MAX_MOB_DROP_TOTAL; d++) {
if( !pair.second->dropitem[d].nameid )
continue;
id = itemdb_search(pair.second->dropitem[d].nameid);

for (k = 0; k < MAX_SEARCH; k++) {
if (id->mob[k].chance <= pair.second->dropitem[d].rate)
break;
}

if (k == MAX_SEARCH)
struct item_data *id = itemdb_search(pair.second->dropitem[d].nameid);
if (!id)
continue;

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].id = pair.first;
item_data::drop_chance drop{pair.second->dropitem[d].rate, pair.second->id};

id->mobs.insert(
std::lower_bound(
id->mobs.begin(),
id->mobs.end(),
drop,
[](const auto &drop, const auto &it) {
return drop.chance < it.chance;
}),
drop);
}
}
}
Expand Down
58 changes: 40 additions & 18 deletions src/map/script.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18239,7 +18239,16 @@ BUILDIN_FUNC(addmonsterdrop)
mob->dropitem[c].rate = rate;
mob->dropitem[c].steal_protected = steal_protected > 0;
mob->dropitem[c].randomopt_group = group;
mob_reload_itemmob_data(); // Reload the mob search data stored in the item_data
item_data::drop_chance drop{static_cast<uint32>(rate), mob->id};
itm->mobs.insert(
std::lower_bound(
itm->mobs.begin(),
itm->mobs.end(),
drop,
[](const auto &drop, const auto &it) {
return drop.chance > it.chance;
}),
drop);

script_pushint(st, true);
return SCRIPT_CMD_SUCCESS;
Expand Down Expand Up @@ -18270,25 +18279,38 @@ BUILDIN_FUNC(delmonsterdrop)
return SCRIPT_CMD_FAILURE;
}

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;
}
}
//No drop on that monster
script_pushint(st,0);
} else {
ShowWarning("delmonsterdrop: bad mob id given %d\n",script_getnum(st,2));
if (!mob) {
if (script_isstring(st, 2))
ShowWarning("delmonsterdrop: bad mob name given %s\n", script_getstr(st, 2));
else
ShowWarning("delmonsterdrop: bad mob id given %d\n", script_getnum(st, 2));
return SCRIPT_CMD_FAILURE;
}

unsigned char i;
for(i = 0; i < MAX_MOB_DROP_TOTAL; i++) {
if(mob->dropitem[i].nameid != item_id) {
continue;
}
mob->dropitem[i].nameid = 0;
mob->dropitem[i].rate = 0;
mob->dropitem[i].steal_protected = false;
mob->dropitem[i].randomopt_group = 0;

std::shared_ptr<item_data> itm = item_db.find(item_id);
if (itm) {
auto it = std::find_if(itm->mobs.begin(), itm->mobs.end(), [&mob](const auto &drop) {
return drop.mob_id == mob->id;
});
if (it != itm->mobs.end())
itm->mobs.erase(it);
}

script_pushint(st,1);
return SCRIPT_CMD_SUCCESS;
}
//No drop on that monster
script_pushint(st,0);
return SCRIPT_CMD_SUCCESS;
}

Expand Down

0 comments on commit 87360db

Please sign in to comment.