|
@@ -81,6 +81,7 @@ struct s_mob_skill_db {
|
|
std::unordered_map<int32, std::shared_ptr<s_mob_skill_db>> mob_skill_db; /// Monster skill temporary db. s_mob_skill_db -> mobid
|
|
std::unordered_map<int32, std::shared_ptr<s_mob_skill_db>> mob_skill_db; /// Monster skill temporary db. s_mob_skill_db -> mobid
|
|
|
|
|
|
std::unordered_map<uint32, std::shared_ptr<s_item_drop_list>> mob_delayed_drops;
|
|
std::unordered_map<uint32, std::shared_ptr<s_item_drop_list>> mob_delayed_drops;
|
|
|
|
+std::unordered_map<uint32, std::shared_ptr<s_item_drop_list>> mob_looted_drops;
|
|
MobSummonDatabase mob_summon_db;
|
|
MobSummonDatabase mob_summon_db;
|
|
MobChatDatabase mob_chat_db;
|
|
MobChatDatabase mob_chat_db;
|
|
MapDropDatabase map_drop_db;
|
|
MapDropDatabase map_drop_db;
|
|
@@ -2216,25 +2217,53 @@ static std::shared_ptr<s_item_drop> mob_setlootitem( s_mob_lootitem& item, unsig
|
|
return drop;
|
|
return drop;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/**
|
|
|
|
+ * Makes all items from a drop list drop
|
|
|
|
+ * @param list: list with all items that should drop
|
|
|
|
+ * @param loot: whether the items in the list are new drops or previously looted items
|
|
|
|
+ */
|
|
|
|
+void mob_process_drop_list(std::shared_ptr<s_item_drop_list>& list, bool loot)
|
|
|
|
+{
|
|
|
|
+ // First regular drop always drops at center
|
|
|
|
+ enum directions dir = DIR_CENTER;
|
|
|
|
+ // Looted drops start north instead
|
|
|
|
+ if (loot)
|
|
|
|
+ dir = DIR_NORTH;
|
|
|
|
+
|
|
|
|
+ for (std::shared_ptr<s_item_drop>& ditem : list->items) {
|
|
|
|
+ map_addflooritem(&ditem->item_data, ditem->item_data.amount,
|
|
|
|
+ list->m, list->x, list->y,
|
|
|
|
+ list->first_charid, list->second_charid, list->third_charid, 4, ditem->mob_id, !loot, dir, BL_CHAR|BL_PET);
|
|
|
|
+ // The drop location loops between three locations: SE -> W -> N -> SE
|
|
|
|
+ if (dir <= DIR_NORTH)
|
|
|
|
+ dir = DIR_SOUTHEAST;
|
|
|
|
+ else if (dir == DIR_SOUTHEAST)
|
|
|
|
+ dir = DIR_WEST;
|
|
|
|
+ else
|
|
|
|
+ dir = DIR_NORTH;
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
/*==========================================
|
|
/*==========================================
|
|
* item drop with delay (timer function)
|
|
* item drop with delay (timer function)
|
|
*------------------------------------------*/
|
|
*------------------------------------------*/
|
|
-static TIMER_FUNC(mob_delay_item_drop){
|
|
|
|
- uint32 bl_id = static_cast<uint32>( id );
|
|
|
|
- std::shared_ptr<s_item_drop_list> list = util::umap_find( mob_delayed_drops, bl_id );
|
|
|
|
|
|
+static TIMER_FUNC(mob_delay_item_drop) {
|
|
|
|
+ uint32 bl_id = static_cast<uint32>(id);
|
|
|
|
|
|
- if( list == nullptr ){
|
|
|
|
- return 0;
|
|
|
|
|
|
+ // Regular drops
|
|
|
|
+ std::shared_ptr<s_item_drop_list> list = util::umap_find(mob_delayed_drops, bl_id);
|
|
|
|
+ if (list != nullptr) {
|
|
|
|
+ mob_process_drop_list(list, false);
|
|
|
|
+ mob_delayed_drops.erase(bl_id);
|
|
}
|
|
}
|
|
|
|
|
|
- for( std::shared_ptr<s_item_drop>& ditem : list->items ){
|
|
|
|
- map_addflooritem(&ditem->item_data,ditem->item_data.amount,
|
|
|
|
- list->m,list->x,list->y,
|
|
|
|
- list->first_charid,list->second_charid,list->third_charid,4,ditem->mob_id,true);
|
|
|
|
|
|
+ // Looted drops
|
|
|
|
+ list = util::umap_find(mob_looted_drops, bl_id);
|
|
|
|
+ if (list != nullptr) {
|
|
|
|
+ mob_process_drop_list(list, true);
|
|
|
|
+ mob_looted_drops.erase(bl_id);
|
|
}
|
|
}
|
|
|
|
|
|
- mob_delayed_drops.erase( bl_id );
|
|
|
|
-
|
|
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -2833,6 +2862,24 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type)
|
|
|
|
|
|
} //End EXP giving.
|
|
} //End EXP giving.
|
|
|
|
|
|
|
|
+ // Looted items have an independent drop position and also don't show special effects when dropped
|
|
|
|
+ // So we need put them into a separate list
|
|
|
|
+ std::shared_ptr<s_item_drop_list> lootlist = std::make_shared<s_item_drop_list>();
|
|
|
|
+ lootlist->m = md->bl.m;
|
|
|
|
+ lootlist->x = md->bl.x;
|
|
|
|
+ lootlist->y = md->bl.y;
|
|
|
|
+ lootlist->first_charid = (mvp_sd ? mvp_sd->status.char_id : 0);
|
|
|
|
+ lootlist->second_charid = (second_sd ? second_sd->status.char_id : 0);
|
|
|
|
+ lootlist->third_charid = (third_sd ? third_sd->status.char_id : 0);
|
|
|
|
+
|
|
|
|
+ // Process items looted by the mob
|
|
|
|
+ if (md->lootitems) {
|
|
|
|
+ for (i = 0; i < md->lootitem_count; i++) {
|
|
|
|
+ std::shared_ptr<s_item_drop> ditem = mob_setlootitem(md->lootitems[i], md->mob_id);
|
|
|
|
+ mob_item_drop(md, lootlist, ditem, 1, 10000, homkillonly || merckillonly);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
if( !(type&1) && !map_getmapflag(m, MF_NOMOBLOOT) && !md->state.rebirth && (
|
|
if( !(type&1) && !map_getmapflag(m, MF_NOMOBLOOT) && !md->state.rebirth && (
|
|
!md->special_state.ai || //Non special mob
|
|
!md->special_state.ai || //Non special mob
|
|
battle_config.alchemist_summon_reward == 2 || //All summoned give drops
|
|
battle_config.alchemist_summon_reward == 2 || //All summoned give drops
|
|
@@ -2846,7 +2893,6 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type)
|
|
#endif
|
|
#endif
|
|
|
|
|
|
std::shared_ptr<s_item_drop_list> dlist = std::make_shared<s_item_drop_list>();
|
|
std::shared_ptr<s_item_drop_list> dlist = std::make_shared<s_item_drop_list>();
|
|
-
|
|
|
|
dlist->m = md->bl.m;
|
|
dlist->m = md->bl.m;
|
|
dlist->x = md->bl.x;
|
|
dlist->x = md->bl.x;
|
|
dlist->y = md->bl.y;
|
|
dlist->y = md->bl.y;
|
|
@@ -2854,40 +2900,6 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type)
|
|
dlist->second_charid = (second_sd ? second_sd->status.char_id : 0);
|
|
dlist->second_charid = (second_sd ? second_sd->status.char_id : 0);
|
|
dlist->third_charid = (third_sd ? third_sd->status.char_id : 0);
|
|
dlist->third_charid = (third_sd ? third_sd->status.char_id : 0);
|
|
|
|
|
|
- for (i = 0; i < MAX_MOB_DROP_TOTAL; i++) {
|
|
|
|
- if (md->db->dropitem[i].nameid == 0)
|
|
|
|
- continue;
|
|
|
|
-
|
|
|
|
- std::shared_ptr<item_data> it = item_db.find(md->db->dropitem[i].nameid);
|
|
|
|
-
|
|
|
|
- if ( it == nullptr )
|
|
|
|
- continue;
|
|
|
|
-
|
|
|
|
- drop_rate = mob_getdroprate(src, md->db, md->db->dropitem[i].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);
|
|
|
|
- continue;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- std::shared_ptr<s_item_drop> ditem = mob_setdropitem( md->db->dropitem[i], 1, md->mob_id );
|
|
|
|
-
|
|
|
|
- //A Rare Drop Global Announce by Lupus
|
|
|
|
- if( mvp_sd && md->db->dropitem[i].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%%)"
|
|
|
|
- intif_broadcast(message,strlen(message)+1,BC_DEFAULT);
|
|
|
|
- }
|
|
|
|
- // 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);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
// Ore Discovery [Celest]
|
|
// Ore Discovery [Celest]
|
|
if (sd == mvp_sd && pc_checkskill(sd,BS_FINDINGORE)>0 && battle_config.finding_ore_rate/10 >= rnd()%10000) {
|
|
if (sd == mvp_sd && pc_checkskill(sd,BS_FINDINGORE)>0 && battle_config.finding_ore_rate/10 >= rnd()%10000) {
|
|
s_mob_drop mobdrop = {};
|
|
s_mob_drop mobdrop = {};
|
|
@@ -2943,13 +2955,39 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- // process items looted by the mob
|
|
|
|
- if (md->lootitems) {
|
|
|
|
- for (i = 0; i < md->lootitem_count; i++) {
|
|
|
|
- std::shared_ptr<s_item_drop> ditem = mob_setlootitem(md->lootitems[i], md->mob_id);
|
|
|
|
|
|
+ // Regular mob drops drop after script-granted drops
|
|
|
|
+ for (i = 0; i < MAX_MOB_DROP_TOTAL; i++) {
|
|
|
|
+ if (md->db->dropitem[i].nameid == 0)
|
|
|
|
+ continue;
|
|
|
|
+
|
|
|
|
+ std::shared_ptr<item_data> it = item_db.find(md->db->dropitem[i].nameid);
|
|
|
|
|
|
- mob_item_drop( md, dlist, ditem, 1, 10000, homkillonly || merckillonly );
|
|
|
|
|
|
+ if (it == nullptr)
|
|
|
|
+ continue;
|
|
|
|
+
|
|
|
|
+ drop_rate = mob_getdroprate(src, md->db, md->db->dropitem[i].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);
|
|
|
|
+ continue;
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ std::shared_ptr<s_item_drop> ditem = mob_setdropitem(md->db->dropitem[i], 1, md->mob_id);
|
|
|
|
+
|
|
|
|
+ //A Rare Drop Global Announce by Lupus
|
|
|
|
+ if (mvp_sd && md->db->dropitem[i].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%%)"
|
|
|
|
+ intif_broadcast(message, strlen(message) + 1, BC_DEFAULT);
|
|
|
|
+ }
|
|
|
|
+ // 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);
|
|
}
|
|
}
|
|
|
|
|
|
// Process map specific drops
|
|
// Process map specific drops
|
|
@@ -2989,29 +3027,16 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type)
|
|
}
|
|
}
|
|
|
|
|
|
// There are drop items.
|
|
// There are drop items.
|
|
- if( !dlist->items.empty() ){
|
|
|
|
|
|
+ if (!dlist->items.empty() || !lootlist->items.empty()) {
|
|
mob_delayed_drops[md->bl.id] = dlist;
|
|
mob_delayed_drops[md->bl.id] = dlist;
|
|
-
|
|
|
|
- add_timer( tick + ( !battle_config.delay_battle_damage ? 500 : 0 ), mob_delay_item_drop, md->bl.id, 0 );
|
|
|
|
- }
|
|
|
|
- } else if (md->lootitems && md->lootitem_count) { //Loot MUST drop!
|
|
|
|
- std::shared_ptr<s_item_drop_list> dlist = std::make_shared<s_item_drop_list>();
|
|
|
|
-
|
|
|
|
- dlist->m = md->bl.m;
|
|
|
|
- dlist->x = md->bl.x;
|
|
|
|
- dlist->y = md->bl.y;
|
|
|
|
- dlist->first_charid = (mvp_sd ? mvp_sd->status.char_id : 0);
|
|
|
|
- dlist->second_charid = (second_sd ? second_sd->status.char_id : 0);
|
|
|
|
- dlist->third_charid = (third_sd ? third_sd->status.char_id : 0);
|
|
|
|
-
|
|
|
|
- for (i = 0; i < md->lootitem_count; i++) {
|
|
|
|
- std::shared_ptr<s_item_drop> ditem = mob_setlootitem(md->lootitems[i], md->mob_id);
|
|
|
|
-
|
|
|
|
- mob_item_drop( md, dlist, ditem, 1, 10000, homkillonly || merckillonly );
|
|
|
|
|
|
+ mob_looted_drops[md->bl.id] = lootlist;
|
|
|
|
+ add_timer(tick + (!battle_config.delay_battle_damage ? 500 : 0), mob_delay_item_drop, md->bl.id, 0);
|
|
}
|
|
}
|
|
-
|
|
|
|
- mob_delayed_drops[md->bl.id] = dlist;
|
|
|
|
- add_timer( tick + ( !battle_config.delay_battle_damage ? 500 : 0 ), mob_delay_item_drop, md->bl.id, 0 );
|
|
|
|
|
|
+ }
|
|
|
|
+ // Loot MUST drop!
|
|
|
|
+ else if (!lootlist->items.empty()) {
|
|
|
|
+ mob_looted_drops[md->bl.id] = lootlist;
|
|
|
|
+ add_timer(tick + (!battle_config.delay_battle_damage ? 500 : 0), mob_delay_item_drop, md->bl.id, 0);
|
|
}
|
|
}
|
|
|
|
|
|
if( mvp_sd && md->get_bosstype() == BOSSTYPE_MVP ){
|
|
if( mvp_sd && md->get_bosstype() == BOSSTYPE_MVP ){
|
|
@@ -3114,7 +3139,7 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type)
|
|
|
|
|
|
if((temp = pc_additem(mvp_sd,&item,1,LOG_TYPE_PICKDROP_PLAYER)) != 0) {
|
|
if((temp = pc_additem(mvp_sd,&item,1,LOG_TYPE_PICKDROP_PLAYER)) != 0) {
|
|
clif_additem(mvp_sd,0,0,temp);
|
|
clif_additem(mvp_sd,0,0,temp);
|
|
- map_addflooritem(&item,1,mvp_sd->bl.m,mvp_sd->bl.x,mvp_sd->bl.y,mvp_sd->status.char_id,(second_sd?second_sd->status.char_id:0),(third_sd?third_sd->status.char_id:0),1,0,true);
|
|
|
|
|
|
+ map_addflooritem(&item,1,mvp_sd->bl.m,mvp_sd->bl.x,mvp_sd->bl.y,mvp_sd->status.char_id,(second_sd?second_sd->status.char_id:0),(third_sd?third_sd->status.char_id:0),1,0,true,DIR_CENTER);
|
|
}
|
|
}
|
|
|
|
|
|
if (i_data->flag.broadcast)
|
|
if (i_data->flag.broadcast)
|