2008-10-02 16:23:55 -05:00
/*
2008-10-23 03:29:52 -05:00
* Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
2008-10-14 11:57:03 -05:00
*
2008-10-23 03:29:52 -05:00
* Copyright (C) 2008 Trinity <http://www.trinitycore.org/>
2008-10-02 16:23:55 -05:00
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
2008-10-14 11:57:03 -05:00
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2008-10-02 16:23:55 -05:00
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
2008-10-14 11:57:03 -05:00
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
2008-10-02 16:23:55 -05:00
*/
#include "LootMgr.h"
#include "Log.h"
#include "ObjectMgr.h"
#include "ProgressBar.h"
#include "World.h"
#include "Util.h"
#include "SharedDefines.h"
static Rates const qualityToRate [ MAX_ITEM_QUALITY ] = {
RATE_DROP_ITEM_POOR , // ITEM_QUALITY_POOR
RATE_DROP_ITEM_NORMAL , // ITEM_QUALITY_NORMAL
RATE_DROP_ITEM_UNCOMMON , // ITEM_QUALITY_UNCOMMON
RATE_DROP_ITEM_RARE , // ITEM_QUALITY_RARE
RATE_DROP_ITEM_EPIC , // ITEM_QUALITY_EPIC
RATE_DROP_ITEM_LEGENDARY , // ITEM_QUALITY_LEGENDARY
RATE_DROP_ITEM_ARTIFACT , // ITEM_QUALITY_ARTIFACT
};
LootStore LootTemplates_Creature ( "creature_loot_template" , "creature entry" );
LootStore LootTemplates_Disenchant ( "disenchant_loot_template" , "item disenchant id" );
LootStore LootTemplates_Fishing ( "fishing_loot_template" , "area id" );
LootStore LootTemplates_Gameobject ( "gameobject_loot_template" , "gameobject entry" );
LootStore LootTemplates_Item ( "item_loot_template" , "item entry" );
LootStore LootTemplates_Pickpocketing ( "pickpocketing_loot_template" , "creature pickpocket lootid" );
LootStore LootTemplates_Prospecting ( "prospecting_loot_template" , "item entry" );
LootStore LootTemplates_QuestMail ( "quest_mail_loot_template" , "quest id" );
LootStore LootTemplates_Reference ( "reference_loot_template" , "reference id" );
LootStore LootTemplates_Skinning ( "skinning_loot_template" , "creature skinning id" );
class LootTemplate :: LootGroup // A set of loot definitions for items (refs are not allowed)
{
public :
void AddEntry ( LootStoreItem & item ); // Adds an entry to the group (at loading stage)
bool HasQuestDrop () const ; // True if group includes at least 1 quest drop entry
bool HasQuestDropForPlayer ( Player const * player ) const ;
// The same for active quests of the player
void Process ( Loot & loot ) const ; // Rolls an item from the group (if any) and adds the item to the loot
float RawTotalChance () const ; // Overall chance for the group (without equal chanced items)
float TotalChance () const ; // Overall chance for the group
void Verify ( LootStore const & lootstore , uint32 id , uint32 group_id ) const ;
void CollectLootIds ( LootIdSet & set ) const ;
void CheckLootRefs ( LootTemplateMap const & store , LootIdSet * ref_set ) const ;
private :
LootStoreItemList ExplicitlyChanced ; // Entries with chances defined in DB
LootStoreItemList EqualChanced ; // Zero chances - every entry takes the same chance
LootStoreItem const * Roll () const ; // Rolls an item from the group, returns NULL if all miss their chances
};
//Remove all data and free all memory
void LootStore :: Clear ()
{
for ( LootTemplateMap :: const_iterator itr = m_LootTemplates . begin (); itr != m_LootTemplates . end (); ++ itr )
delete itr -> second ;
m_LootTemplates . clear ();
}
// Checks validity of the loot store
// Actual checks are done within LootTemplate::Verify() which is called for every template
void LootStore :: Verify () const
{
for ( LootTemplateMap :: const_iterator i = m_LootTemplates . begin (); i != m_LootTemplates . end (); ++ i )
i -> second -> Verify ( * this , i -> first );
}
// Loads a *_loot_template DB table into loot store
// All checks of the loaded template are called from here, no error reports at loot generation required
void LootStore :: LoadLootTable ()
{
LootTemplateMap :: iterator tab ;
uint32 count = 0 ;
// Clearing store (for reloading case)
Clear ();
sLog . outString ( "%s :" , GetName ());
// 0 1 2 3 4 5 6 7 8
QueryResult * result = WorldDatabase . PQuery ( "SELECT entry, item, ChanceOrQuestChance, groupid, mincountOrRef, maxcount, lootcondition, condition_value1, condition_value2 FROM %s" , GetName ());
if ( result )
{
barGoLink bar ( result -> GetRowCount ());
do
{
Field * fields = result -> Fetch ();
bar . step ();
uint32 entry = fields [ 0 ]. GetUInt32 ();
uint32 item = fields [ 1 ]. GetUInt32 ();
float chanceOrQuestChance = fields [ 2 ]. GetFloat ();
uint8 group = fields [ 3 ]. GetUInt8 ();
int32 mincountOrRef = fields [ 4 ]. GetInt32 ();
uint8 maxcount = fields [ 5 ]. GetUInt8 ();
ConditionType condition = ( ConditionType ) fields [ 6 ]. GetUInt8 ();
uint32 cond_value1 = fields [ 7 ]. GetUInt32 ();
uint32 cond_value2 = fields [ 8 ]. GetUInt32 ();
if ( ! PlayerCondition :: IsValid ( condition , cond_value1 , cond_value2 ))
{
sLog . outErrorDb ( "... in table '%s' entry %u item %u" , GetName (), entry , item );
continue ; // error already printed to log/console.
}
// (condition + cond_value1/2) are converted into single conditionId
uint16 conditionId = objmgr . GetConditionId ( condition , cond_value1 , cond_value2 );
LootStoreItem storeitem = LootStoreItem ( item , chanceOrQuestChance , group , conditionId , mincountOrRef , maxcount );
if ( ! storeitem . IsValid ( * this , entry )) // Validity checks
continue ;
// Looking for the template of the entry
// often entries are put together
if ( m_LootTemplates . empty () || tab -> first != entry )
{
// Searching the template (in case template Id changed)
tab = m_LootTemplates . find ( entry );
if ( tab == m_LootTemplates . end () )
{
std :: pair < LootTemplateMap :: iterator , bool > pr = m_LootTemplates . insert ( LootTemplateMap :: value_type ( entry , new LootTemplate ));
tab = pr . first ;
}
}
// else is empty - template Id and iter are the same
// finally iter refers to already existed or just created <entry, LootTemplate>
// Adds current row to the template
tab -> second -> AddEntry ( storeitem );
++ count ;
} while ( result -> NextRow ());
delete result ;
Verify (); // Checks validity of the loot store
sLog . outString ();
sLog . outString ( ">> Loaded %u loot definitions (%d templates)" , count , m_LootTemplates . size ());
}
else
{
sLog . outString ();
sLog . outErrorDb ( ">> Loaded 0 loot definitions. DB table `%s` is empty." , GetName () );
}
}
bool LootStore :: HaveQuestLootFor ( uint32 loot_id ) const
{
LootTemplateMap :: const_iterator itr = m_LootTemplates . find ( loot_id );
if ( itr == m_LootTemplates . end ())
return false ;
// scan loot for quest items
return itr -> second -> HasQuestDrop ( m_LootTemplates );
}
bool LootStore :: HaveQuestLootForPlayer ( uint32 loot_id , Player * player ) const
{
LootTemplateMap :: const_iterator tab = m_LootTemplates . find ( loot_id );
if ( tab != m_LootTemplates . end ())
if ( tab -> second -> HasQuestDropForPlayer ( m_LootTemplates , player ))
return true ;
return false ;
}
LootTemplate const * LootStore :: GetLootFor ( uint32 loot_id ) const
{
LootTemplateMap :: const_iterator tab = m_LootTemplates . find ( loot_id );
if ( tab == m_LootTemplates . end ())
return NULL ;
return tab -> second ;
}
void LootStore :: LoadAndCollectLootIds ( LootIdSet & ids_set )
{
LoadLootTable ();
for ( LootTemplateMap :: const_iterator tab = m_LootTemplates . begin (); tab != m_LootTemplates . end (); ++ tab )
ids_set . insert ( tab -> first );
}
void LootStore :: CheckLootRefs ( LootIdSet * ref_set ) const
{
for ( LootTemplateMap :: const_iterator ltItr = m_LootTemplates . begin (); ltItr != m_LootTemplates . end (); ++ ltItr )
ltItr -> second -> CheckLootRefs ( m_LootTemplates , ref_set );
}
void LootStore :: ReportUnusedIds ( LootIdSet const & ids_set ) const
{
// all still listed ids isn't referenced
for ( LootIdSet :: const_iterator itr = ids_set . begin (); itr != ids_set . end (); ++ itr )
sLog . outErrorDb ( "Table '%s' entry %d isn't %s and not referenced from loot, and then useless." , GetName (), * itr , GetEntryName ());
}
void LootStore :: ReportNotExistedId ( uint32 id ) const
{
sLog . outErrorDb ( "Table '%s' entry %d (%s) not exist but used as loot id in DB." , GetName (), id , GetEntryName ());
}
//
// --------- LootStoreItem ---------
//
// Checks if the entry (quest, non-quest, reference) takes it's chance (at loot generation)
// RATE_DROP_ITEMS is no longer used for all types of entries
bool LootStoreItem :: Roll () const
{
if ( chance >= 100.f )
return true ;
if ( mincountOrRef < 0 ) // reference case
return roll_chance_f ( chance * sWorld . getRate ( RATE_DROP_ITEM_REFERENCED ));
ItemPrototype const * pProto = objmgr . GetItemPrototype ( itemid );
float qualityModifier = pProto ? sWorld . getRate ( qualityToRate [ pProto -> Quality ]) : 1.0f ;
return roll_chance_f ( chance * qualityModifier );
}
// Checks correctness of values
bool LootStoreItem :: IsValid ( LootStore const & store , uint32 entry ) const
{
if ( mincountOrRef == 0 )
{
sLog . outErrorDb ( "Table '%s' entry %d item %d: wrong mincountOrRef (%d) - skipped" , store . GetName (), entry , itemid , mincountOrRef );
return false ;
}
if ( mincountOrRef > 0 ) // item (quest or non-quest) entry, maybe grouped
{
ItemPrototype const * proto = objmgr . GetItemPrototype ( itemid );
if ( ! proto )
{
sLog . outErrorDb ( "Table '%s' entry %d item %d: item entry not listed in `item_template` - skipped" , store . GetName (), entry , itemid );
return false ;
}
if ( chance == 0 && group == 0 ) // Zero chance is allowed for grouped entries only
{
sLog . outErrorDb ( "Table '%s' entry %d item %d: equal-chanced grouped entry, but group not defined - skipped" , store . GetName (), entry , itemid );
return false ;
}
if ( chance != 0 && chance < 0.000001f ) // loot with low chance
{
2008-11-14 17:03:03 -06:00
sLog . outErrorDb ( "Table '%s' entry %d item %d: low chance (%f) - skipped" ,
store . GetName (), entry , itemid , chance );
2008-10-02 16:23:55 -05:00
return false ;
}
}
else // mincountOrRef < 0
{
if ( needs_quest )
sLog . outErrorDb ( "Table '%s' entry %d item %d: quest chance will be treated as non-quest chance" , store . GetName (), entry , itemid );
else if ( chance == 0 ) // no chance for the reference
{
sLog . outErrorDb ( "Table '%s' entry %d item %d: zero chance is specified for a reference, skipped" , store . GetName (), entry , itemid );
return false ;
}
}
return true ; // Referenced template existence is checked at whole store level
}
//
// --------- LootItem ---------
//
// Constructor, copies most fields from LootStoreItem and generates random count
LootItem :: LootItem ( LootStoreItem const & li )
{
itemid = li . itemid ;
conditionId = li . conditionId ;
ItemPrototype const * proto = objmgr . GetItemPrototype ( itemid );
freeforall = proto && ( proto -> Flags & ITEM_FLAGS_PARTY_LOOT );
needs_quest = li . needs_quest ;
count = urand ( li . mincountOrRef , li . maxcount ); // constructor called for mincountOrRef > 0 only
randomSuffix = GenerateEnchSuffixFactor ( itemid );
randomPropertyId = Item :: GenerateItemRandomPropertyId ( itemid );
is_looted = 0 ;
is_blocked = 0 ;
is_underthreshold = 0 ;
is_counted = 0 ;
}
// Basic checks for player/item compatibility - if false no chance to see the item in the loot
bool LootItem :: AllowedForPlayer ( Player const * player ) const
{
// DB conditions check
if ( ! objmgr . IsPlayerMeetToCondition ( player , conditionId ) )
return false ;
if ( needs_quest )
{
// Checking quests for quest-only drop (check only quests requirements in this case)
if ( ! player -> HasQuestForItem ( itemid ) )
return false ;
}
else
{
// Not quest only drop (check quest starting items for already accepted non-repeatable quests)
ItemPrototype const * pProto = objmgr . GetItemPrototype ( itemid );
if ( pProto && pProto -> StartQuest && player -> GetQuestStatus ( pProto -> StartQuest ) != QUEST_STATUS_NONE && ! player -> HasQuestForItem ( itemid ))
return false ;
}
return true ;
}
//
// --------- Loot ---------
//
// Inserts the item into the loot (called by LootTemplate processors)
void Loot :: AddItem ( LootStoreItem const & item )
{
if ( item . needs_quest ) // Quest drop
{
if ( quest_items . size () < MAX_NR_QUEST_ITEMS )
quest_items . push_back ( LootItem ( item ));
}
else if ( items . size () < MAX_NR_LOOT_ITEMS ) // Non-quest drop
{
items . push_back ( LootItem ( item ));
// 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 ( ! item . conditionId )
{
ItemPrototype const * proto = objmgr . GetItemPrototype ( item . itemid );
if ( ! proto || ( proto -> Flags & ITEM_FLAGS_PARTY_LOOT ) == 0 )
++ unlootedCount ;
}
}
}
// Calls processor of corresponding LootTemplate (which handles everything including references)
void Loot :: FillLoot ( uint32 loot_id , LootStore const & store , Player * loot_owner )
{
LootTemplate const * tab = store . GetLootFor ( loot_id );
if ( ! tab )
{
sLog . outErrorDb ( "Table '%s' loot id #%u used but it doesn't have records." , store . GetName (), loot_id );
return ;
}
items . reserve ( MAX_NR_LOOT_ITEMS );
quest_items . reserve ( MAX_NR_QUEST_ITEMS );
tab -> Process ( * this , store ); // Processing is done there, callback via Loot::AddItem()
// Setting access rights fow group-looting case
if ( ! loot_owner )
return ;
Group * pGroup = loot_owner -> GetGroup ();
if ( ! pGroup )
return ;
for ( GroupReference * itr = pGroup -> GetFirstMember (); itr != NULL ; itr = itr -> next ())
{
//fill the quest item map for every player in the recipient's group
Player * pl = itr -> getSource ();
if ( ! pl )
continue ;
uint32 plguid = pl -> GetGUIDLow ();
QuestItemMap :: iterator qmapitr = PlayerQuestItems . find ( plguid );
if ( qmapitr == PlayerQuestItems . end ())
{
FillQuestLoot ( pl );
}
qmapitr = PlayerFFAItems . find ( plguid );
if ( qmapitr == PlayerFFAItems . end ())
{
FillFFALoot ( pl );
}
qmapitr = PlayerNonQuestNonFFAConditionalItems . find ( plguid );
if ( qmapitr == PlayerNonQuestNonFFAConditionalItems . end ())
{
FillNonQuestNonFFAConditionalLoot ( pl );
}
}
}
QuestItemList * Loot :: FillFFALoot ( Player * player )
{
QuestItemList * ql = new QuestItemList ();
for ( uint8 i = 0 ; i < items . size (); i ++ )
{
LootItem & item = items [ i ];
if ( ! item . is_looted && item . freeforall && item . AllowedForPlayer ( player ) )
{
ql -> push_back ( QuestItem ( i ));
++ unlootedCount ;
}
}
if ( ql -> empty ())
{
delete ql ;
return NULL ;
}
PlayerFFAItems [ player -> GetGUIDLow ()] = ql ;
return ql ;
}
QuestItemList * Loot :: FillQuestLoot ( Player * player )
{
if ( items . size () == MAX_NR_LOOT_ITEMS ) return NULL ;
QuestItemList * ql = new QuestItemList ();
for ( uint8 i = 0 ; i < quest_items . size (); i ++ )
{
LootItem & item = quest_items [ i ];
if ( ! item . is_looted && item . AllowedForPlayer ( player ) )
{
ql -> push_back ( QuestItem ( i ));
// questitems get blocked when they first apper in a
// player's quest vector
//
// increase once if one looter only, looter-times if free for all
if ( item . freeforall || ! item . is_blocked )
++ unlootedCount ;
item . is_blocked = true ;
if ( items . size () + ql -> size () == MAX_NR_LOOT_ITEMS )
break ;
}
}
if ( ql -> empty ())
{
delete ql ;
return NULL ;
}
PlayerQuestItems [ player -> GetGUIDLow ()] = ql ;
return ql ;
}
QuestItemList * Loot :: FillNonQuestNonFFAConditionalLoot ( Player * player )
{
QuestItemList * ql = new QuestItemList ();
for ( uint8 i = 0 ; i < items . size (); ++ i )
{
LootItem & item = items [ i ];
if ( ! item . is_looted && ! item . freeforall && item . conditionId && item . AllowedForPlayer ( player ))
{
ql -> push_back ( QuestItem ( i ));
if ( ! item . is_counted )
{
++ unlootedCount ;
item . is_counted = true ;
}
}
}
if ( ql -> empty ())
{
delete ql ;
return NULL ;
}
PlayerNonQuestNonFFAConditionalItems [ player -> GetGUIDLow ()] = ql ;
return ql ;
}
//===================================================
void Loot :: NotifyItemRemoved ( uint8 lootIndex )
{
// notify all players that are looting this that the item was removed
// convert the index to the slot the player sees
std :: set < uint64 >:: iterator i_next ;
for ( std :: set < uint64 >:: iterator i = PlayersLooting . begin (); i != PlayersLooting . end (); i = i_next )
{
i_next = i ;
++ i_next ;
if ( Player * pl = ObjectAccessor :: FindPlayer ( * i ))
pl -> SendNotifyLootItemRemoved ( lootIndex );
else
PlayersLooting . erase ( i );
}
}
void Loot :: NotifyMoneyRemoved ()
{
// notify all players that are looting this that the money was removed
std :: set < uint64 >:: iterator i_next ;
for ( std :: set < uint64 >:: iterator i = PlayersLooting . begin (); i != PlayersLooting . end (); i = i_next )
{
i_next = i ;
++ i_next ;
if ( Player * pl = ObjectAccessor :: FindPlayer ( * i ))
pl -> SendNotifyLootMoneyRemoved ();
else
PlayersLooting . erase ( i );
}
}
void Loot :: NotifyQuestItemRemoved ( uint8 questIndex )
{
// when a free for all questitem is looted
// all players will get notified of it being removed
// (other questitems can be looted by each group member)
2008-11-14 16:28:45 -06:00
// bit inefficient but isn't called often
2008-10-02 16:23:55 -05:00
std :: set < uint64 >:: iterator i_next ;
for ( std :: set < uint64 >:: iterator i = PlayersLooting . begin (); i != PlayersLooting . end (); i = i_next )
{
i_next = i ;
++ i_next ;
if ( Player * pl = ObjectAccessor :: FindPlayer ( * i ))
{
QuestItemMap :: iterator pq = PlayerQuestItems . find ( pl -> GetGUIDLow ());
if ( pq != PlayerQuestItems . end () && pq -> second )
{
// find where/if the player has the given item in it's vector
QuestItemList & pql = * pq -> second ;
uint8 j ;
for ( j = 0 ; j < pql . size (); ++ j )
if ( pql [ j ]. index == questIndex )
break ;
if ( j < pql . size ())
pl -> SendNotifyLootItemRemoved ( items . size () + j );
}
}
else
PlayersLooting . erase ( i );
}
}
void Loot :: generateMoneyLoot ( uint32 minAmount , uint32 maxAmount )
{
if ( maxAmount > 0 )
{
if ( maxAmount <= minAmount )
gold = uint32 ( maxAmount * sWorld . getRate ( RATE_DROP_MONEY ));
else if (( maxAmount - minAmount ) < 32700 )
gold = uint32 ( urand ( minAmount , maxAmount ) * sWorld . getRate ( RATE_DROP_MONEY ));
else
gold = uint32 ( urand ( minAmount >> 8 , maxAmount >> 8 ) * sWorld . getRate ( RATE_DROP_MONEY )) << 8 ;
}
}
LootItem * Loot :: LootItemInSlot ( uint32 lootSlot , Player * player , QuestItem ** qitem , QuestItem ** ffaitem , QuestItem ** conditem )
{
LootItem * item = NULL ;
bool is_looted = true ;
if ( lootSlot >= items . size ())
{
uint32 questSlot = lootSlot - items . size ();
QuestItemMap :: const_iterator itr = PlayerQuestItems . find ( player -> GetGUIDLow ());
if ( itr != PlayerQuestItems . end () && questSlot < itr -> second -> size ())
{
QuestItem * qitem2 = & itr -> second -> at ( questSlot );
if ( qitem )
* qitem = qitem2 ;
item = & quest_items [ qitem2 -> index ];
is_looted = qitem2 -> is_looted ;
}
}
else
{
item = & items [ lootSlot ];
is_looted = item -> is_looted ;
if ( item -> freeforall )
{
QuestItemMap :: const_iterator itr = PlayerFFAItems . find ( player -> GetGUIDLow ());
if ( itr != PlayerFFAItems . end ())
{
for ( QuestItemList :: iterator iter = itr -> second -> begin (); iter != itr -> second -> end (); ++ iter )
if ( iter -> index == lootSlot )
{
QuestItem * ffaitem2 = ( QuestItem * ) & ( * iter );
if ( ffaitem )
* ffaitem = ffaitem2 ;
is_looted = ffaitem2 -> is_looted ;
break ;
}
}
}
else if ( item -> conditionId )
{
QuestItemMap :: const_iterator itr = PlayerNonQuestNonFFAConditionalItems . find ( player -> GetGUIDLow ());
if ( itr != PlayerNonQuestNonFFAConditionalItems . end ())
{
for ( QuestItemList :: iterator iter = itr -> second -> begin (); iter != itr -> second -> end (); ++ iter )
{
if ( iter -> index == lootSlot )
{
QuestItem * conditem2 = ( QuestItem * ) & ( * iter );
if ( conditem )
* conditem = conditem2 ;
is_looted = conditem2 -> is_looted ;
break ;
}
}
}
}
}
if ( is_looted )
return NULL ;
return item ;
}
ByteBuffer & operator << ( ByteBuffer & b , LootItem const & li )
{
b << uint32 ( li . itemid );
b << uint32 ( li . count ); // nr of items of this type
b << uint32 ( objmgr . GetItemPrototype ( li . itemid ) -> DisplayInfoID );
b << uint32 ( li . randomSuffix );
b << uint32 ( li . randomPropertyId );
//b << uint8(0); // slot type - will send after this function call
return b ;
}
ByteBuffer & operator << ( ByteBuffer & b , LootView const & lv )
{
Loot & l = lv . loot ;
uint8 itemsShown = 0 ;
//gold
b << uint32 ( lv . permission != NONE_PERMISSION ? l . gold : 0 );
size_t count_pos = b . wpos (); // pos of item count byte
b << uint8 ( 0 ); // item count placeholder
switch ( lv . permission )
{
case GROUP_PERMISSION :
{
// You are not the items proprietary, so you can only see
// blocked rolled items and quest items, and !ffa items
for ( uint8 i = 0 ; i < l . items . size (); ++ i )
{
if ( ! l . items [ i ]. is_looted && ! l . items [ i ]. freeforall && ! l . items [ i ]. conditionId && l . items [ i ]. AllowedForPlayer ( lv . viewer ))
{
uint8 slot_type = ( l . items [ i ]. is_blocked || l . items [ i ]. is_underthreshold ) ? 0 : 1 ;
b << uint8 ( i ) << l . items [ i ]; //send the index and the item if it's not looted, and blocked or under threshold, free for all items will be sent later, only one-player loots here
b << uint8 ( slot_type ); // 0 - get 1 - look only
++ itemsShown ;
}
}
break ;
}
case ALL_PERMISSION :
case MASTER_PERMISSION :
{
uint8 slot_type = ( lv . permission == MASTER_PERMISSION ) ? 2 : 0 ;
for ( uint8 i = 0 ; i < l . items . size (); ++ i )
{
if ( ! l . items [ i ]. is_looted && ! l . items [ i ]. freeforall && ! l . items [ i ]. conditionId && l . items [ i ]. AllowedForPlayer ( lv . viewer ))
{
b << uint8 ( i ) << l . items [ i ]; //only send one-player loot items now, free for all will be sent later
b << uint8 ( slot_type ); // 0 - get 2 - master selection
++ itemsShown ;
}
}
break ;
}
case NONE_PERMISSION :
default :
return b ; // nothing output more
}
if ( lv . qlist )
{
for ( QuestItemList :: iterator qi = lv . qlist -> begin () ; qi != lv . qlist -> end (); ++ qi )
{
LootItem & item = l . quest_items [ qi -> index ];
if ( ! qi -> is_looted && ! item . is_looted )
{
b << uint8 ( l . items . size () + ( qi - lv . qlist -> begin ()));
b << item ;
b << uint8 ( 0 ); // allow loot
++ itemsShown ;
}
}
}
if ( lv . ffalist )
{
for ( QuestItemList :: iterator fi = lv . ffalist -> begin () ; fi != lv . ffalist -> end (); ++ fi )
{
LootItem & item = l . items [ fi -> index ];
if ( ! fi -> is_looted && ! item . is_looted )
{
b << uint8 ( fi -> index ) << item ;
b << uint8 ( 0 ); // allow loot
++ itemsShown ;
}
}
}
if ( lv . conditionallist )
{
for ( QuestItemList :: iterator ci = lv . conditionallist -> begin () ; ci != lv . conditionallist -> end (); ++ ci )
{
LootItem & item = l . items [ ci -> index ];
if ( ! ci -> is_looted && ! item . is_looted )
{
b << uint8 ( ci -> index ) << item ;
b << uint8 ( 0 ); // allow loot
++ itemsShown ;
}
}
}
//update number of items shown
b . put < uint8 > ( count_pos , itemsShown );
return b ;
}
//
// --------- LootTemplate::LootGroup ---------
//
// Adds an entry to the group (at loading stage)
void LootTemplate :: LootGroup :: AddEntry ( LootStoreItem & item )
{
if ( item . chance != 0 )
ExplicitlyChanced . push_back ( item );
else
EqualChanced . push_back ( item );
}
// Rolls an item from the group, returns NULL if all miss their chances
LootStoreItem const * LootTemplate :: LootGroup :: Roll () const
{
if ( ! ExplicitlyChanced . empty ()) // First explicitly chanced entries are checked
{
float Roll = rand_chance ();
for ( uint32 i = 0 ; i < ExplicitlyChanced . size (); ++ i ) //check each explicitly chanced entry in the template and modify its chance based on quality.
{
if ( ExplicitlyChanced [ i ]. chance >= 100.f )
return & ExplicitlyChanced [ i ];
ItemPrototype const * pProto = objmgr . GetItemPrototype ( ExplicitlyChanced [ i ]. itemid );
float qualityMultiplier = pProto ? sWorld . getRate ( qualityToRate [ pProto -> Quality ]) : 1.0f ;
Roll -= ExplicitlyChanced [ i ]. chance * qualityMultiplier ;
if ( Roll < 0 )
return & ExplicitlyChanced [ i ];
}
}
if ( ! EqualChanced . empty ()) // If nothing selected yet - an item is taken from equal-chanced part
return & EqualChanced [ irand ( 0 , EqualChanced . size () - 1 )];
return NULL ; // Empty drop from the group
}
// True if group includes at least 1 quest drop entry
bool LootTemplate :: LootGroup :: HasQuestDrop () const
{
for ( LootStoreItemList :: const_iterator i = ExplicitlyChanced . begin (); i != ExplicitlyChanced . end (); ++ i )
if ( i -> needs_quest )
return true ;
for ( LootStoreItemList :: const_iterator i = EqualChanced . begin (); i != EqualChanced . end (); ++ i )
if ( i -> needs_quest )
return true ;
return false ;
}
// True if group includes at least 1 quest drop entry for active quests of the player
bool LootTemplate :: LootGroup :: HasQuestDropForPlayer ( Player const * player ) const
{
for ( LootStoreItemList :: const_iterator i = ExplicitlyChanced . begin (); i != ExplicitlyChanced . end (); ++ i )
if ( player -> HasQuestForItem ( i -> itemid ))
return true ;
for ( LootStoreItemList :: const_iterator i = EqualChanced . begin (); i != EqualChanced . end (); ++ i )
if ( player -> HasQuestForItem ( i -> itemid ))
return true ;
return false ;
}
// Rolls an item from the group (if any takes its chance) and adds the item to the loot
void LootTemplate :: LootGroup :: Process ( Loot & loot ) const
{
LootStoreItem const * item = Roll ();
if ( item != NULL )
loot . AddItem ( * item );
}
// Overall chance for the group without equal chanced items
float LootTemplate :: LootGroup :: RawTotalChance () const
{
float result = 0 ;
for ( LootStoreItemList :: const_iterator i = ExplicitlyChanced . begin (); i != ExplicitlyChanced . end (); ++ i )
if ( ! i -> needs_quest )
result += i -> chance ;
return result ;
}
// Overall chance for the group
float LootTemplate :: LootGroup :: TotalChance () const
{
float result = RawTotalChance ();
if ( ! EqualChanced . empty () && result < 100.0f )
return 100.0f ;
return result ;
}
void LootTemplate :: LootGroup :: Verify ( LootStore const & lootstore , uint32 id , uint32 group_id ) const
{
float chance = RawTotalChance ();
if ( chance > 101.0f ) // TODO: replace with 100% when DBs will be ready
{
sLog . outErrorDb ( "Table '%s' entry %u group %d has total chance > 100%% (%f)" , lootstore . GetName (), id , group_id , chance );
}
if ( chance >= 100.0f && ! EqualChanced . empty ())
{
sLog . outErrorDb ( "Table '%s' entry %u group %d has items with chance=0%% but group total chance >= 100%% (%f)" , lootstore . GetName (), id , group_id , chance );
}
}
void LootTemplate :: LootGroup :: CheckLootRefs ( LootTemplateMap const & store , LootIdSet * ref_set ) const
{
for ( LootStoreItemList :: const_iterator ieItr = ExplicitlyChanced . begin (); ieItr != ExplicitlyChanced . end (); ++ ieItr )
{
if ( ieItr -> mincountOrRef < 0 )
{
if ( ! LootTemplates_Reference . GetLootFor ( - ieItr -> mincountOrRef ))
LootTemplates_Reference . ReportNotExistedId ( - ieItr -> mincountOrRef );
else if ( ref_set )
ref_set -> erase ( - ieItr -> mincountOrRef );
}
}
for ( LootStoreItemList :: const_iterator ieItr = EqualChanced . begin (); ieItr != EqualChanced . end (); ++ ieItr )
{
if ( ieItr -> mincountOrRef < 0 )
{
if ( ! LootTemplates_Reference . GetLootFor ( - ieItr -> mincountOrRef ))
LootTemplates_Reference . ReportNotExistedId ( - ieItr -> mincountOrRef );
else if ( ref_set )
ref_set -> erase ( - ieItr -> mincountOrRef );
}
}
}
//
// --------- LootTemplate ---------
//
// Adds an entry to the group (at loading stage)
void LootTemplate :: AddEntry ( LootStoreItem & item )
{
if ( item . group > 0 && item . mincountOrRef > 0 ) // Group
{
if ( item . group >= Groups . size ())
Groups . resize ( item . group ); // Adds new group the the loot template if needed
Groups [ item . group - 1 ]. AddEntry ( item ); // Adds new entry to the group
}
else // Non-grouped entries and references are stored together
Entries . push_back ( item );
}
// Rolls for every item in the template and adds the rolled items the the loot
void LootTemplate :: Process ( Loot & loot , LootStore const & store , uint8 groupId ) const
{
if ( groupId ) // Group reference uses own processing of the group
{
if ( groupId > Groups . size ())
return ; // Error message already printed at loading stage
Groups [ groupId - 1 ]. Process ( loot );
return ;
}
// Rolling non-grouped items
2008-12-08 16:34:03 -06:00
for ( LootStoreItemList :: const_iterator i = Entries . begin () ; i != Entries . end () ; ++ i )
2008-10-02 16:23:55 -05:00
{
if ( ! i -> Roll () )
continue ; // Bad luck for the entry
if ( i -> mincountOrRef < 0 ) // References processing
{
LootTemplate const * Referenced = LootTemplates_Reference . GetLootFor ( - i -> mincountOrRef );
if ( ! Referenced )
continue ; // Error message already printed at loading stage
for ( uint32 loop = 0 ; loop < i -> maxcount ; ++ loop ) // Ref multiplicator
Referenced -> Process ( loot , store , i -> group ); // Ref processing
}
else // Plain entries (not a reference, not grouped)
loot . AddItem ( * i ); // Chance is already checked, just add
}
// Now processing groups
2008-12-08 16:34:03 -06:00
for ( LootGroups :: const_iterator i = Groups . begin ( ) ; i != Groups . end ( ) ; ++ i )
2008-10-02 16:23:55 -05:00
i -> Process ( loot );
}
// True if template includes at least 1 quest drop entry
bool LootTemplate :: HasQuestDrop ( LootTemplateMap const & store , uint8 groupId ) const
{
if ( groupId ) // Group reference
{
if ( groupId > Groups . size ())
return false ; // Error message [should be] already printed at loading stage
return Groups [ groupId - 1 ]. HasQuestDrop ();
}
for ( LootStoreItemList :: const_iterator i = Entries . begin (); i != Entries . end (); ++ i )
{
if ( i -> mincountOrRef < 0 ) // References
{
LootTemplateMap :: const_iterator Referenced = store . find ( - i -> mincountOrRef );
if ( Referenced == store . end () )
continue ; // Error message [should be] already printed at loading stage
if ( Referenced -> second -> HasQuestDrop ( store , i -> group ) )
return true ;
}
else if ( i -> needs_quest )
return true ; // quest drop found
}
// Now processing groups
2008-12-08 16:34:03 -06:00
for ( LootGroups :: const_iterator i = Groups . begin () ; i != Groups . end () ; ++ i )
2008-10-02 16:23:55 -05:00
if ( i -> HasQuestDrop ())
return true ;
return false ;
}
// True if template includes at least 1 quest drop for an active quest of the player
bool LootTemplate :: HasQuestDropForPlayer ( LootTemplateMap const & store , Player const * player , uint8 groupId ) const
{
if ( groupId ) // Group reference
{
if ( groupId > Groups . size ())
return false ; // Error message already printed at loading stage
return Groups [ groupId - 1 ]. HasQuestDropForPlayer ( player );
}
// Checking non-grouped entries
2008-12-08 16:34:03 -06:00
for ( LootStoreItemList :: const_iterator i = Entries . begin () ; i != Entries . end () ; ++ i )
2008-10-02 16:23:55 -05:00
{
if ( i -> mincountOrRef < 0 ) // References processing
{
LootTemplateMap :: const_iterator Referenced = store . find ( - i -> mincountOrRef );
if ( Referenced == store . end () )
continue ; // Error message already printed at loading stage
if ( Referenced -> second -> HasQuestDropForPlayer ( store , player , i -> group ) )
return true ;
}
else if ( player -> HasQuestForItem ( i -> itemid ) )
return true ; // active quest drop found
}
// Now checking groups
for ( LootGroups :: const_iterator i = Groups . begin (); i != Groups . end (); ++ i )
if ( i -> HasQuestDrop ())
return true ;
return false ;
}
// Checks integrity of the template
void LootTemplate :: Verify ( LootStore const & lootstore , uint32 id ) const
{
// Checking group chances
for ( uint32 i = 0 ; i < Groups . size (); ++ i )
Groups [ i ]. Verify ( lootstore , id , i + 1 );
// TODO: References validity checks
}
void LootTemplate :: CheckLootRefs ( LootTemplateMap const & store , LootIdSet * ref_set ) const
{
for ( LootStoreItemList :: const_iterator ieItr = Entries . begin (); ieItr != Entries . end (); ++ ieItr )
{
if ( ieItr -> mincountOrRef < 0 )
{
if ( ! LootTemplates_Reference . GetLootFor ( - ieItr -> mincountOrRef ))
LootTemplates_Reference . ReportNotExistedId ( - ieItr -> mincountOrRef );
else if ( ref_set )
ref_set -> erase ( - ieItr -> mincountOrRef );
}
}
for ( LootGroups :: const_iterator grItr = Groups . begin (); grItr != Groups . end (); ++ grItr )
grItr -> CheckLootRefs ( store , ref_set );
}
void LoadLootTemplates_Creature ()
{
LootIdSet ids_set , ids_setUsed ;
LootTemplates_Creature . LoadAndCollectLootIds ( ids_set );
// remove real entries and check existence loot
for ( uint32 i = 1 ; i < sCreatureStorage . MaxEntry ; ++ i )
{
if ( CreatureInfo const * cInfo = sCreatureStorage . LookupEntry < CreatureInfo > ( i ))
{
if ( uint32 lootid = cInfo -> lootid )
{
if ( ! ids_set . count ( lootid ))
LootTemplates_Creature . ReportNotExistedId ( lootid );
else
ids_setUsed . insert ( lootid );
}
}
}
for ( LootIdSet :: const_iterator itr = ids_setUsed . begin (); itr != ids_setUsed . end (); ++ itr )
ids_set . erase ( * itr );
// output error for any still listed (not referenced from appropriate table) ids
LootTemplates_Creature . ReportUnusedIds ( ids_set );
}
void LoadLootTemplates_Disenchant ()
{
LootIdSet ids_set , ids_setUsed ;
LootTemplates_Disenchant . LoadAndCollectLootIds ( ids_set );
// remove real entries and check existence loot
for ( uint32 i = 1 ; i < sItemStorage . MaxEntry ; ++ i )
{
if ( ItemPrototype const * proto = sItemStorage . LookupEntry < ItemPrototype > ( i ))
{
if ( uint32 lootid = proto -> DisenchantID )
{
if ( ! ids_set . count ( lootid ))
LootTemplates_Disenchant . ReportNotExistedId ( lootid );
else
ids_setUsed . insert ( lootid );
}
}
}
for ( LootIdSet :: const_iterator itr = ids_setUsed . begin (); itr != ids_setUsed . end (); ++ itr )
ids_set . erase ( * itr );
// output error for any still listed (not referenced from appropriate table) ids
LootTemplates_Disenchant . ReportUnusedIds ( ids_set );
}
void LoadLootTemplates_Fishing ()
{
LootIdSet ids_set ;
LootTemplates_Fishing . LoadAndCollectLootIds ( ids_set );
// remove real entries and check existence loot
for ( uint32 i = 1 ; i < sAreaStore . GetNumRows (); ++ i )
{
if ( AreaTableEntry const * areaEntry = sAreaStore . LookupEntry ( i ))
if ( ids_set . count ( areaEntry -> ID ))
ids_set . erase ( areaEntry -> ID );
}
// output error for any still listed (not referenced from appropriate table) ids
LootTemplates_Fishing . ReportUnusedIds ( ids_set );
}
void LoadLootTemplates_Gameobject ()
{
LootIdSet ids_set , ids_setUsed ;
LootTemplates_Gameobject . LoadAndCollectLootIds ( ids_set );
// remove real entries and check existence loot
for ( uint32 i = 1 ; i < sGOStorage . MaxEntry ; ++ i )
{
if ( GameObjectInfo const * gInfo = sGOStorage . LookupEntry < GameObjectInfo > ( i ))
{
if ( uint32 lootid = GameObject :: GetLootId ( gInfo ))
{
if ( ! ids_set . count ( lootid ))
LootTemplates_Gameobject . ReportNotExistedId ( lootid );
else
ids_setUsed . insert ( lootid );
}
}
}
for ( LootIdSet :: const_iterator itr = ids_setUsed . begin (); itr != ids_setUsed . end (); ++ itr )
ids_set . erase ( * itr );
// output error for any still listed (not referenced from appropriate table) ids
LootTemplates_Gameobject . ReportUnusedIds ( ids_set );
}
void LoadLootTemplates_Item ()
{
LootIdSet ids_set ;
LootTemplates_Item . LoadAndCollectLootIds ( ids_set );
// remove real entries and check existence loot
for ( uint32 i = 1 ; i < sItemStorage . MaxEntry ; ++ i )
if ( ItemPrototype const * proto = sItemStorage . LookupEntry < ItemPrototype > ( i ))
if ( ids_set . count ( proto -> ItemId ))
ids_set . erase ( proto -> ItemId );
// output error for any still listed (not referenced from appropriate table) ids
LootTemplates_Item . ReportUnusedIds ( ids_set );
}
void LoadLootTemplates_Pickpocketing ()
{
LootIdSet ids_set , ids_setUsed ;
LootTemplates_Pickpocketing . LoadAndCollectLootIds ( ids_set );
// remove real entries and check existence loot
for ( uint32 i = 1 ; i < sCreatureStorage . MaxEntry ; ++ i )
{
if ( CreatureInfo const * cInfo = sCreatureStorage . LookupEntry < CreatureInfo > ( i ))
{
if ( uint32 lootid = cInfo -> pickpocketLootId )
{
if ( ! ids_set . count ( lootid ))
LootTemplates_Pickpocketing . ReportNotExistedId ( lootid );
else
ids_setUsed . insert ( lootid );
}
}
}
for ( LootIdSet :: const_iterator itr = ids_setUsed . begin (); itr != ids_setUsed . end (); ++ itr )
ids_set . erase ( * itr );
// output error for any still listed (not referenced from appropriate table) ids
LootTemplates_Pickpocketing . ReportUnusedIds ( ids_set );
}
void LoadLootTemplates_Prospecting ()
{
LootIdSet ids_set ;
LootTemplates_Prospecting . LoadAndCollectLootIds ( ids_set );
// remove real entries and check existence loot
for ( uint32 i = 1 ; i < sItemStorage . MaxEntry ; ++ i )
if ( ItemPrototype const * proto = sItemStorage . LookupEntry < ItemPrototype > ( i ))
if ( ids_set . count ( proto -> ItemId ))
ids_set . erase ( proto -> ItemId );
// output error for any still listed (not referenced from appropriate table) ids
LootTemplates_Prospecting . ReportUnusedIds ( ids_set );
}
void LoadLootTemplates_QuestMail ()
{
LootIdSet ids_set ;
LootTemplates_QuestMail . LoadAndCollectLootIds ( ids_set );
// remove real entries and check existence loot
ObjectMgr :: QuestMap const & questMap = objmgr . GetQuestTemplates ();
for ( ObjectMgr :: QuestMap :: const_iterator itr = questMap . begin (); itr != questMap . end (); ++ itr )
if ( ids_set . count ( itr -> first ))
ids_set . erase ( itr -> first );
// output error for any still listed (not referenced from appropriate table) ids
LootTemplates_QuestMail . ReportUnusedIds ( ids_set );
}
void LoadLootTemplates_Skinning ()
{
LootIdSet ids_set , ids_setUsed ;
LootTemplates_Skinning . LoadAndCollectLootIds ( ids_set );
// remove real entries and check existence loot
for ( uint32 i = 1 ; i < sCreatureStorage . MaxEntry ; ++ i )
{
if ( CreatureInfo const * cInfo = sCreatureStorage . LookupEntry < CreatureInfo > ( i ))
{
if ( uint32 lootid = cInfo -> SkinLootId )
{
if ( ! ids_set . count ( lootid ))
LootTemplates_Skinning . ReportNotExistedId ( lootid );
else
ids_setUsed . insert ( lootid );
}
}
}
for ( LootIdSet :: const_iterator itr = ids_setUsed . begin (); itr != ids_setUsed . end (); ++ itr )
ids_set . erase ( * itr );
// output error for any still listed (not referenced from appropriate table) ids
LootTemplates_Skinning . ReportUnusedIds ( ids_set );
}
void LoadLootTemplates_Reference ()
{
LootIdSet ids_set ;
LootTemplates_Reference . LoadAndCollectLootIds ( ids_set );
// check references and remove used
LootTemplates_Creature . CheckLootRefs ( & ids_set );
LootTemplates_Fishing . CheckLootRefs ( & ids_set );
LootTemplates_Gameobject . CheckLootRefs ( & ids_set );
LootTemplates_Item . CheckLootRefs ( & ids_set );
LootTemplates_Pickpocketing . CheckLootRefs ( & ids_set );
LootTemplates_Skinning . CheckLootRefs ( & ids_set );
LootTemplates_Disenchant . CheckLootRefs ( & ids_set );
LootTemplates_Prospecting . CheckLootRefs ( & ids_set );
LootTemplates_QuestMail . CheckLootRefs ( & ids_set );
LootTemplates_Reference . CheckLootRefs ( & ids_set );
// output error for any still listed ids (not referenced from any loot table)
LootTemplates_Reference . ReportUnusedIds ( ids_set );
}