Lootdrop level filtering adjustments

This commit is contained in:
Akkadius
2020-08-15 16:37:38 -05:00
parent bda13383ef
commit 94d87584aa
16 changed files with 300 additions and 172 deletions
+1 -1
View File
@@ -448,7 +448,7 @@ void Mob::WakeTheDead(uint16 spell_id, Mob *target, uint32 duration)
sitem = CorpseToUse->GetWornItem(x);
if(sitem){
const EQ::ItemData * itm = database.GetItem(sitem);
npca->AddLootDrop(itm, &npca->itemlist, 1, 1, 255, true, true);
npca->AddLootDrop(itm, &npca->itemlist, LootDropEntries_Struct{ .equip_item = true }, true);
}
}
+1 -1
View File
@@ -2464,7 +2464,7 @@ bool NPC::Death(Mob* killer_mob, int32 damage, uint16 spell, EQ::skills::SkillTy
killer = killer->GetOwner();
if (killer->IsClient() && !killer->CastToClient()->GetGM())
this->CheckMinMaxLevel(killer);
this->CheckTrivialMinMaxLevelDrop(killer);
}
entity_list.RemoveFromAutoXTargets(this);
+161 -99
View File
@@ -29,6 +29,7 @@
#include "zone_store.h"
#include "global_loot_manager.h"
#include "../common/repositories/criteria/content_filter_criteria.h"
#include "../common/say_link.h"
#include <iostream>
#include <stdlib.h>
@@ -116,110 +117,127 @@ void ZoneDatabase::AddLootTableToNPC(NPC* npc,uint32 loottable_id, ItemList* ite
// Called by AddLootTableToNPC
// maxdrops = size of the array npcd
void ZoneDatabase::AddLootDropToNPC(NPC* npc,uint32 lootdrop_id, ItemList* itemlist, uint8 droplimit, uint8 mindrop) {
const LootDrop_Struct* lds = GetLootDrop(lootdrop_id);
if (!lds) {
void ZoneDatabase::AddLootDropToNPC(NPC *npc, uint32 lootdrop_id, ItemList *item_list, uint8 droplimit, uint8 mindrop)
{
const LootDrop_Struct *loot_drop = GetLootDrop(lootdrop_id);
if (!loot_drop) {
return;
}
if(lds->NumEntries == 0)
if (loot_drop->NumEntries == 0) {
return;
}
if(droplimit == 0 && mindrop == 0) {
for(uint32 i = 0; i < lds->NumEntries; ++i) {
int charges = lds->Entries[i].multiplier;
for(int j = 0; j < charges; ++j) {
if(zone->random.Real(0.0, 100.0) <= lds->Entries[i].chance) {
const EQ::ItemData* dbitem = GetItem(lds->Entries[i].item_id);
npc->AddLootDrop(dbitem, itemlist, lds->Entries[i].item_charges, lds->Entries[i].minlevel,
lds->Entries[i].maxlevel, lds->Entries[i].equip_item > 0 ? true : false, false);
if (droplimit == 0 && mindrop == 0) {
for (uint32 i = 0; i < loot_drop->NumEntries; ++i) {
int charges = loot_drop->Entries[i].multiplier;
for (int j = 0; j < charges; ++j) {
if (zone->random.Real(0.0, 100.0) <= loot_drop->Entries[i].chance && npc->MeetsLevelRequirements(loot_drop->Entries[i])) {
const EQ::ItemData *database_item = GetItem(loot_drop->Entries[i].item_id);
npc->AddLootDrop(
database_item,
item_list,
loot_drop->Entries[i]
);
}
}
}
return;
}
if(lds->NumEntries > 100 && droplimit == 0) {
if (loot_drop->NumEntries > 100 && droplimit == 0) {
droplimit = 10;
}
if(droplimit < mindrop) {
if (droplimit < mindrop) {
droplimit = mindrop;
}
float roll_t = 0.0f;
float roll_t_min = 0.0f;
bool active_item_list = false;
for(uint32 i = 0; i < lds->NumEntries; ++i) {
const EQ::ItemData* db_item = GetItem(lds->Entries[i].item_id);
if(db_item) {
roll_t += lds->Entries[i].chance;
float roll_t = 0.0f;
float roll_t_min = 0.0f;
bool active_item_list = false;
for (uint32 i = 0; i < loot_drop->NumEntries; ++i) {
const EQ::ItemData *db_item = GetItem(loot_drop->Entries[i].item_id);
if (db_item) {
roll_t += loot_drop->Entries[i].chance;
active_item_list = true;
}
}
roll_t_min = roll_t;
roll_t = EQ::ClampLower(roll_t, 100.0f);
roll_t = EQ::ClampLower(roll_t, 100.0f);
if(!active_item_list) {
if (!active_item_list) {
return;
}
for(int i = 0; i < mindrop; ++i) {
float roll = (float)zone->random.Real(0.0, roll_t_min);
for(uint32 j = 0; j < lds->NumEntries; ++j) {
const EQ::ItemData* db_item = GetItem(lds->Entries[j].item_id);
if(db_item) {
if(roll < lds->Entries[j].chance) {
npc->AddLootDrop(db_item, itemlist, lds->Entries[j].item_charges, lds->Entries[j].minlevel,
lds->Entries[j].maxlevel, lds->Entries[j].equip_item > 0 ? true : false, false);
for (int i = 0; i < mindrop; ++i) {
float roll = (float) zone->random.Real(0.0, roll_t_min);
for (uint32 j = 0; j < loot_drop->NumEntries; ++j) {
const EQ::ItemData *db_item = GetItem(loot_drop->Entries[j].item_id);
if (db_item) {
if (roll < loot_drop->Entries[j].chance && npc->MeetsLevelRequirements(loot_drop->Entries[j])) {
npc->AddLootDrop(
db_item,
item_list,
loot_drop->Entries[j]
);
int charges = (int)lds->Entries[i].multiplier;
int charges = (int) loot_drop->Entries[i].multiplier;
charges = EQ::ClampLower(charges, 1);
for(int k = 1; k < charges; ++k) {
float c_roll = (float)zone->random.Real(0.0, 100.0);
if(c_roll <= lds->Entries[i].chance) {
npc->AddLootDrop(db_item, itemlist, lds->Entries[j].item_charges, lds->Entries[j].minlevel,
lds->Entries[j].maxlevel, lds->Entries[j].equip_item > 0 ? true : false, false);
for (int k = 1; k < charges; ++k) {
float c_roll = (float) zone->random.Real(0.0, 100.0);
if (c_roll <= loot_drop->Entries[i].chance) {
npc->AddLootDrop(
db_item,
item_list,
loot_drop->Entries[i]
);
}
}
j = lds->NumEntries;
j = loot_drop->NumEntries;
break;
}
else {
roll -= lds->Entries[j].chance;
roll -= loot_drop->Entries[j].chance;
}
}
}
}
for(int i = mindrop; i < droplimit; ++i) {
float roll = (float)zone->random.Real(0.0, roll_t);
for(uint32 j = 0; j < lds->NumEntries; ++j) {
const EQ::ItemData* db_item = GetItem(lds->Entries[j].item_id);
if(db_item) {
if(roll < lds->Entries[j].chance) {
npc->AddLootDrop(db_item, itemlist, lds->Entries[j].item_charges, lds->Entries[j].minlevel,
lds->Entries[j].maxlevel, lds->Entries[j].equip_item > 0 ? true : false, false);
for (int i = mindrop; i < droplimit; ++i) {
float roll = (float) zone->random.Real(0.0, roll_t);
for (uint32 j = 0; j < loot_drop->NumEntries; ++j) {
const EQ::ItemData *db_item = GetItem(loot_drop->Entries[j].item_id);
if (db_item) {
if (roll < loot_drop->Entries[j].chance && npc->MeetsLevelRequirements(loot_drop->Entries[j])) {
npc->AddLootDrop(
db_item,
item_list,
loot_drop->Entries[j]
);
int charges = (int)lds->Entries[i].multiplier;
int charges = (int) loot_drop->Entries[i].multiplier;
charges = EQ::ClampLower(charges, 1);
for(int k = 1; k < charges; ++k) {
float c_roll = (float)zone->random.Real(0.0, 100.0);
if(c_roll <= lds->Entries[i].chance) {
npc->AddLootDrop(db_item, itemlist, lds->Entries[j].item_charges, lds->Entries[j].minlevel,
lds->Entries[j].maxlevel, lds->Entries[j].equip_item > 0 ? true : false, false);
for (int k = 1; k < charges; ++k) {
float c_roll = (float) zone->random.Real(0.0, 100.0);
if (c_roll <= loot_drop->Entries[i].chance) {
npc->AddLootDrop(
db_item,
item_list,
loot_drop->Entries[i]
);
}
}
j = lds->NumEntries;
j = loot_drop->NumEntries;
break;
}
else {
roll -= lds->Entries[j].chance;
roll -= loot_drop->Entries[j].chance;
}
}
}
@@ -231,43 +249,86 @@ void ZoneDatabase::AddLootDropToNPC(NPC* npc,uint32 lootdrop_id, ItemList* iteml
// npc->SendAppearancePacket(AT_Light, npc->GetActiveLightValue());
}
//if itemlist is null, just send wear changes
void NPC::AddLootDrop(const EQ::ItemData *item2, ItemList* itemlist, int16 charges, uint8 minlevel, uint8 maxlevel, bool equipit, bool wearchange, uint32 aug1, uint32 aug2, uint32 aug3, uint32 aug4, uint32 aug5, uint32 aug6) {
if(item2 == nullptr)
return;
//make sure we are doing something...
if(!itemlist && !wearchange)
return;
auto item = new ServerLootItem_Struct;
#if EQDEBUG>=11
LogDebug("Adding drop to npc: [{}], Item: [{}]", GetName(), item2->ID);
#endif
EQApplicationPacket* outapp = nullptr;
WearChange_Struct* wc = nullptr;
if(wearchange) {
outapp = new EQApplicationPacket(OP_WearChange, sizeof(WearChange_Struct));
wc = (WearChange_Struct*)outapp->pBuffer;
wc->spawn_id = GetID();
wc->material=0;
bool NPC::MeetsLevelRequirements(LootDropEntries_Struct loot_drop)
{
if (loot_drop.npc_min_level > 0 && GetLevel() < loot_drop.npc_min_level) {
return false;
}
item->item_id = item2->ID;
item->charges = charges;
item->aug_1 = aug1;
item->aug_2 = aug2;
item->aug_3 = aug3;
item->aug_4 = aug4;
item->aug_5 = aug5;
item->aug_6 = aug6;
item->attuned = 0;
item->min_level = minlevel;
item->max_level = maxlevel;
item->equip_slot = EQ::invslot::SLOT_INVALID;
if (loot_drop.npc_max_level > 0 && GetLevel() > loot_drop.npc_max_level) {
return false;
}
if (equipit) {
return true;
}
//if itemlist is null, just send wear changes
void NPC::AddLootDrop(
const EQ::ItemData *item2,
ItemList *itemlist,
LootDropEntries_Struct loot_drop,
bool wear_change,
uint32 aug1,
uint32 aug2,
uint32 aug3,
uint32 aug4,
uint32 aug5,
uint32 aug6
)
{
if (item2 == nullptr) {
return;
}
//make sure we are doing something...
if (!itemlist && !wear_change) {
return;
}
auto item = new ServerLootItem_Struct;
if (LogSys.log_settings[Logs::Loot].is_category_enabled == 1) {
EQ::SayLinkEngine linker;
linker.SetLinkType(EQ::saylink::SayLinkItemData);
linker.SetItemData(item2);
LogLoot(
"[NPC::AddLootDrop] NPC [{}] Item ({}) [{}] charges [{}] chance [{}] trivial min/max [{}/{}] npc min/max [{}/{}]",
GetName(),
item2->ID,
linker.GenerateLink(),
loot_drop.item_charges,
loot_drop.chance,
loot_drop.trivial_min_level,
loot_drop.trivial_max_level,
loot_drop.npc_min_level,
loot_drop.npc_max_level
);
}
EQApplicationPacket *outapp = nullptr;
WearChange_Struct *p_wear_change_struct = nullptr;
if (wear_change) {
outapp = new EQApplicationPacket(OP_WearChange, sizeof(WearChange_Struct));
p_wear_change_struct = (WearChange_Struct *) outapp->pBuffer;
p_wear_change_struct->spawn_id = GetID();
p_wear_change_struct->material = 0;
}
item->item_id = item2->ID;
item->charges = loot_drop.item_charges;
item->aug_1 = aug1;
item->aug_2 = aug2;
item->aug_3 = aug3;
item->aug_4 = aug4;
item->aug_5 = aug5;
item->aug_6 = aug6;
item->attuned = 0;
item->trivial_min_level = loot_drop.trivial_min_level;
item->trivial_max_level = loot_drop.trivial_max_level;
item->equip_slot = EQ::invslot::SLOT_INVALID;
if (loot_drop.equip_item > 0) {
uint8 eslot = 0xFF;
char newid[20];
const EQ::ItemData* compitem = nullptr;
@@ -403,9 +464,9 @@ void NPC::AddLootDrop(const EQ::ItemData *item2, ItemList* itemlist, int16 charg
//if we found an open slot it goes in...
if(eslot != 0xFF) {
if(wearchange) {
wc->wear_slot_id = eslot;
wc->material = emat;
if(wear_change) {
p_wear_change_struct->wear_slot_id = eslot;
p_wear_change_struct->material = emat;
}
}
@@ -415,24 +476,25 @@ void NPC::AddLootDrop(const EQ::ItemData *item2, ItemList* itemlist, int16 charg
}
}
if(itemlist != nullptr)
if (itemlist != nullptr) {
itemlist->push_back(item);
else
safe_delete(item);
}
else safe_delete(item);
if(wearchange && outapp) {
if (wear_change && outapp) {
entity_list.QueueClients(this, outapp);
safe_delete(outapp);
}
UpdateEquipmentLight();
if (UpdateActiveLight())
if (UpdateActiveLight()) {
SendAppearancePacket(AT_Light, GetActiveLightType());
}
}
void NPC::AddItem(const EQ::ItemData* item, uint16 charges, bool equipitem) {
//slot isnt needed, its determined from the item.
AddLootDrop(item, &itemlist, charges, 1, 255, equipitem, equipitem);
AddLootDrop(item, &itemlist, LootDropEntries_Struct{ .equip_item = equipitem }, true);
}
void NPC::AddItem(uint32 itemid, uint16 charges, bool equipitem, uint32 aug1, uint32 aug2, uint32 aug3, uint32 aug4, uint32 aug5, uint32 aug6) {
@@ -440,7 +502,7 @@ void NPC::AddItem(uint32 itemid, uint16 charges, bool equipitem, uint32 aug1, ui
const EQ::ItemData * i = database.GetItem(itemid);
if(i == nullptr)
return;
AddLootDrop(i, &itemlist, charges, 1, 255, equipitem, equipitem, aug1, aug2, aug3, aug4, aug5, aug6);
AddLootDrop(i, &itemlist, LootDropEntries_Struct{ .equip_item = equipitem }, true, aug1, aug2, aug3, aug4, aug5, aug6);
}
void NPC::AddLootTable() {
+22 -13
View File
@@ -576,25 +576,33 @@ void NPC::RemoveItem(uint32 item_id, uint16 quantity, uint16 slot) {
}
}
void NPC::CheckMinMaxLevel(Mob *them)
void NPC::CheckTrivialMinMaxLevelDrop(Mob *killer)
{
if(them == nullptr || !them->IsClient())
if (killer == nullptr || !killer->IsClient()) {
return;
}
uint16 themlevel = them->GetLevel();
uint8 material;
uint16 killer_level = killer->GetLevel();
uint8 material;
auto cur = itemlist.begin();
while(cur != itemlist.end())
{
if(!(*cur))
while (cur != itemlist.end()) {
if (!(*cur)) {
return;
}
if(themlevel < (*cur)->min_level || themlevel > (*cur)->max_level)
{
uint16 trivial_min_level = (*cur)->trivial_min_level;
uint16 trivial_max_level = (*cur)->trivial_max_level;
bool fits_trivial_criteria = (
(trivial_min_level > 0 && killer_level < trivial_min_level) ||
(trivial_max_level > 0 && killer_level > trivial_max_level)
);
if (fits_trivial_criteria) {
material = EQ::InventoryProfile::CalcMaterialFromSlot((*cur)->equip_slot);
if (material != EQ::textures::materialInvalid)
if (material != EQ::textures::materialInvalid) {
SendWearChange(material);
}
cur = itemlist.erase(cur);
continue;
@@ -603,8 +611,9 @@ void NPC::CheckMinMaxLevel(Mob *them)
}
UpdateEquipmentLight();
if (UpdateActiveLight())
if (UpdateActiveLight()) {
SendAppearancePacket(AT_Light, GetActiveLightType());
}
}
void NPC::ClearItemList() {
@@ -647,8 +656,8 @@ void NPC::QueryLoot(Client* to)
item_count,
linker.GenerateLink().c_str(),
(*cur)->item_id,
(*cur)->min_level,
(*cur)->max_level
(*cur)->trivial_min_level,
(*cur)->trivial_max_level
);
}
+19 -3
View File
@@ -25,6 +25,7 @@
#include "zonedb.h"
#include "zone_store.h"
#include "zonedump.h"
#include "../common/loottable.h"
#include <deque>
#include <list>
@@ -193,7 +194,7 @@ public:
void CheckGlobalLootTables();
void DescribeAggro(Client *towho, Mob *mob, bool verbose);
void RemoveItem(uint32 item_id, uint16 quantity = 0, uint16 slot = 0);
void CheckMinMaxLevel(Mob *them);
void CheckTrivialMinMaxLevelDrop(Mob *killer);
void ClearItemList();
ServerLootItem_Struct* GetItem(int slot_id);
void AddCash(uint16 in_copper, uint16 in_silver, uint16 in_gold, uint16 in_platinum);
@@ -291,7 +292,22 @@ public:
void PickPocket(Client* thief);
void Disarm(Client* client, int chance);
void StartSwarmTimer(uint32 duration) { swarm_timer.Start(duration); }
void AddLootDrop(const EQ::ItemData*dbitem, ItemList* itemlistconst, int16 charges, uint8 minlevel, uint8 maxlevel, bool equipit, bool wearchange = false, uint32 aug1 = 0, uint32 aug2 = 0, uint32 aug3 = 0, uint32 aug4 = 0, uint32 aug5 = 0, uint32 aug6 = 0);
void AddLootDrop(
const EQ::ItemData *item2,
ItemList *itemlist,
LootDropEntries_Struct loot_drop,
bool wear_change = false,
uint32 aug1 = 0,
uint32 aug2 = 0,
uint32 aug3 = 0,
uint32 aug4 = 0,
uint32 aug5 = 0,
uint32 aug6 = 0
);
bool MeetsLevelRequirements(LootDropEntries_Struct loot_drop);
virtual void DoClassAttacks(Mob *target);
void CheckSignal();
inline bool IsNotTargetableWithHotkey() const { return no_target_hotkey; }
@@ -620,12 +636,12 @@ protected:
bool ignore_despawn; //NPCs with this set to 1 will ignore the despawn value in spawngroup
private:
uint32 loottable_id;
bool skip_global_loot;
bool skip_auto_scale;
bool p_depop;
};
#endif
+2 -2
View File
@@ -394,7 +394,7 @@ void Mob::MakePoweredPet(uint16 spell_id, const char* pettype, int16 petpower,
for (int i = EQ::invslot::EQUIPMENT_BEGIN; i <= EQ::invslot::EQUIPMENT_END; i++)
if (petinv[i]) {
item = database.GetItem(petinv[i]);
npc->AddLootDrop(item, &npc->itemlist, 0, 1, 127, true, true);
npc->AddLootDrop(item, &npc->itemlist, LootDropEntries_Struct{ .equip_item = true }, true);
}
}
@@ -625,7 +625,7 @@ void NPC::SetPetState(SpellBuff_Struct *pet_buffs, uint32 *items) {
_CLIENTPET(this) && GetPetType() <= petOther);
if (!noDrop || petCanHaveNoDrop) {
AddLootDrop(item2, &itemlist, 0, 1, 255, true, true);
AddLootDrop(item2, &itemlist, LootDropEntries_Struct{.equip_item = true }, true);
}
}
}
+12 -4
View File
@@ -899,8 +899,12 @@ void Client::FinishTrade(Mob* tradingWith, bool finalizer, void* event_entry, st
if (baginst) {
const EQ::ItemData* bagitem = baginst->GetItem();
if (bagitem && (GetGM() || (bagitem->NoDrop != 0 && baginst->IsAttuned() == false))) {
tradingWith->CastToNPC()->AddLootDrop(bagitem, &tradingWith->CastToNPC()->itemlist,
baginst->GetCharges(), 1, 127, true, true);
tradingWith->CastToNPC()->AddLootDrop(
bagitem,
&tradingWith->CastToNPC()->itemlist,
LootDropEntries_Struct{.item_charges = static_cast<int8>(baginst->GetCharges()), .equip_item = true },
true
);
}
else if (RuleB(NPC, ReturnNonQuestNoDropItems)) {
PushItemOnCursor(*baginst, true);
@@ -909,8 +913,12 @@ void Client::FinishTrade(Mob* tradingWith, bool finalizer, void* event_entry, st
}
}
tradingWith->CastToNPC()->AddLootDrop(item, &tradingWith->CastToNPC()->itemlist,
inst->GetCharges(), 1, 127, true, true);
tradingWith->CastToNPC()->AddLootDrop(
item,
&tradingWith->CastToNPC()->itemlist,
LootDropEntries_Struct{.item_charges = static_cast<int8>(inst->GetCharges()), .equip_item = true },
true
);
}
// Return NO DROP and Attuned items being handed into a non-quest NPC if the rule is true
else if (RuleB(NPC, ReturnNonQuestNoDropItems)) {
+1 -1
View File
@@ -453,7 +453,7 @@ public:
bool GetPoweredPetEntry(const char *pet_type, int16 petpower, PetRecord *into);
bool GetBasePetItems(int32 equipmentset, uint32 *items);
void AddLootTableToNPC(NPC* npc, uint32 loottable_id, ItemList* itemlist, uint32* copper, uint32* silver, uint32* gold, uint32* plat);
void AddLootDropToNPC(NPC* npc, uint32 lootdrop_id, ItemList* itemlist, uint8 droplimit, uint8 mindrop);
void AddLootDropToNPC(NPC* npc, uint32 lootdrop_id, ItemList* item_list, uint8 droplimit, uint8 mindrop);
uint32 GetMaxNPCSpellsID();
uint32 GetMaxNPCSpellsEffectsID();
bool GetAuraEntry(uint16 spell_id, AuraRecord &record);