rendered paste bodyIndex: sql/mangos.sql===================================================================--- sql/mangos.sql (revision 5478)+++ sql/mangos.sql (working copy)@@ -659,6 +659,8 @@ `civilian` tinyint(3) unsigned NOT NULL default '0', `flag1` int(11) unsigned default '0', `lootid` int(10) unsigned NOT NULL default '0',+ `lootMinCount` tinyint(3) unsigned NOT NULL default '0',+ `lootMaxCount` tinyint(3) unsigned NOT NULL default '0', `pickpocketloot` int(10) unsigned NOT NULL default '0', `skinloot` int(10) unsigned NOT NULL default '0', `resistance1` int(10) unsigned NOT NULL default '0',Index: sql/updates/5XXX_mangos_creature_template.sql===================================================================--- sql/updates/5478_mangos_creature_template.sql (revision 0)+++ sql/updates/5478_mangos_creature_template.sql (revision 0)@@ -0,0 +1,6 @@+ALTER TABLE creature_template ADD COLUMN lootMinCount tinyint(3) unsigned NOT NULL default '0' AFTER `lootid`;+ALTER TABLE creature_template ADD COLUMN lootMaxCount tinyint(3) unsigned NOT NULL default '0' AFTER `lootMinCount`;++INSERT INTO `mangos_string` (`entry`, `content_default`) values('580','Loot count range for creature entry #%u has been modified to (%u,%u)');+INSERT INTO `mangos_string` (`entry`, `content_default`) values('581','Invalid value for loot count range');+INSERT INTO `command` (`name`, `security`, `help`) values('setlootrange','3','Syntax: .setlootrange #max_count [#min_count]\r\n Set the lootMaxCount and lootMinCount in creature_template for selected creature. min_count default is 0.');Index: src/game/Chat.cpp===================================================================--- src/game/Chat.cpp (revision 5478)+++ src/game/Chat.cpp (working copy)@@ -392,7 +392,7 @@ { "setvalue", SEC_ADMINISTRATOR, &ChatHandler::HandleSetValue, "", NULL }, { "getvalue", SEC_ADMINISTRATOR, &ChatHandler::HandleGetValue, "", NULL }, { "Mod32Value", SEC_ADMINISTRATOR, &ChatHandler::HandleMod32Value, "", NULL },-+ { "setlootrange",SEC_ADMINISTRATOR, &ChatHandler::HandleSetLootRangeCommand, "", NULL }, { NULL, 0, NULL, "", NULL } }; Index: src/game/Chat.h===================================================================--- src/game/Chat.h (revision 5478)+++ src/game/Chat.h (working copy)@@ -339,6 +339,7 @@ bool HandleRemoveQuest(const char * args); bool HandleSaveAllCommand(const char* args); bool HandleGetItemState(const char * args);+ bool HandleSetLootRangeCommand(const char* arg); Player* getSelectedPlayer(); Creature* getSelectedCreature();Index: src/game/Creature.h===================================================================--- src/game/Creature.h (revision 5478)+++ src/game/Creature.h (working copy)@@ -162,7 +162,7 @@ uint32 faction_H; uint32 npcflag; float speed;- float scale; + float scale; uint32 rank; float mindmg; float maxdmg;@@ -184,6 +184,8 @@ bool civilian; uint32 flag1; uint32 lootid;+ uint32 lootMinCount;+ uint32 lootMaxCount; uint32 pickpocketLootId; uint32 SkinLootId; uint32 resistance1;Index: src/game/Language.h===================================================================--- src/game/Language.h (revision 5478)+++ src/game/Language.h (working copy)@@ -481,6 +481,8 @@ LANG_INVISIBLE_INVISIBLE = 577, LANG_INVISIBLE_VISIBLE = 578, LANG_SELECTED_TARGET_NOT_HAVE_VICTIM = 579,+ LANG_CREATURE_LOOT_RANGE_MODIFIED = 580,+ LANG_CREATURE_LOOT_RANGE_INVALID = 581, // Battleground LANG_BG_A_WINS = 600,Index: src/game/Level3.cpp===================================================================--- src/game/Level3.cpp (revision 5478)+++ src/game/Level3.cpp (working copy)@@ -44,6 +44,50 @@ #include "SystemConfig.h" #include "Config/ConfigEnv.h" ++++++bool ChatHandler::HandleSetLootRangeCommand(const char* args)+{+ if(!*args)+ return false;++ Creature* pCreature = getSelectedCreature();+ if (!pCreature)+ {+ PSendSysMessage(LANG_SELECT_CREATURE);+ return false;+ }++ char* pMax = strtok((char*)args, " ");+ char* pMin = strtok(NULL, " ");+ int iMax, iMin = 0;+ + if ( !pMax )+ {+ SendSysMessage(LANG_CMD_SYNTAX);+ return false;+ }++ iMax = atoi( pMax );+ if ( pMin ) iMin = atoi( pMin );+ + if ( iMax < iMin || iMax<0 || iMin<0 )+ {+ SendSysMessage( LANG_CREATURE_LOOT_RANGE_INVALID );+ return false;+ }++ WorldDatabase.PExecuteLog("UPDATE creature_template SET lootMinCount = '%d', lootMaxCount = '%d' WHERE entry = '%u'", iMin, iMax, pCreature->GetEntry());+ const_cast<CreatureInfo*>(pCreature->GetCreatureInfo())->lootMinCount = iMin;+ const_cast<CreatureInfo*>(pCreature->GetCreatureInfo())->lootMaxCount = iMax;+ PSendSysMessage( LANG_CREATURE_LOOT_RANGE_MODIFIED, pCreature->GetEntry(), iMin, iMax );+ return true;+}++ //reload commands bool ChatHandler::HandleReloadCommand(const char* arg) {Index: src/game/LootMgr.cpp===================================================================--- src/game/LootMgr.cpp (revision 5478)+++ src/game/LootMgr.cpp (working copy)@@ -74,7 +74,7 @@ sLog.outString( "%s :", tablename); - // 0 1 2 3 4 5 6 7 8+ // 0 1 2 3 4 5 6 7 8 9 QueryResult *result = WorldDatabase.PQuery("SELECT entry, item, ChanceOrRef, QuestChanceOrGroup, mincount, maxcount, freeforall, lootcondition, condition_value1, condition_value2 FROM %s",tablename); if (result)@@ -254,6 +254,21 @@ LoadLootTable(LootTemplates_Prospecting, "prospecting_loot_template"); } ++struct LootedItem {+ float value;+ LootStoreItem *item;++ LootedItem(LootStoreItem *_item, float _value) : value(_value), item(_item) {};+ bool operator<(const LootedItem &_2) const+ {+ return value < _2.value;+ }+};++typedef std::set<LootedItem> LootItems;++ struct HasChance { struct GroupChance@@ -267,10 +282,12 @@ typedef std::vector<GroupChance> GroupChances; LootStore* m_store;+ LootItems &m_items;+ int m_min, m_max; GroupChances groupChance; - explicit HasChance(LootStore* _store) : m_store(_store)+ explicit HasChance(LootStore* _store, LootItems &_items, int _min, int _max) : m_store(_store), m_items(_items), m_min(_min), m_max(_max) { } @@ -284,8 +301,43 @@ if (itm.questChanceOrGroup == 0) { // reference non group loot ( itm.chanceOrRef < 0) handled separatly- if ( itm.chanceOrRef > 0 && roll_chance_f(itm.chanceOrRef * sWorld.getRate(RATE_DROP_ITEMS)) )- return &itm;+ if ( itm.chanceOrRef > 0 )+ {+ float chance = rand_chance();+ float itm_chance = itm.chanceOrRef * sWorld.getRate(RATE_DROP_ITEMS);+ if ( m_min==0 && m_max==0 )+ {+ //sLog.outDebug("HasChance: min=max=0 returning item as usual");+ if ( chance < itm_chance ) + return &itm;+ return NULL;+ }++ float n_chance = chance / itm_chance; // normalize chance+ if ( chance > itm_chance ) n_chance *= -1;+ //sLog.outDebug("HasChance: item %u has normalized chance: %f", itm.itemid, n_chance);+ if ( m_items.size() < m_min )+ {+ //sLog.outDebug("HasChance: itemcount: %d, min: %d not reached", m_items.size(), m_min);+ m_items.insert( LootedItem(&itm, n_chance) );+ }+ else if ( m_items.size() >= m_max || m_items.begin()->value < 0 )+ {+ //sLog.outDebug("HasChance: itemcount: %d, max: %d reached or begin->value < 0 (%f)", m_items.size(), m_max, m_items.begin()->value);+ // see if the lowest value in the set is lower than this, in this case replace the item+ if ( m_items.begin()->value < n_chance )+ {+ //sLog.outDebug("HasChance: lowest value (%f) is smaller than chance %f, dropping %u, adding %u", m_items.begin()->value, n_chance, m_items.begin()->item->itemid, itm.itemid);+ m_items.erase( m_items.begin() );+ m_items.insert( LootedItem(&itm, n_chance) );+ }+ } else {+ //sLog.outDebug("HasChance:%d count. min (%d) reached, max (%d) not. Adding %u as ususal. %f<%f ? nchance: %f,min_nchance: %f", m_items.size(), m_min, m_max, itm.itemid, chance, itm_chance, n_chance, m_items.begin()->value);+ if ( chance < itm_chance )+ m_items.insert( LootedItem(&itm, n_chance) );+ }+ // return NULL, no to add item to lootlist yet+ } return NULL; } @@ -349,7 +401,7 @@ inline bool operator() ( LootStoreItem &itm ) { return roll_chance_i(itm.questChanceOrGroup); } }; -void FillLoot(Loot* loot, uint32 loot_id, LootStore& store, Player* loot_owner, bool referenced)+void FillLoot(Loot* loot, uint32 loot_id, LootStore& store, Player* loot_owner, bool referenced, int min, int max) { LootStore::iterator tab = store.find(loot_id); @@ -363,7 +415,8 @@ loot->quest_items.reserve(min(tab->second.size(),MAX_NR_QUEST_ITEMS)); // fill loot with all normal and quest items that have a chance- HasChance hasChance(&store);+ LootItems items;+ HasChance hasChance(&store, items, min, max); HasQuestChance hasQuestChance; for(LootStoreItemList::iterator item_iter = tab->second.begin(); item_iter != tab->second.end(); ++item_iter)@@ -383,7 +436,7 @@ // Reference to another loot int LootId = -int(item_iter->chanceOrRef); - FillLoot(loot,LootId,store,loot_owner,true);+ FillLoot(loot,LootId,store,loot_owner,true, item_iter->mincount, item_iter->maxcount); continue; } @@ -405,6 +458,26 @@ } } + if ( items.size() )+ {+ //sLog.outDebug("FillLoot: items.size() is not 0");+ for (LootItems::iterator it = items.begin(); it != items.end(); ++it)+ {+ //sLog.outDebug("FillLoot: adding 'items' to loot: %u (had value:%f)", it->item->itemid, it->value);+ loot->items.push_back(+ LootItem(*it->item, urand(it->item->mincount, it->item->maxcount),+ GenerateEnchSuffixFactor(it->item->itemid),+ Item::GenerateItemRandomPropertyId(it->item->itemid))+ );++ // non-conditional one-player only items are counted here,+ // free for all items are counted in FillFFALoot(),+ // non-ffa conditionals are counted in FillNonQuestNonFFAConditionalLoot()+ if( ! it->item->freeforall && ! it->item->condition)+ ++loot->unlootedCount;+ }+ }+ if(referenced) return; if(!loot_owner)Index: src/game/LootMgr.h===================================================================--- src/game/LootMgr.h (revision 5478)+++ src/game/LootMgr.h (working copy)@@ -258,7 +258,7 @@ QuestItemList* FillFFALoot(Player* player, Loot *loot); QuestItemList* FillQuestLoot(Player* player, Loot *loot); QuestItemList* FillNonQuestNonFFAConditionalLoot(Player* player, Loot *loot);-void FillLoot(Loot *loot, uint32 loot_id, LootStore& store, Player* loot_owner, bool referenced = false);+void FillLoot(Loot *loot, uint32 loot_id, LootStore& store, Player* loot_owner, bool referenced = false, int min_loot_count = 0, int max_loot_count = 0); void LoadLootTables(); void LoadLootTable(LootStore& lootstore,char const* tablename); Index: src/game/ObjectMgr.cpp===================================================================--- src/game/ObjectMgr.cpp (revision 5478)+++ src/game/ObjectMgr.cpp (working copy)@@ -541,6 +541,12 @@ const_cast<CreatureInfo*>(cInfo)->scale = ScaleEntry ? ScaleEntry->scale : 1.0f; } + if ( cInfo->lootMaxCount < cInfo->lootMinCount ) + {+ sLog.outErrorDb("Creature (Entry: %u) has wrong minLoot,maxLoot value (%u, %u), set to 0,0.",cInfo->Entry,cInfo->lootMinCount, cInfo->lootMaxCount);+ const_cast<CreatureInfo*>(cInfo)->lootMinCount = 0;+ const_cast<CreatureInfo*>(cInfo)->lootMaxCount = 0;+ } } } Index: src/game/Player.cpp===================================================================--- src/game/Player.cpp (revision 5478)+++ src/game/Player.cpp (working copy)@@ -6897,7 +6897,7 @@ if (!creature->HasFlag(UNIT_NPC_FLAGS,UNIT_NPC_FLAG_VENDOR) && lootid) { loot->clear();- FillLoot(loot, lootid, LootTemplates_Creature, recipient);+ FillLoot(loot, lootid, LootTemplates_Creature, recipient, false, creature->GetCreatureInfo()->lootMinCount, creature->GetCreatureInfo()->lootMaxCount); } loot->generateMoneyLoot(creature->GetCreatureInfo()->mingold,creature->GetCreatureInfo()->maxgold);Index: src/shared/Database/SQLStorage.cpp===================================================================--- src/shared/Database/SQLStorage.cpp (revision 5478)+++ src/shared/Database/SQLStorage.cpp (working copy)@@ -27,7 +27,7 @@ extern DatabaseMysql WorldDatabase; #endif -const char CreatureInfofmt[]="iiiiisssiiiiiiiiiiffiffiiiiiiiiiiiffiiliiiiiiiiiiiiiiiisiilliis";+const char CreatureInfofmt[]="iiiiisssiiiiiiiiiiffiffiiiiiiiiiiiffiiliiiiiiiiiiiiiiiiiisiilliis"; const char CreatureDataAddonInfofmt[]="iiiiiis"; const char CreatureModelfmt[]="iffii"; const char CreatureInfoAddonInfofmt[]="iiiiiis";