mirror of
https://github.com/TrinityCore/TrinityCore.git
synced 2026-01-16 15:40:45 +01:00
Core/Loot: fix the way quest items are handled. so far ive only found 3 quest items that should be able to be masterlooted. added a new flag to item_template.flagsCustom to allow for making a quest item always follow loot rules. a bonus may be the fix of the handlelootmasteropcode crash!
This commit is contained in:
1
sql/updates/world/2012_07_05_00_world_item_template.sql
Normal file
1
sql/updates/world/2012_07_05_00_world_item_template.sql
Normal file
@@ -0,0 +1 @@
|
||||
UPDATE `item_template` SET `flagsCustom` = `flagsCustom` | 3 WHERE `entry` IN (50226,50231,50274);
|
||||
@@ -196,6 +196,7 @@ enum ItemFlagsCustom
|
||||
{
|
||||
ITEM_FLAGS_CU_DURATION_REAL_TIME = 0x0001, // Item duration will tick even if player is offline
|
||||
ITEM_FLAGS_CU_IGNORE_QUEST_STATUS = 0x0002, // No quest status will be checked when this item drops
|
||||
ITEM_FLAGS_CU_FOLLOW_LOOT_RULES = 0x0003, // Item will always follow group/master/need before greed looting rules
|
||||
};
|
||||
|
||||
enum BAG_FAMILY_MASK
|
||||
|
||||
@@ -949,6 +949,64 @@ void Group::GroupLoot(Loot* loot, WorldObject* pLootedObject)
|
||||
else
|
||||
i->is_underthreshold = true;
|
||||
}
|
||||
|
||||
for (i = loot->quest_items.begin(); i != loot->quest_items.end(); ++i, ++itemSlot)
|
||||
{
|
||||
if (!i->follow_loot_rules)
|
||||
continue;
|
||||
|
||||
item = sObjectMgr->GetItemTemplate(i->itemid);
|
||||
if (!item)
|
||||
{
|
||||
//sLog->outDebug("Group::GroupLoot: missing item prototype for item with id: %d", i->itemid);
|
||||
continue;
|
||||
}
|
||||
|
||||
uint64 newitemGUID = MAKE_NEW_GUID(sObjectMgr->GenerateLowGuid(HIGHGUID_ITEM), 0, HIGHGUID_ITEM);
|
||||
Roll* r = new Roll(newitemGUID, *i);
|
||||
|
||||
//a vector is filled with only near party members
|
||||
for (GroupReference* itr = GetFirstMember(); itr != NULL; itr = itr->next())
|
||||
{
|
||||
Player* member = itr->getSource();
|
||||
if (!member || !member->GetSession())
|
||||
continue;
|
||||
|
||||
if (i->AllowedForPlayer(member))
|
||||
{
|
||||
if (member->IsWithinDistInMap(pLootedObject, sWorld->getFloatConfig(CONFIG_GROUP_XP_DISTANCE), false))
|
||||
{
|
||||
r->totalPlayersRolling++;
|
||||
r->playerVote[member->GetGUID()] = NOT_EMITED_YET;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (r->totalPlayersRolling > 0)
|
||||
{
|
||||
r->setLoot(loot);
|
||||
r->itemSlot = itemSlot;
|
||||
|
||||
loot->quest_items[itemSlot - loot->items.size()].is_blocked = true;
|
||||
|
||||
SendLootStartRoll(60000, pLootedObject->GetMapId(), *r);
|
||||
|
||||
RollId.push_back(r);
|
||||
|
||||
if (Creature* creature = pLootedObject->ToCreature())
|
||||
{
|
||||
creature->m_groupLootTimer = 60000;
|
||||
creature->lootingGroupLowGUID = GetLowGUID();
|
||||
}
|
||||
else if (GameObject* go = pLootedObject->ToGameObject())
|
||||
{
|
||||
go->m_groupLootTimer = 60000;
|
||||
go->lootingGroupLowGUID = GetLowGUID();
|
||||
}
|
||||
}
|
||||
else
|
||||
delete r;
|
||||
}
|
||||
}
|
||||
|
||||
void Group::NeedBeforeGreed(Loot* loot, WorldObject* lootedObject)
|
||||
@@ -1033,6 +1091,66 @@ void Group::NeedBeforeGreed(Loot* loot, WorldObject* lootedObject)
|
||||
else
|
||||
i->is_underthreshold = true;
|
||||
}
|
||||
|
||||
for (std::vector<LootItem>::iterator i = loot->quest_items.begin(); i != loot->quest_items.end(); ++i, ++itemSlot)
|
||||
{
|
||||
if (!i->follow_loot_rules)
|
||||
continue;
|
||||
|
||||
item = sObjectMgr->GetItemTemplate(i->itemid);
|
||||
uint64 newitemGUID = MAKE_NEW_GUID(sObjectMgr->GenerateLowGuid(HIGHGUID_ITEM), 0, HIGHGUID_ITEM);
|
||||
Roll* r = new Roll(newitemGUID, *i);
|
||||
|
||||
for (GroupReference* itr = GetFirstMember(); itr != NULL; itr = itr->next())
|
||||
{
|
||||
Player* playerToRoll = itr->getSource();
|
||||
if (!playerToRoll || !playerToRoll->GetSession())
|
||||
continue;
|
||||
|
||||
bool allowedForPlayer = i->AllowedForPlayer(playerToRoll);
|
||||
if (allowedForPlayer && playerToRoll->IsWithinDistInMap(lootedObject, sWorld->getFloatConfig(CONFIG_GROUP_XP_DISTANCE), false))
|
||||
{
|
||||
r->totalPlayersRolling++;
|
||||
r->playerVote[playerToRoll->GetGUID()] = NOT_EMITED_YET;
|
||||
}
|
||||
}
|
||||
|
||||
if (r->totalPlayersRolling > 0)
|
||||
{
|
||||
r->setLoot(loot);
|
||||
r->itemSlot = itemSlot;
|
||||
|
||||
loot->quest_items[itemSlot - loot->items.size()].is_blocked = true;
|
||||
|
||||
//Broadcast Pass and Send Rollstart
|
||||
for (Roll::PlayerVote::const_iterator itr = r->playerVote.begin(); itr != r->playerVote.end(); ++itr)
|
||||
{
|
||||
Player* p = ObjectAccessor::FindPlayer(itr->first);
|
||||
if (!p || !p->GetSession())
|
||||
continue;
|
||||
|
||||
if (itr->second == PASS)
|
||||
SendLootRoll(newitemGUID, p->GetGUID(), 128, ROLL_PASS, *r);
|
||||
else
|
||||
SendLootStartRollToPlayer(60000, lootedObject->GetMapId(), p, p->CanRollForItemInLFG(item, lootedObject) == EQUIP_ERR_OK, *r);
|
||||
}
|
||||
|
||||
RollId.push_back(r);
|
||||
|
||||
if (Creature* creature = lootedObject->ToCreature())
|
||||
{
|
||||
creature->m_groupLootTimer = 60000;
|
||||
creature->lootingGroupLowGUID = GetLowGUID();
|
||||
}
|
||||
else if (GameObject* go = lootedObject->ToGameObject())
|
||||
{
|
||||
go->m_groupLootTimer = 60000;
|
||||
go->lootingGroupLowGUID = GetLowGUID();
|
||||
}
|
||||
}
|
||||
else
|
||||
delete r;
|
||||
}
|
||||
}
|
||||
|
||||
void Group::MasterLoot(Loot* /*loot*/, WorldObject* pLootedObject)
|
||||
|
||||
@@ -472,16 +472,18 @@ void WorldSession::HandleLootMasterGiveOpcode(WorldPacket & recv_data)
|
||||
if (!loot)
|
||||
return;
|
||||
|
||||
if (slotid > loot->items.size())
|
||||
if (slotid > loot->items.size() + loot->quest_items.size())
|
||||
{
|
||||
sLog->outDebug(LOG_FILTER_LOOT, "MasterLootItem: Player %s might be using a hack! (slot %d, size %lu)", GetPlayer()->GetName(), slotid, (unsigned long)loot->items.size());
|
||||
return;
|
||||
}
|
||||
|
||||
LootItem& item = loot->items[slotid];
|
||||
LootItem& item = slotid > loot->items.size() ? loot->quest_items[slotid - loot->items.size()] : loot->items[slotid];
|
||||
|
||||
ItemPosCountVec dest;
|
||||
InventoryResult msg = target->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, item.itemid, item.count);
|
||||
if (item.follow_loot_rules && !item.AllowedForPlayer(target))
|
||||
msg = EQUIP_ERR_YOU_CAN_NEVER_USE_THAT_ITEM;
|
||||
if (msg != EQUIP_ERR_OK)
|
||||
{
|
||||
target->SendEquipError(msg, NULL, NULL, item.itemid);
|
||||
|
||||
@@ -322,6 +322,7 @@ LootItem::LootItem(LootStoreItem const& li)
|
||||
|
||||
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemid);
|
||||
freeforall = proto && (proto->Flags & ITEM_PROTO_FLAG_PARTY_LOOT);
|
||||
follow_loot_rules = proto && (proto->FlagsCu & ITEM_FLAGS_CU_FOLLOW_LOOT_RULES);
|
||||
|
||||
needs_quest = li.needs_quest;
|
||||
|
||||
@@ -358,12 +359,7 @@ bool LootItem::AllowedForPlayer(Player const* player) const
|
||||
|
||||
// check quest requirements
|
||||
if (!(pProto->FlagsCu & ITEM_FLAGS_CU_IGNORE_QUEST_STATUS) && ((needs_quest || (pProto->StartQuest && player->GetQuestStatus(pProto->StartQuest) != QUEST_STATUS_NONE)) && !player->HasQuestForItem(itemid)))
|
||||
if (Group const* group = player->GetGroup())
|
||||
{
|
||||
if (pProto->Flags & ITEM_PROTO_FLAG_PARTY_LOOT || ((pProto->Flags & ITEM_PROTO_FLAG_PARTY_LOOT) == 0 && (group->GetLootMethod() != MASTER_LOOT || group->GetLooterGuid() != player->GetGUID())))
|
||||
return false;
|
||||
}
|
||||
else return false;
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -517,18 +513,20 @@ QuestItemList* Loot::FillQuestLoot(Player* player)
|
||||
for (uint8 i = 0; i < quest_items.size(); ++i)
|
||||
{
|
||||
LootItem &item = quest_items[i];
|
||||
if (!item.is_looted && item.AllowedForPlayer(player))
|
||||
|
||||
if ((!item.is_looted && item.AllowedForPlayer(player)) || (item.follow_loot_rules && player->GetGroup() && player->GetGroup()->GetLootMethod() == MASTER_LOOT && player->GetGroup()->GetLooterGuid() == player->GetGUID()))
|
||||
{
|
||||
ql->push_back(QuestItem(i));
|
||||
|
||||
// questitems get blocked when they first appear in a
|
||||
// quest items get blocked when they first appear in a
|
||||
// player's quest vector
|
||||
//
|
||||
// increase once if one looter only, looter-times if free for all
|
||||
if (item.freeforall || !item.is_blocked)
|
||||
if (item.freeforall && !item.is_blocked)
|
||||
{
|
||||
++unlootedCount;
|
||||
|
||||
item.is_blocked = true;
|
||||
item.is_blocked = true;
|
||||
}
|
||||
|
||||
if (items.size() + ql->size() == MAX_NR_LOOT_ITEMS)
|
||||
break;
|
||||
@@ -551,7 +549,7 @@ QuestItemList* Loot::FillNonQuestNonFFAConditionalLoot(Player* player, bool pres
|
||||
for (uint8 i = 0; i < items.size(); ++i)
|
||||
{
|
||||
LootItem &item = items[i];
|
||||
if (!item.is_looted && !item.freeforall && item.AllowedForPlayer(player))
|
||||
if (!item.is_looted && !item.freeforall && (item.AllowedForPlayer(player)) || (item.follow_loot_rules && player->GetGroup() && player->GetGroup()->GetLootMethod() == MASTER_LOOT && player->GetGroup()->GetLooterGuid() == player->GetGUID()))
|
||||
{
|
||||
if (presentAtLooting)
|
||||
item.AddAllowedLooter(player);
|
||||
@@ -907,8 +905,24 @@ ByteBuffer& operator<<(ByteBuffer& b, LootView const& lv)
|
||||
{
|
||||
b << uint8(l.items.size() + (qi - q_list->begin()));
|
||||
b << item;
|
||||
if (!item.freeforall)
|
||||
b << uint8(partySlotType);
|
||||
if (item.follow_loot_rules)
|
||||
{
|
||||
switch (lv.permission)
|
||||
{
|
||||
case MASTER_PERMISSION:
|
||||
b << uint8(LOOT_SLOT_TYPE_MASTER);
|
||||
break;
|
||||
case GROUP_PERMISSION:
|
||||
if (!item.is_blocked)
|
||||
b << uint8(LOOT_SLOT_TYPE_ALLOW_LOOT);
|
||||
else
|
||||
b << uint8(LOOT_SLOT_TYPE_ROLL_ONGOING);
|
||||
break;
|
||||
default:
|
||||
b << uint8(slotType);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
b << uint8(slotType);
|
||||
++itemsShown;
|
||||
@@ -928,10 +942,7 @@ ByteBuffer& operator<<(ByteBuffer& b, LootView const& lv)
|
||||
{
|
||||
b << uint8(fi->index);
|
||||
b << item;
|
||||
if (!item.freeforall)
|
||||
b << uint8(partySlotType);
|
||||
else
|
||||
b << uint8(slotType);
|
||||
b << uint8(slotType);
|
||||
++itemsShown;
|
||||
}
|
||||
}
|
||||
@@ -949,8 +960,24 @@ ByteBuffer& operator<<(ByteBuffer& b, LootView const& lv)
|
||||
{
|
||||
b << uint8(ci->index);
|
||||
b << item;
|
||||
if (!item.freeforall)
|
||||
b << uint8(partySlotType);
|
||||
if (item.follow_loot_rules)
|
||||
{
|
||||
switch (lv.permission)
|
||||
{
|
||||
case MASTER_PERMISSION:
|
||||
b << uint8(LOOT_SLOT_TYPE_MASTER);
|
||||
break;
|
||||
case GROUP_PERMISSION:
|
||||
if (!item.is_blocked)
|
||||
b << uint8(LOOT_SLOT_TYPE_ALLOW_LOOT);
|
||||
else
|
||||
b << uint8(LOOT_SLOT_TYPE_ROLL_ONGOING);
|
||||
break;
|
||||
default:
|
||||
b << uint8(slotType);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
b << uint8(slotType);
|
||||
++itemsShown;
|
||||
|
||||
@@ -140,6 +140,7 @@ struct LootItem
|
||||
bool is_underthreshold : 1;
|
||||
bool is_counted : 1;
|
||||
bool needs_quest : 1; // quest drop
|
||||
bool follow_loot_rules : 1;
|
||||
|
||||
// Constructor, copies most fields from LootStoreItem, generates random count and random suffixes/properties
|
||||
// Should be called for non-reference LootStoreItem entries only (mincountOrRef > 0)
|
||||
@@ -280,6 +281,7 @@ struct Loot
|
||||
QuestItemMap const& GetPlayerNonQuestNonFFAConditionalItems() const { return PlayerNonQuestNonFFAConditionalItems; }
|
||||
|
||||
std::vector<LootItem> items;
|
||||
std::vector<LootItem> quest_items;
|
||||
uint32 gold;
|
||||
uint8 unlootedCount;
|
||||
uint64 roundRobinPlayer; // GUID of the player having the Round-Robin ownership for the loot. If 0, round robin owner has released.
|
||||
@@ -344,7 +346,6 @@ struct Loot
|
||||
QuestItemList* FillQuestLoot(Player* player);
|
||||
QuestItemList* FillNonQuestNonFFAConditionalLoot(Player* player, bool presentAtLooting);
|
||||
|
||||
std::vector<LootItem> quest_items;
|
||||
std::set<uint64> PlayersLooting;
|
||||
QuestItemMap PlayerQuestItems;
|
||||
QuestItemMap PlayerFFAItems;
|
||||
|
||||
Reference in New Issue
Block a user