diff --git a/zone/loottables.cpp b/zone/loottables.cpp index b2ebec5ff..1a3abe513 100644 --- a/zone/loottables.cpp +++ b/zone/loottables.cpp @@ -23,13 +23,11 @@ #include "masterentity.h" #include "zonedb.h" #include "../common/misc_functions.h" -#ifdef _WINDOWS -#define snprintf _snprintf -#endif +#include "../common/data_verification.h" // Queries the loottable: adds item & coin to the npc void ZoneDatabase::AddLootTableToNPC(NPC* npc,uint32 loottable_id, ItemList* itemlist, uint32* copper, uint32* silver, uint32* gold, uint32* plat) { - const LootTable_Struct* lts = 0; + const LootTable_Struct* lts = nullptr; *copper = 0; *silver = 0; *gold = 0; @@ -39,44 +37,37 @@ void ZoneDatabase::AddLootTableToNPC(NPC* npc,uint32 loottable_id, ItemList* ite if (!lts) return; - // do coin - if (lts->mincash > lts->maxcash) { - std::cerr << "Error in loottable #" << loottable_id << ": mincash > maxcash" << std::endl; + uint32 min_cash = lts->mincash; + uint32 max_cash = lts->maxcash; + if(min_cash > max_cash) { + uint32 t = min_cash; + min_cash = max_cash; + max_cash = t; } - else if (lts->maxcash != 0) { - uint32 cash = 0; - if (lts->mincash == lts->maxcash) - cash = lts->mincash; - else - cash = MakeRandomInt(lts->mincash, lts->maxcash); - if (cash != 0) { - if (lts->avgcoin != 0) { - //this is some crazy ass stuff... and makes very little sense... dont use it, k? - uint32 mincoin = (uint32) (lts->avgcoin * 0.75 + 1); - uint32 maxcoin = (uint32) (lts->avgcoin * 1.25 + 1); - *copper = MakeRandomInt(mincoin, maxcoin); - *silver = MakeRandomInt(mincoin, maxcoin); - *gold = MakeRandomInt(mincoin, maxcoin); - if(*copper > cash) { *copper = cash; } - cash -= *copper; - if(*silver>(cash/10)) { *silver = (cash/10); } - cash -= *silver*10; - if(*gold > (cash/100)) { *gold = (cash/100); } - cash -= *gold*100; - } - if (cash < 0) { - cash = 0; - } - *plat = cash / 1000; - cash -= *plat * 1000; - uint32 gold2 = cash / 100; - cash -= gold2 * 100; - uint32 silver2 = cash / 10; - cash -= silver2 * 10; - *gold += gold2; - *silver += silver2; - *copper += cash; + + uint32 cash = 0; + if(max_cash > 0 && EQEmu::ValueWithin(lts->avgcoin, min_cash, max_cash)) { + int avg_cash_roll = MakeRandomInt(1, 100); + if(avg_cash_roll > 50) { + cash = MakeRandomInt(lts->avgcoin, max_cash); + } else { + cash = MakeRandomInt(min_cash, lts->avgcoin); } + } else { + cash = MakeRandomInt(min_cash, max_cash); + } + + if(cash != 0) { + *plat = cash / 1000; + cash -= *plat * 1000; + + *gold = cash / 100; + cash -= *gold * 100; + + *silver = cash / 10; + cash -= *silver * 10; + + *copper = cash; } // Do items @@ -91,11 +82,11 @@ void ZoneDatabase::AddLootTableToNPC(NPC* npc,uint32 loottable_id, ItemList* ite float drop_chance = 0.0f; if(ltchance > 0.0 && ltchance < 100.0) { - drop_chance = MakeRandomFloat(0.0, 100.0); + drop_chance = (float)MakeRandomFloat(0.0, 100.0); } - if (ltchance != 0.0 && (ltchance == 100.0 || drop_chance < ltchance)) { - AddLootDropToNPC(npc,lts->Entries[i].lootdrop_id, itemlist, droplimit, mindrop); + if (ltchance != 0.0 && (ltchance == 100.0 || drop_chance <= ltchance)) { + AddLootDropToNPC(npc, lts->Entries[i].lootdrop_id, itemlist, droplimit, mindrop); } } } @@ -108,66 +99,79 @@ void ZoneDatabase::AddLootDropToNPC(NPC* npc,uint32 lootdrop_id, ItemList* iteml if (!lds) { return; } - if(lds->NumEntries == 0) //nothing possible to add + + if(lds->NumEntries == 0) return; - // Too long a list needs to be limited. - if(lds->NumEntries > 99 && droplimit < 1) - droplimit = lds->NumEntries/100; - - uint8 limit = 0; - // Start at a random point in itemlist. - uint32 item = MakeRandomInt(0, lds->NumEntries-1); - // Main loop. - for (uint32 i=0; iNumEntries;) - { - //Force the itemlist back to beginning. - if (item > (lds->NumEntries-1)) - item = 0; - - uint8 charges = lds->Entries[item].multiplier; - uint8 pickedcharges = 0; - // Loop to check multipliers. - for (uint32 x=1; x<=charges; x++) - { - // Actual roll. - float thischance = 0.0; - thischance = lds->Entries[item].chance; - - float drop_chance = 0.0; - if(thischance != 100.0) - drop_chance = MakeRandomFloat(0.0, 100.0); - -#if EQDEBUG>=11 - LogFile->write(EQEMuLog::Debug, "Drop chance for npc: %s, this chance:%f, drop roll:%f", npc->GetName(), thischance, drop_chance); -#endif - if (thischance == 100.0 || drop_chance < thischance) - { - uint32 itemid = lds->Entries[item].item_id; - - const Item_Struct* dbitem = GetItem(itemid); - npc->AddLootDrop(dbitem, itemlist, lds->Entries[item].item_charges, lds->Entries[item].minlevel, lds->Entries[item].maxlevel, lds->Entries[item].equip_item, false); - pickedcharges++; + 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(MakeRandomFloat(0.0, 100.0) <= lds->Entries[i].chance) { + const Item_Struct* 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); + } } } - // Items with multipliers only count as 1 towards the limit. - if(pickedcharges > 0) - limit++; + return; + } - // If true, limit reached. - if(limit >= droplimit && droplimit > 0) - break; + if(lds->NumEntries > 100 && droplimit == 0) { + droplimit = 10; + } - item++; - i++; + if(droplimit < mindrop) { + droplimit = mindrop; + } - // We didn't reach our minimium, run loop again. - if(i == lds->NumEntries){ - if(limit < mindrop){ - i = 0; + float roll_t = 0.0f; + bool active_item_list = false; + for(uint32 i = 0; i < lds->NumEntries; ++i) { + const Item_Struct* db_item = GetItem(lds->Entries[i].item_id); + if(db_item) { + roll_t += lds->Entries[i].chance; + active_item_list = true; + } + } + + roll_t = EQEmu::ClampLower(roll_t, 100.0f); + + if(!active_item_list) { + return; + } + + mindrop = EQEmu::ClampLower(mindrop, (uint8)1); + int item_count = MakeRandomInt(mindrop, droplimit); + for(int i = 0; i < item_count; ++i) { + float roll = (float)MakeRandomFloat(0.0, roll_t); + for(uint32 j = 0; j < lds->NumEntries; ++j) { + const Item_Struct* 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); + + int charges = (int)lds->Entries[i].multiplier; + charges = EQEmu::ClampLower(charges, 1); + + for(int k = 1; k < charges; ++k) { + float c_roll = (float)MakeRandomFloat(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); + } + } + + j = lds->NumEntries; + break; + } + else { + roll -= lds->Entries[j].chance; + } } } - } // We either ran out of items or reached our limit. + } } //if itemlist is null, just send wear changes @@ -194,7 +198,7 @@ void NPC::AddLootDrop(const Item_Struct *item2, ItemList* itemlist, int16 charge } item->item_id = item2->ID; - item->charges = charges; + item->charges = (uint8)charges; item->aug1 = 0; item->aug2 = 0; item->aug3 = 0; diff --git a/zone/mob.h b/zone/mob.h index 2617e98e2..d76fe5a8c 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -616,7 +616,7 @@ public: bool PassCastRestriction(bool UseCastRestriction = true, int16 value = 0, bool IsDamage = true); bool ImprovedTaunt(); bool TryRootFadeByDamage(int buffslot, Mob* attacker); - int16 GetSlowMitigation() const {return slow_mitigation;} + float GetSlowMitigation() const { return slow_mitigation; } void CalcSpellPowerDistanceMod(uint16 spell_id, float range, Mob* caster = nullptr); inline int16 GetSpellPowerDistanceMod() const { return SpellPowerDistanceMod; }; inline void SetSpellPowerDistanceMod(int16 value) { SpellPowerDistanceMod = value; }; diff --git a/zone/npc.h b/zone/npc.h index f1e4c790c..945bad250 100644 --- a/zone/npc.h +++ b/zone/npc.h @@ -257,7 +257,7 @@ public: uint32 GetMaxDMG() const {return max_dmg;} uint32 GetMinDMG() const {return min_dmg;} - int16 GetSlowMitigation() const {return slow_mitigation;} + float GetSlowMitigation() const { return slow_mitigation; } float GetAttackSpeed() const {return attack_speed;} bool IsAnimal() const { return(bodytype == BT_Animal); } uint16 GetPetSpellID() const {return pet_spell_id;} diff --git a/zone/perl_npc.cpp b/zone/perl_npc.cpp index c1f8d8828..fe71b1f0b 100644 --- a/zone/perl_npc.cpp +++ b/zone/perl_npc.cpp @@ -2075,7 +2075,7 @@ XS(XS_NPC_GetSlowMitigation) Perl_croak(aTHX_ "Usage: NPC::GetSlowMitigation(THIS)"); { NPC * THIS; - int16 RETVAL; + float RETVAL; dXSTARG; if (sv_derived_from(ST(0), "NPC")) { @@ -2088,7 +2088,7 @@ XS(XS_NPC_GetSlowMitigation) Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); RETVAL = THIS->GetSlowMitigation(); - XSprePUSH; PUSHn((UV)RETVAL); + XSprePUSH; PUSHn((double)RETVAL); } XSRETURN(1); }