Here is the code that handles the loot drops being added. Note the part highlighted in green where it picks a random entry from the total number of entries to start at and resets it back to 0 if it rolls past the end of the list.
loottables.cpp
Code:
// Called by AddLootTableToNPC
// maxdrops = size of the array npcd
void ZoneDatabase::AddLootDropToNPC(NPC* npc,int32 lootdrop_id, ItemList* itemlist) {
const LootDrop_Struct* lds = GetLootDrop(lootdrop_id);
if (!lds) {
// LogFile->write(EQEMuLog::Error, "Database Or Memory error GetLootDrop(%i) == 0, npc:%s", lootdrop_id, npc->GetName());
return;
}
if(lds->NumEntries == 0) //nothing possible to add
return;
// This is Wiz's updated Pool Looting functionality. Eventually, the database format should be moved over to use this
// or implemented to support both methods. (A unique identifier in lootable_entries indicates to roll for a pool item
// in another table.
#ifdef POOLLOOTING
printf("POOL!\n");
int32 chancepool = 0;
int32 loot_items[100];
int8 equipitem[100];
int32 itemchance[100];
int16 itemcharges[100];
int8 i = 0;
for (int m=0;m < 100;m++) {
loot_items[m]=0;
itemchance[m]=0;
itemcharges[m]=0;
equipitem[m]=0;
}
for (int k=0; k<lds->NumEntries; k++)
{
loot_items[i] = lds->Entries[k].item_id;
int chance = lds->Entries[k].chance;
itemcharges[i] = lds->Entries[k].item_charges;
equipitem[i] = lds->Entries[k].equip_item;
/*
im not sure what this is trying to accomplish...
LinkedListIterator<ServerLootItem_Struct*> iterator(*itemlist);
iterator.Reset();
while(iterator.MoreElements())
{
if (iterator.GetData()->item_id == loot_items[i])
{
chance /= 5;
}
iterator.Advance();
}*/
chance += chancepool;
chancepool += lds->Entries[k].chance;
itemchance[i] = chance;
i++;
}
int32 res;
i = 0;
if (chancepool!=0) { //avoid divide by zero if some mobs have 0 for chancepool
res = MakeRandomInt(0, chancepool-1);
}
else {
res = 0;
}
while (loot_items[i] != 0) {
if (res <= itemchance[i])
break;
else
i++;
}
const Item_Struct* dbitem = GetItem(items[i]);
npc->AddLootDrop(dbitem, itemlist, lds->Entries[k].item_charges, lds->Entries[k].equip_item, false);
#else
//non-pool based looting
int32 r;
int32 totalchance = 0;
for (r = 0; r < lds->NumEntries; r++) {
totalchance += lds->Entries[r].chance;
}
uint32 thischance = 0;
unsigned short k;
bool found = false;
k = MakeRandomInt(0, lds->NumEntries-1);
while(!found) {
if (k > (lds->NumEntries - 1)) {
k = 0;
}
thischance = lds->Entries[k].chance;
unsigned int drop_chance = MakeRandomInt(0, totalchance-1);
#if EQDEBUG>=11
LogFile->write(EQEMuLog::Debug, "Drop chance for npc: %s, total chance:%i this chance:%i, drop roll:%i", npc->GetName(), totalchance, thischance, drop_chance);
#endif
if ( totalchance == 0
|| thischance == 100
|| thischance == totalchance // only droppable item in loot table
|| drop_chance < thischance //can never be true if thischance is 0
) {
found = true;
int32 itemid = lds->Entries[k].item_id;
const Item_Struct* dbitem = GetItem(itemid);
npc->AddLootDrop(dbitem, itemlist, lds->Entries[k].item_charges, lds->Entries[k].equip_item, false);
break;
//continue;
} //end if it will drop
k++; //Cycle to the next droppable item in the list
} //end loop
#endif
}
This should prevent it from favoring items at the top of the list. It has been a while since I really looked into how these are all handled (not since that old thread Zothen referred to), but I think the way I described things so far in this thread should be accurate.
And yes, Zothen, your example of having 3 drops from that setup is correct. With those settings, you could have either 2 or 3 drops, depending on if the 50% probability one wins the roll or not.