Monster Loot / Item Dropping Position (#8347)
- When a monster is killed, its first item will now always drop at the exact cell the monster was on - When a monster drops more than one item, the items will be deployed on 3 cells around the monster (looping): SE, W and N - Fixed drop position of looted items (similar to regular drops but independent to it and starts north) - Fixed item drop order (script-granted -> regular drops -> looted drops) - Fixed looted items showing special drop effects - Searching for a free cell to drop an item on now uses the official algorithm - When a monster drops an item, it will no longer drop on cells that are occupied by characters or pets - When a player drops an item, it will now drop in a 5x5 area around the player - Items dropped by players can now stack on the same cell unless the new "item_stacking" config is disabled - When an MVP drop drops to the floor because the player's inventory was full, it will now always drop on that player's cell - Fixes #8345
This commit is contained in:
parent
e8f1c7f9e7
commit
ec9a5fae4b
@ -190,3 +190,10 @@ hide_fav_sell: no
|
|||||||
// affects teleportation. Set this to 1 if you want it to be closer to the old emulator behavior.
|
// affects teleportation. Set this to 1 if you want it to be closer to the old emulator behavior.
|
||||||
// Valid values: 1-40
|
// Valid values: 1-40
|
||||||
map_edge_size: 15
|
map_edge_size: 15
|
||||||
|
|
||||||
|
// When a player drops items, can they stack on the same cell? (Note 1)
|
||||||
|
// Officially there's no limit on how many items you can drop on the same cell.
|
||||||
|
// If you set this to "no", when you drop an item, it will only drop on a cell that has no item on it yet.
|
||||||
|
// A free cell will be searched for in eight directions. If no free cell could be found in those eight tries,
|
||||||
|
// then dropping the item will fail (the item stays in the player's inventory).
|
||||||
|
item_stacking: yes
|
||||||
|
@ -11524,6 +11524,7 @@ static const struct _battle_data {
|
|||||||
{ "feature.instance_allow_reconnect", &battle_config.instance_allow_reconnect, 0, 0, 1, },
|
{ "feature.instance_allow_reconnect", &battle_config.instance_allow_reconnect, 0, 0, 1, },
|
||||||
#endif
|
#endif
|
||||||
{ "synchronize_damage", &battle_config.synchronize_damage, 0, 0, 1, },
|
{ "synchronize_damage", &battle_config.synchronize_damage, 0, 0, 1, },
|
||||||
|
{ "item_stacking", &battle_config.item_stacking, 1, 0, 1, },
|
||||||
|
|
||||||
#include <custom/battle_config_init.inc>
|
#include <custom/battle_config_init.inc>
|
||||||
};
|
};
|
||||||
|
@ -756,6 +756,7 @@ struct Battle_Config
|
|||||||
int feature_banking_state_enforce;
|
int feature_banking_state_enforce;
|
||||||
int instance_allow_reconnect;
|
int instance_allow_reconnect;
|
||||||
int synchronize_damage;
|
int synchronize_damage;
|
||||||
|
int item_stacking;
|
||||||
|
|
||||||
#include <custom/battle_config_struct.inc>
|
#include <custom/battle_config_struct.inc>
|
||||||
};
|
};
|
||||||
|
114
src/map/map.cpp
114
src/map/map.cpp
@ -1644,43 +1644,71 @@ void map_clearflooritem(struct block_list *bl) {
|
|||||||
map_freeblock(&fitem->bl);
|
map_freeblock(&fitem->bl);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*==========================================
|
/**
|
||||||
* (m,x,y) locates a random available free cell around the given coordinates
|
* Returns if a cell is passable and not occupied by given types of block
|
||||||
* to place an BL_ITEM object. Scan area is 9x9, returns 1 on success.
|
* Cells 5 cells from the SW edge and 4 cells from the NE edge are never considered as free
|
||||||
* x and y are modified with the target cell when successful.
|
* @param m: Map of cell to check
|
||||||
*------------------------------------------*/
|
* @param x: X-coordinate of cell to check
|
||||||
int map_searchrandfreecell(int16 m,int16 *x,int16 *y,int stack) {
|
* @param y: Y-coordinate of cell to check
|
||||||
int free_cell,i,j;
|
* @param type: Types of block to check for
|
||||||
int free_cells[9][2];
|
* @return True if cell is passable, not on the edge and not occupied by given types of block
|
||||||
struct map_data *mapdata = map_getmapdata(m);
|
*/
|
||||||
|
bool map_cell_free(int16 m, int16 x, int16 y, int type)
|
||||||
if( mapdata == nullptr || mapdata->block == nullptr ){
|
{
|
||||||
return 0;
|
struct map_data* mapdata = map_getmapdata(m);
|
||||||
|
if (mapdata == nullptr || mapdata->block == nullptr) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
for(free_cell=0,i=-1;i<=1;i++){
|
// Cells outside the map or within 4-5 cells of the map edge are considered invalid officially
|
||||||
if(i+*y<0 || i+*y>=mapdata->ys)
|
// Note that this isn't symmetric (NE - 4 cells, SW - 5 cells)
|
||||||
continue;
|
// If for some reason edge size was set to below 5 cells, we consider them as valid
|
||||||
for(j=-1;j<=1;j++){
|
int16 edge_valid = std::min(battle_config.map_edge_size, 5);
|
||||||
if(j+*x<0 || j+*x>=mapdata->xs)
|
if (x < edge_valid || x > mapdata->xs - edge_valid || y < edge_valid || y > mapdata->ys - edge_valid)
|
||||||
continue;
|
return false;
|
||||||
if(map_getcell(m,j+*x,i+*y,CELL_CHKNOPASS) && !map_getcell(m,j+*x,i+*y,CELL_CHKICEWALL))
|
if (map_getcell(m, x, y, CELL_CHKNOPASS))
|
||||||
continue;
|
return false;
|
||||||
//Avoid item stacking to prevent against exploits. [Skotlex]
|
if (map_count_oncell(m, x, y, type, 0) > 0)
|
||||||
if(stack && map_count_oncell(m,j+*x,i+*y, BL_ITEM, 0) > stack)
|
return false;
|
||||||
continue;
|
|
||||||
free_cells[free_cell][0] = j+*x;
|
return true;
|
||||||
free_cells[free_cell++][1] = i+*y;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(free_cell==0)
|
|
||||||
return 0;
|
|
||||||
free_cell = rnd_value(0, free_cell-1);
|
|
||||||
*x = free_cells[free_cell][0];
|
|
||||||
*y = free_cells[free_cell][1];
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Locates a random available free cell around the given coordinates within a given distance range.
|
||||||
|
* This uses the official algorithm that checks each quadrant and line once.
|
||||||
|
* x and y are modified with the target free cell when successful.
|
||||||
|
* @param m: Map to search
|
||||||
|
* @param x: X-coordinate around which free cell is searched
|
||||||
|
* @param y: Y-coordinate around which free cell is searched
|
||||||
|
* @param distmin: Minimum distance from the given cell
|
||||||
|
* @param distmax: Maximum distance from the given cell
|
||||||
|
* @param type: If the given types of block are present on the cell, it counts as occupied
|
||||||
|
* @return True if free cell could be found
|
||||||
|
*/
|
||||||
|
bool map_search_freecell_dist(int16 m, int16* x, int16* y, int16 distmin, int16 distmax, int type)
|
||||||
|
{
|
||||||
|
// This is to prevent that always the same quadrant is checked first
|
||||||
|
int16 mirrorx = (rnd()%2) ? -1 : 1;
|
||||||
|
int16 mirrory = (rnd()%2) ? -1 : 1;
|
||||||
|
|
||||||
|
for (int16 i = -1; i <= 1; i++) {
|
||||||
|
for (int16 j = -1; j <= 1; j++) {
|
||||||
|
if (i || j)
|
||||||
|
{
|
||||||
|
int16 checkX = *x + mirrorx * i * rnd_value(distmin, distmax);
|
||||||
|
int16 checkY = *y + mirrory * j * rnd_value(distmin, distmax);
|
||||||
|
if (map_cell_free(m, checkX, checkY, type))
|
||||||
|
{
|
||||||
|
*x = checkX;
|
||||||
|
*y = checkY;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
static int map_count_sub(struct block_list *bl,va_list ap)
|
static int map_count_sub(struct block_list *bl,va_list ap)
|
||||||
{
|
{
|
||||||
@ -1860,12 +1888,14 @@ bool map_closest_freecell(int16 m, int16 *x, int16 *y, int type, int flag)
|
|||||||
* @param first_charid : 1st player that could loot the item (only charid that could loot for first_get_tick duration)
|
* @param first_charid : 1st player that could loot the item (only charid that could loot for first_get_tick duration)
|
||||||
* @param second_charid : 2nd player that could loot the item (2nd charid that could loot for second_get_charid duration)
|
* @param second_charid : 2nd player that could loot the item (2nd charid that could loot for second_get_charid duration)
|
||||||
* @param third_charid : 3rd player that could loot the item (3rd charid that could loot for third_get_charid duration)
|
* @param third_charid : 3rd player that could loot the item (3rd charid that could loot for third_get_charid duration)
|
||||||
* @param flag: &1 MVP item. &2 do stacking check. &4 bypass droppable check.
|
* @param flag: &1 MVP item. &2 search free cell in 5x5 area instead of 3x3. &4 bypass droppable check.
|
||||||
* @param mob_id: Monster ID if dropped by monster
|
* @param mob_id: Monster ID if dropped by monster
|
||||||
* @param canShowEffect: enable pillar effect on the dropped item (if set in the database)
|
* @param canShowEffect: enable pillar effect on the dropped item (if set in the database)
|
||||||
|
* @param dir: where the item should drop around the target (DIR_MAX: random cell around center)
|
||||||
|
* @param type: types of block the item should not stack on
|
||||||
* @return 0:failure, x:item_gid [MIN_FLOORITEM;MAX_FLOORITEM]==[2;START_ACCOUNT_NUM]
|
* @return 0:failure, x:item_gid [MIN_FLOORITEM;MAX_FLOORITEM]==[2;START_ACCOUNT_NUM]
|
||||||
*------------------------------------------*/
|
*------------------------------------------*/
|
||||||
int map_addflooritem(struct item *item, int amount, int16 m, int16 x, int16 y, int first_charid, int second_charid, int third_charid, int flags, unsigned short mob_id, bool canShowEffect)
|
int map_addflooritem(struct item *item, int amount, int16 m, int16 x, int16 y, int first_charid, int second_charid, int third_charid, int flags, unsigned short mob_id, bool canShowEffect, enum directions dir, int type)
|
||||||
{
|
{
|
||||||
struct flooritem_data *fitem = nullptr;
|
struct flooritem_data *fitem = nullptr;
|
||||||
|
|
||||||
@ -1874,8 +1904,18 @@ int map_addflooritem(struct item *item, int amount, int16 m, int16 x, int16 y, i
|
|||||||
if (!(flags&4) && battle_config.item_onfloor && (itemdb_traderight(item->nameid).trade))
|
if (!(flags&4) && battle_config.item_onfloor && (itemdb_traderight(item->nameid).trade))
|
||||||
return 0; //can't be dropped
|
return 0; //can't be dropped
|
||||||
|
|
||||||
if (!map_searchrandfreecell(m,&x,&y,flags&2?1:0))
|
if (dir > DIR_CENTER && dir < DIR_MAX) {
|
||||||
return 0;
|
x += dirx[dir];
|
||||||
|
y += diry[dir];
|
||||||
|
}
|
||||||
|
// If cell occupied and not center cell, drop item around the drop target cell
|
||||||
|
if (dir == DIR_MAX || (dir != DIR_CENTER && !map_cell_free(m, x, y, type))) {
|
||||||
|
if (!map_search_freecell_dist(m, &x, &y, 1, (flags&2)?2:1, type)) {
|
||||||
|
// Only stop here if BL_ITEM shall not stack, otherwise drop on original target cell
|
||||||
|
if (type&BL_ITEM)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
CREATE(fitem, struct flooritem_data, 1);
|
CREATE(fitem, struct flooritem_data, 1);
|
||||||
fitem->bl.type=BL_ITEM;
|
fitem->bl.type=BL_ITEM;
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
|
|
||||||
#include "navi.hpp"
|
#include "navi.hpp"
|
||||||
#include "script.hpp"
|
#include "script.hpp"
|
||||||
|
#include "path.hpp"
|
||||||
|
|
||||||
using rathena::server_core::Core;
|
using rathena::server_core::Core;
|
||||||
using rathena::server_core::e_core_type;
|
using rathena::server_core::e_core_type;
|
||||||
@ -1137,7 +1138,7 @@ bool map_addnpc(int16 m,struct npc_data *);
|
|||||||
TIMER_FUNC(map_clearflooritem_timer);
|
TIMER_FUNC(map_clearflooritem_timer);
|
||||||
TIMER_FUNC(map_removemobs_timer);
|
TIMER_FUNC(map_removemobs_timer);
|
||||||
void map_clearflooritem(struct block_list* bl);
|
void map_clearflooritem(struct block_list* bl);
|
||||||
int map_addflooritem(struct item *item, int amount, int16 m, int16 x, int16 y, int first_charid, int second_charid, int third_charid, int flags, unsigned short mob_id, bool canShowEffect = false);
|
int map_addflooritem(struct item *item, int amount, int16 m, int16 x, int16 y, int first_charid, int second_charid, int third_charid, int flags, unsigned short mob_id, bool canShowEffect = false, enum directions dir = DIR_MAX, int type = BL_NUL);
|
||||||
|
|
||||||
// instances
|
// instances
|
||||||
int map_addinstancemap(int src_m, int instance_id, bool no_mapflag);
|
int map_addinstancemap(int src_m, int instance_id, bool no_mapflag);
|
||||||
|
171
src/map/mob.cpp
171
src/map/mob.cpp
@ -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){
|
static TIMER_FUNC(mob_delay_item_drop) {
|
||||||
uint32 bl_id = static_cast<uint32>( id );
|
uint32 bl_id = static_cast<uint32>(id);
|
||||||
std::shared_ptr<s_item_drop_list> list = util::umap_find( mob_delayed_drops, bl_id );
|
|
||||||
|
|
||||||
if( list == nullptr ){
|
// Regular drops
|
||||||
return 0;
|
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 ){
|
// Looted drops
|
||||||
map_addflooritem(&ditem->item_data,ditem->item_data.amount,
|
list = util::umap_find(mob_looted_drops, bl_id);
|
||||||
list->m,list->x,list->y,
|
if (list != nullptr) {
|
||||||
list->first_charid,list->second_charid,list->third_charid,4,ditem->mob_id,true);
|
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
|
// Regular mob drops drop after script-granted drops
|
||||||
if (md->lootitems) {
|
for (i = 0; i < MAX_MOB_DROP_TOTAL; i++) {
|
||||||
for (i = 0; i < md->lootitem_count; i++) {
|
if (md->db->dropitem[i].nameid == 0)
|
||||||
std::shared_ptr<s_item_drop> ditem = mob_setlootitem(md->lootitems[i], md->mob_id);
|
continue;
|
||||||
|
|
||||||
mob_item_drop( md, dlist, ditem, 1, 10000, homkillonly || merckillonly );
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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;
|
||||||
|
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 );
|
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>();
|
// Loot MUST drop!
|
||||||
|
else if (!lootlist->items.empty()) {
|
||||||
dlist->m = md->bl.m;
|
mob_looted_drops[md->bl.id] = lootlist;
|
||||||
dlist->x = md->bl.x;
|
add_timer(tick + (!battle_config.delay_battle_damage ? 500 : 0), mob_delay_item_drop, md->bl.id, 0);
|
||||||
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_delayed_drops[md->bl.id] = dlist;
|
|
||||||
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)
|
||||||
|
@ -6052,9 +6052,12 @@ bool pc_dropitem(map_session_data *sd,int n,int amount)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// bypass drop restriction in map_addflooritem because we've already checked it above
|
// Bypass drop restriction in map_addflooritem because we've already checked it above
|
||||||
if (!map_addflooritem(&sd->inventory.u.items_inventory[n], amount, sd->bl.m, sd->bl.x, sd->bl.y, 0, 0, 0, 2|4, 0))
|
if (!map_addflooritem(&sd->inventory.u.items_inventory[n], amount, sd->bl.m, sd->bl.x, sd->bl.y, 0, 0, 0, 2|4, 0,
|
||||||
|
false, DIR_MAX, battle_config.item_stacking?BL_NUL:BL_ITEM))
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
pc_delitem(sd, n, amount, 1, 0, LOG_TYPE_PICKDROP_PLAYER);
|
pc_delitem(sd, n, amount, 1, 0, LOG_TYPE_PICKDROP_PLAYER);
|
||||||
clif_dropitem( *sd, n, amount );
|
clif_dropitem( *sd, n, amount );
|
||||||
|
Loading…
x
Reference in New Issue
Block a user