Merge pull request #364 from EQEmu/loot

Loot Upgrade
This commit is contained in:
Alex 2015-02-05 00:06:38 -08:00
commit 54c3f9ab94
7 changed files with 109 additions and 96 deletions

View File

@ -22,7 +22,7 @@
#define LOGIN_VERSION "0.8.0" #define LOGIN_VERSION "0.8.0"
#define EQEMU_PROTOCOL_VERSION "0.3.10" #define EQEMU_PROTOCOL_VERSION "0.3.10"
#define CURRENT_VERSION "1.0.0" #define CURRENT_VERSION "1.1.3"
/* /*
Everytime a Database SQL is added to Github, Everytime a Database SQL is added to Github,
@ -30,7 +30,7 @@
Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt
*/ */
#define CURRENT_BINARY_DATABASE_VERSION 9075 #define CURRENT_BINARY_DATABASE_VERSION 9076
#define COMPILE_DATE __DATE__ #define COMPILE_DATE __DATE__
#define COMPILE_TIME __TIME__ #define COMPILE_TIME __TIME__
#ifndef WIN32 #ifndef WIN32

View File

@ -329,6 +329,7 @@
9073|2015_01_31_character_item_recast.sql|SHOW TABLES LIKE 'character_item_recast'|empty| 9073|2015_01_31_character_item_recast.sql|SHOW TABLES LIKE 'character_item_recast'|empty|
9074|2015_02_01_logsys_packet_logs.sql|SELECT * FROM `logsys_categories` WHERE `log_category_description` LIKE 'Packet: Server -> Client'|empty| 9074|2015_02_01_logsys_packet_logs.sql|SELECT * FROM `logsys_categories` WHERE `log_category_description` LIKE 'Packet: Server -> Client'|empty|
9075|2015_02_02_logsys_packet_logs_with_dump.sql|SELECT * FROM `logsys_categories` WHERE `log_category_description` LIKE 'Packet: Server -> Client With Dump'|empty| 9075|2015_02_02_logsys_packet_logs_with_dump.sql|SELECT * FROM `logsys_categories` WHERE `log_category_description` LIKE 'Packet: Server -> Client With Dump'|empty|
9076|2014_10_30_special_abilities_null.sql|SHOW COLUMNS FROM `loottable` WHERE Field = 'avgcoin'|contains|smallint
# Upgrade conditions: # Upgrade conditions:
# This won't be needed after this system is implemented, but it is used database that are not # This won't be needed after this system is implemented, but it is used database that are not

View File

@ -0,0 +1,2 @@
ALTER TABLE `loottable` CHANGE COLUMN `avgcoin` `avgcoin` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `maxcash`;
UPDATE `loottable` SET avgcoin = 0;

View File

@ -19,6 +19,7 @@
#include "../common/global_define.h" #include "../common/global_define.h"
#include "../common/loottable.h" #include "../common/loottable.h"
#include "../common/misc_functions.h" #include "../common/misc_functions.h"
#include "../common/data_verification.h"
#include "client.h" #include "client.h"
#include "entity.h" #include "entity.h"
@ -35,7 +36,7 @@
// Queries the loottable: adds item & coin to the npc // 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) { 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; *copper = 0;
*silver = 0; *silver = 0;
*gold = 0; *gold = 0;
@ -45,44 +46,39 @@ void ZoneDatabase::AddLootTableToNPC(NPC* npc,uint32 loottable_id, ItemList* ite
if (!lts) if (!lts)
return; return;
// do coin uint32 min_cash = lts->mincash;
if (lts->mincash > lts->maxcash) { uint32 max_cash = lts->maxcash;
std::cerr << "Error in loottable #" << loottable_id << ": mincash > maxcash" << std::endl; if(min_cash > max_cash) {
uint32 t = min_cash;
min_cash = max_cash;
max_cash = t;
} }
else if (lts->maxcash != 0) {
uint32 cash = 0; uint32 cash = 0;
if (lts->mincash == lts->maxcash) if(max_cash > 0 && lts->avgcoin > 0 && EQEmu::ValueWithin(lts->avgcoin, min_cash, max_cash)) {
cash = lts->mincash; float upper_chance = (float)(lts->avgcoin - min_cash) / (float)(max_cash - min_cash);
else float avg_cash_roll = (float)zone->random.Real(0.0, 1.0);
cash = zone->random.Int(lts->mincash, lts->maxcash);
if (cash != 0) { if(avg_cash_roll < upper_chance) {
if (lts->avgcoin != 0) { cash = zone->random.Int(lts->avgcoin, max_cash);
//this is some crazy ass stuff... and makes very little sense... dont use it, k? } else {
uint32 mincoin = (uint32) (lts->avgcoin * 0.75 + 1); cash = zone->random.Int(min_cash, lts->avgcoin);
uint32 maxcoin = (uint32) (lts->avgcoin * 1.25 + 1);
*copper = zone->random.Int(mincoin, maxcoin);
*silver = zone->random.Int(mincoin, maxcoin);
*gold = zone->random.Int(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) { } else {
cash = 0; cash = zone->random.Int(min_cash, max_cash);
} }
if(cash != 0) {
*plat = cash / 1000; *plat = cash / 1000;
cash -= *plat * 1000; cash -= *plat * 1000;
uint32 gold2 = cash / 100;
cash -= gold2 * 100; *gold = cash / 100;
uint32 silver2 = cash / 10; cash -= *gold * 100;
cash -= silver2 * 10;
*gold += gold2; *silver = cash / 10;
*silver += silver2; cash -= *silver * 10;
*copper += cash;
} *copper = cash;
} }
// Do items // Do items
@ -97,11 +93,11 @@ void ZoneDatabase::AddLootTableToNPC(NPC* npc,uint32 loottable_id, ItemList* ite
float drop_chance = 0.0f; float drop_chance = 0.0f;
if(ltchance > 0.0 && ltchance < 100.0) { if(ltchance > 0.0 && ltchance < 100.0) {
drop_chance = zone->random.Real(0.0, 100.0); drop_chance = (float)zone->random.Real(0.0, 100.0);
} }
if (ltchance != 0.0 && (ltchance == 100.0 || drop_chance < ltchance)) { if (ltchance != 0.0 && (ltchance == 100.0 || drop_chance <= ltchance)) {
AddLootDropToNPC(npc,lts->Entries[i].lootdrop_id, itemlist, droplimit, mindrop); AddLootDropToNPC(npc, lts->Entries[i].lootdrop_id, itemlist, droplimit, mindrop);
} }
} }
} }
@ -114,63 +110,76 @@ void ZoneDatabase::AddLootDropToNPC(NPC* npc,uint32 lootdrop_id, ItemList* iteml
if (!lds) { if (!lds) {
return; return;
} }
if(lds->NumEntries == 0) //nothing possible to add
if(lds->NumEntries == 0)
return; return;
// Too long a list needs to be limited. if(droplimit == 0 && mindrop == 0) {
if(lds->NumEntries > 99 && droplimit < 1) for(uint32 i = 0; i < lds->NumEntries; ++i) {
droplimit = lds->NumEntries/100; int charges = lds->Entries[i].multiplier;
for(int j = 0; j < charges; ++j) {
uint8 limit = 0; if(zone->random.Real(0.0, 100.0) <= lds->Entries[i].chance) {
// Start at a random point in itemlist. const Item_Struct* dbitem = GetItem(lds->Entries[i].item_id);
uint32 item = zone->random.Int(0, lds->NumEntries-1); npc->AddLootDrop(dbitem, itemlist, lds->Entries[i].item_charges, lds->Entries[i].minlevel,
// Main loop. lds->Entries[i].maxlevel, lds->Entries[i].equip_item > 0 ? true : false, false);
for (uint32 i=0; i<lds->NumEntries;)
{
//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 = zone->random.Real(0.0, 100.0);
#if EQDEBUG>=11
Log.Out(Logs::General, Logs::None, "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++;
} }
} }
// Items with multipliers only count as 1 towards the limit. }
if(pickedcharges > 0) return;
limit++; }
// If true, limit reached. if(lds->NumEntries > 100 && droplimit == 0) {
if(limit >= droplimit && droplimit > 0) droplimit = 10;
}
if(droplimit < mindrop) {
droplimit = mindrop;
}
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 = zone->random.Int(mindrop, droplimit);
for(int i = 0; i < item_count; ++i) {
float roll = (float)zone->random.Real(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)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);
}
}
j = lds->NumEntries;
break; break;
}
item++; else {
i++; roll -= lds->Entries[j].chance;
}
// We didn't reach our minimium, run loop again.
if(i == lds->NumEntries){
if(limit < mindrop){
i = 0;
} }
} }
} // We either ran out of items or reached our limit. } // We either ran out of items or reached our limit.
@ -215,6 +224,7 @@ void NPC::AddLootDrop(const Item_Struct *item2, ItemList* itemlist, int16 charge
item->attuned = 0; item->attuned = 0;
item->min_level = minlevel; item->min_level = minlevel;
item->max_level = maxlevel; item->max_level = maxlevel;
if (equipit) { if (equipit) {
uint8 eslot = 0xFF; uint8 eslot = 0xFF;
char newid[20]; char newid[20];

View File

@ -622,7 +622,7 @@ public:
bool PassCastRestriction(bool UseCastRestriction = true, int16 value = 0, bool IsDamage = true); bool PassCastRestriction(bool UseCastRestriction = true, int16 value = 0, bool IsDamage = true);
bool ImprovedTaunt(); bool ImprovedTaunt();
bool TryRootFadeByDamage(int buffslot, Mob* attacker); 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); void CalcSpellPowerDistanceMod(uint16 spell_id, float range, Mob* caster = nullptr);
inline int16 GetSpellPowerDistanceMod() const { return SpellPowerDistanceMod; }; inline int16 GetSpellPowerDistanceMod() const { return SpellPowerDistanceMod; };
inline void SetSpellPowerDistanceMod(int16 value) { SpellPowerDistanceMod = value; }; inline void SetSpellPowerDistanceMod(int16 value) { SpellPowerDistanceMod = value; };

View File

@ -253,7 +253,7 @@ public:
uint32 GetMaxDMG() const {return max_dmg;} uint32 GetMaxDMG() const {return max_dmg;}
uint32 GetMinDMG() const {return min_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;} float GetAttackSpeed() const {return attack_speed;}
uint8 GetAttackDelay() const {return attack_delay;} uint8 GetAttackDelay() const {return attack_delay;}
bool IsAnimal() const { return(bodytype == BT_Animal); } bool IsAnimal() const { return(bodytype == BT_Animal); }

View File

@ -2076,7 +2076,7 @@ XS(XS_NPC_GetSlowMitigation)
Perl_croak(aTHX_ "Usage: NPC::GetSlowMitigation(THIS)"); Perl_croak(aTHX_ "Usage: NPC::GetSlowMitigation(THIS)");
{ {
NPC * THIS; NPC * THIS;
int16 RETVAL; float RETVAL;
dXSTARG; dXSTARG;
if (sv_derived_from(ST(0), "NPC")) { if (sv_derived_from(ST(0), "NPC")) {
@ -2089,7 +2089,7 @@ XS(XS_NPC_GetSlowMitigation)
Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
RETVAL = THIS->GetSlowMitigation(); RETVAL = THIS->GetSlowMitigation();
XSprePUSH; PUSHn((UV)RETVAL); XSprePUSH; PUSHn((double)RETVAL);
} }
XSRETURN(1); XSRETURN(1);
} }