Core/Loot: Move loot rolls from Group to Loot

Port From (https://github.com/TrinityCore/TrinityCore/commit/3ef5079feeedfdafc9d3c1d9f865e96dbc77ecc8)
This commit is contained in:
hondacrx
2022-09-18 17:57:08 -04:00
parent 0f77a9b27d
commit b76c9571d2
19 changed files with 803 additions and 932 deletions
+715 -98
View File
@@ -16,14 +16,15 @@
*/
using Framework.Constants;
using Framework.Dynamic;
using Game.Conditions;
using Game.DataStorage;
using Game.Entities;
using Game.Groups;
using Game.Maps;
using Game.Networking.Packets;
using System;
using System.Collections.Generic;
using Game.Maps;
using System.Linq;
namespace Game.Loots
{
@@ -37,7 +38,7 @@ namespace Game.Loots
ItemTemplate proto = Global.ObjectMgr.GetItemTemplate(itemid);
freeforall = proto != null && proto.HasFlag(ItemFlags.MultiDrop);
follow_loot_rules = proto != null && proto.FlagsCu.HasAnyFlag(ItemFlagsCustom.FollowLootRules);
follow_loot_rules = !li.needs_quest || (proto != null && proto.FlagsCu.HasAnyFlag(ItemFlagsCustom.FollowLootRules));
needs_quest = li.needs_quest;
@@ -110,7 +111,7 @@ namespace Game.Loots
public List<ObjectGuid> GetAllowedLooters() { return allowedGUIDs; }
public uint itemid;
public uint itemIndex;
public uint LootListId;
public uint randomBonusListId;
public List<uint> BonusListIDs = new();
public ItemContext context;
@@ -145,35 +146,450 @@ namespace Game.Loots
}
}
public class LootValidatorRef : Reference<Loot, LootValidatorRef>
public class PlayerRollVote
{
public override void TargetObjectDestroyLink()
public RollVote Vote;
public byte RollNumber;
public PlayerRollVote()
{
}
public override void SourceObjectDestroyLink()
{
Vote = RollVote.NotValid;
RollNumber = 0;
}
}
public class LootValidatorRefManager : RefManager<Loot, LootValidatorRef>
public class LootRoll
{
public new LootValidatorRef GetFirst() { return (LootValidatorRef)base.GetFirst(); }
public new LootValidatorRef GetLast() { return (LootValidatorRef)base.GetLast(); }
static TimeSpan LOOT_ROLL_TIMEOUT = TimeSpan.FromMinutes(1);
Map m_map;
Dictionary<ObjectGuid, PlayerRollVote> m_rollVoteMap = new();
bool m_isStarted;
LootItem m_lootItem;
Loot m_loot;
uint m_lootListId;
RollMask m_voteMask;
DateTime m_endTime = DateTime.MinValue;
~LootRoll()
{
if (m_isStarted)
SendAllPassed();
foreach (var (playerGuid, roll) in m_rollVoteMap)
{
if (roll.Vote != RollVote.NotEmitedYet)
continue;
Player player = Global.ObjAccessor.GetPlayer(m_map, playerGuid);
if (!player)
continue;
player.RemoveLootRoll(this);
}
}
// Send the roll for the whole group
void SendStartRoll()
{
ItemTemplate itemTemplate = Global.ObjectMgr.GetItemTemplate(m_lootItem.itemid);
foreach (var (playerGuid, roll) in m_rollVoteMap)
{
if (roll.Vote != RollVote.NotEmitedYet)
continue;
Player player = Global.ObjAccessor.GetPlayer(m_map, playerGuid);
if (player == null)
continue;
StartLootRoll startLootRoll = new();
startLootRoll.LootObj = m_loot.GetGUID();
startLootRoll.MapID = (int)m_map.GetId();
startLootRoll.RollTime = (uint)LOOT_ROLL_TIMEOUT.TotalMilliseconds;
startLootRoll.Method = m_loot.GetLootMethod();
startLootRoll.ValidRolls = m_voteMask;
// In NEED_BEFORE_GREED need disabled for non-usable item for player
if (m_loot.GetLootMethod() == LootMethod.NeedBeforeGreed && player.CanRollForItemInLFG(itemTemplate, m_map) != InventoryResult.Ok)
startLootRoll.ValidRolls &= ~RollMask.Need;
FillPacket(startLootRoll.Item);
startLootRoll.Item.UIType = LootSlotType.RollOngoing;
player.SendPacket(startLootRoll);
}
// Handle auto pass option
foreach (var (playerGuid, roll) in m_rollVoteMap)
{
if (roll.Vote != RollVote.Pass)
continue;
SendRoll(playerGuid, -1, RollVote.Pass, null);
}
}
// Send all passed message
void SendAllPassed()
{
LootAllPassed lootAllPassed = new();
lootAllPassed.LootObj = m_loot.GetGUID();
FillPacket(lootAllPassed.Item);
lootAllPassed.Item.UIType = LootSlotType.AllowLoot;
lootAllPassed.Write();
foreach (var (playerGuid, roll) in m_rollVoteMap)
{
if (roll.Vote != RollVote.NotValid)
continue;
Player player = Global.ObjAccessor.GetPlayer(m_map, playerGuid);
if (player == null)
continue;
player.SendPacket(lootAllPassed);
}
}
// Send roll of targetGuid to the whole group (included targuetGuid)
void SendRoll(ObjectGuid targetGuid, int rollNumber, RollVote rollType, ObjectGuid? rollWinner)
{
LootRollBroadcast lootRoll = new();
lootRoll.LootObj = m_loot.GetGUID();
lootRoll.Player = targetGuid;
lootRoll.Roll = rollNumber;
lootRoll.RollType = rollType;
lootRoll.Autopassed = false;
FillPacket(lootRoll.Item);
lootRoll.Item.UIType = LootSlotType.RollOngoing;
lootRoll.Write();
foreach (var (playerGuid, roll) in m_rollVoteMap)
{
if (roll.Vote == RollVote.NotValid)
continue;
if (playerGuid == rollWinner)
continue;
Player player = Global.ObjAccessor.GetPlayer(m_map, playerGuid);
if (player == null)
continue;
player.SendPacket(lootRoll);
}
if (rollWinner.HasValue)
{
Player player = Global.ObjAccessor.GetPlayer(m_map, rollWinner.Value);
if (player != null)
{
lootRoll.Item.UIType = LootSlotType.AllowLoot;
lootRoll.Clear();
player.SendPacket(lootRoll);
}
}
}
// Send roll 'value' of the whole group and the winner to the whole group
void SendLootRollWon(ObjectGuid targetGuid, int rollNumber, RollVote rollType)
{
// Send roll values
foreach (var (playerGuid, roll) in m_rollVoteMap)
{
switch (roll.Vote)
{
case RollVote.Pass:
break;
case RollVote.NotEmitedYet:
case RollVote.NotValid:
SendRoll(playerGuid, 0, RollVote.Pass, targetGuid);
break;
default:
SendRoll(playerGuid, roll.RollNumber, roll.Vote, targetGuid);
break;
}
}
LootRollWon lootRollWon = new();
lootRollWon.LootObj = m_loot.GetGUID();
lootRollWon.Winner = targetGuid;
lootRollWon.Roll = rollNumber;
lootRollWon.RollType = rollType;
FillPacket(lootRollWon.Item);
lootRollWon.Item.UIType = LootSlotType.Locked;
lootRollWon.MainSpec = true; // offspec rolls not implemented
lootRollWon.Write();
foreach (var (playerGuid, roll) in m_rollVoteMap)
{
if (roll.Vote == RollVote.NotValid)
continue;
if (playerGuid == targetGuid)
continue;
Player player1 = Global.ObjAccessor.GetPlayer(m_map, playerGuid);
if (player1 == null)
continue;
player1.SendPacket(lootRollWon);
}
Player player = Global.ObjAccessor.GetPlayer(m_map, targetGuid);
if (player != null)
{
lootRollWon.Item.UIType = LootSlotType.AllowLoot;
lootRollWon.Clear();
player.SendPacket(lootRollWon);
}
}
void FillPacket(LootItemData lootItem)
{
lootItem.Quantity = m_lootItem.count;
lootItem.LootListID = (byte)(m_lootListId + 1);
lootItem.CanTradeToTapList = m_lootItem.allowedGUIDs.Count > 1;
lootItem.Loot = new(m_lootItem);
}
// Try to start the group roll for the specified item (it may fail for quest item or any condition
// If this method return false the roll have to be removed from the container to avoid any problem
public bool TryToStart(Map map, Loot loot, uint lootListId, ushort enchantingSkill)
{
if (!m_isStarted)
{
if (lootListId >= loot.items.Count)
return false;
m_map = map;
// initialize the data needed for the roll
m_lootItem = loot.items[(int)lootListId];
m_loot = loot;
m_lootListId = lootListId;
m_lootItem.is_blocked = true; // block the item while rolling
uint playerCount = 0;
foreach (ObjectGuid allowedLooter in m_lootItem.GetAllowedLooters())
{
Player plr = Global.ObjAccessor.GetPlayer(m_map, allowedLooter);
if (!plr || !m_lootItem.AllowedForPlayer(plr)) // check if player meet the condition to be able to roll this item
{
m_rollVoteMap[allowedLooter].Vote = RollVote.NotValid;
continue;
}
// initialize player vote map
m_rollVoteMap[allowedLooter].Vote = plr.GetPassOnGroupLoot() ? RollVote.Pass : RollVote.NotEmitedYet;
if (!plr.GetPassOnGroupLoot())
plr.AddLootRoll(this);
++playerCount;
}
// initialize item prototype and check enchant possibilities for this group
ItemTemplate itemTemplate = Global.ObjectMgr.GetItemTemplate(m_lootItem.itemid);
m_voteMask = RollMask.AllMask;
if (itemTemplate.HasFlag(ItemFlags2.CanOnlyRollGreed))
m_voteMask = m_voteMask & ~RollMask.Need;
var disenchant = GetItemDisenchantLoot();
if (disenchant == null || disenchant.SkillRequired > enchantingSkill)
m_voteMask = m_voteMask & ~RollMask.Disenchant;
if (playerCount > 1) // check if more than one player can loot this item
{
// start the roll
SendStartRoll();
m_endTime = GameTime.Now() + LOOT_ROLL_TIMEOUT;
m_isStarted = true;
return true;
}
// no need to start roll if one or less player can loot this item so place it under threshold
m_lootItem.is_underthreshold = true;
m_lootItem.is_blocked = false;
}
return false;
}
// Add vote from playerGuid
public bool PlayerVote(Player player, RollVote vote)
{
ObjectGuid playerGuid = player.GetGUID();
if (!m_rollVoteMap.TryGetValue(playerGuid, out PlayerRollVote voter))
return false;
voter.Vote = vote;
if (vote != RollVote.Pass && vote != RollVote.NotValid)
voter.RollNumber = (byte)RandomHelper.URand(1, 100);
switch (vote)
{
case RollVote.Pass: // Player choose pass
{
SendRoll(playerGuid, -1, RollVote.Pass, null);
break;
}
case RollVote.Need: // player choose Need
{
SendRoll(playerGuid, 0, RollVote.Need, null);
player.UpdateCriteria(CriteriaType.RollAnyNeed, 1);
break;
}
case RollVote.Greed: // player choose Greed
{
SendRoll(playerGuid, -1, RollVote.Greed, null);
player.UpdateCriteria(CriteriaType.RollAnyGreed, 1);
break;
}
case RollVote.Disenchant: // player choose Disenchant
{
SendRoll(playerGuid, -1, RollVote.Disenchant, null);
player.UpdateCriteria(CriteriaType.RollAnyGreed, 1);
break;
}
default: // Roll removed case
return false;
}
return true;
}
// check if we can found a winner for this roll or if timer is expired
public bool UpdateRoll()
{
KeyValuePair<ObjectGuid, PlayerRollVote> winner = default;
if (AllPlayerVoted(ref winner) || m_endTime <= GameTime.Now())
{
Finish(winner);
return true;
}
return false;
}
public bool IsLootItem(ObjectGuid lootObject, uint lootListId)
{
return m_loot.GetGUID() == lootObject && m_lootListId == lootListId;
}
/**
* \brief Check if all player have voted and return true in that case. Also return current winner.
* \param winnerItr > will be different than m_rollCoteMap.end() if winner exist. (Someone voted greed or need)
* \returns true if all players voted
**/
bool AllPlayerVoted(ref KeyValuePair<ObjectGuid, PlayerRollVote> winnerPair)
{
uint notVoted = 0;
bool isSomeoneNeed = false;
winnerPair = default;
foreach (var pair in m_rollVoteMap)
{
switch (pair.Value.Vote)
{
case RollVote.Need:
if (!isSomeoneNeed || winnerPair.Value == null || pair.Value.RollNumber > winnerPair.Value.RollNumber)
{
isSomeoneNeed = true; // first passage will force to set winner because need is prioritized
winnerPair = pair;
}
break;
case RollVote.Greed:
case RollVote.Disenchant:
if (!isSomeoneNeed) // if at least one need is detected then winner can't be a greed
{
if (winnerPair.Value == null || pair.Value.RollNumber > winnerPair.Value.RollNumber)
winnerPair = pair;
}
break;
// Explicitly passing excludes a player from winning loot, so no action required.
case RollVote.Pass:
break;
case RollVote.NotEmitedYet:
++notVoted;
break;
default:
break;
}
}
return notVoted == 0;
}
ItemDisenchantLootRecord GetItemDisenchantLoot()
{
ItemInstance itemInstance = new(m_lootItem);
BonusData bonusData = new(itemInstance);
if (!bonusData.CanDisenchant)
return null;
ItemTemplate itemTemplate = Global.ObjectMgr.GetItemTemplate(m_lootItem.itemid);
uint itemLevel = Item.GetItemLevel(itemTemplate, bonusData, 1, 0, 0, 0, 0, false, 0);
return Item.GetDisenchantLoot(itemTemplate, (uint)bonusData.Quality, itemLevel);
}
// terminate the roll
void Finish(KeyValuePair<ObjectGuid, PlayerRollVote> winnerPair)
{
m_lootItem.is_blocked = false;
if (winnerPair.Value == null)
{
SendAllPassed();
}
else
{
m_lootItem.rollWinnerGUID = winnerPair.Key;
SendLootRollWon(winnerPair.Key, winnerPair.Value.RollNumber, winnerPair.Value.Vote);
Player player = Global.ObjAccessor.FindConnectedPlayer(winnerPair.Key);
if (player != null)
{
if (winnerPair.Value.Vote == RollVote.Need)
player.UpdateCriteria(CriteriaType.RollNeed, m_lootItem.itemid, winnerPair.Value.RollNumber);
else if (winnerPair.Value.Vote == RollVote.Disenchant)
player.UpdateCriteria(CriteriaType.CastSpell, 13262);
else
player.UpdateCriteria(CriteriaType.RollGreed, m_lootItem.itemid, winnerPair.Value.RollNumber);
if (winnerPair.Value.Vote == RollVote.Disenchant)
{
var disenchant = GetItemDisenchantLoot();
Loot loot = new(m_map, m_loot.GetOwnerGUID(), LootType.Disenchanting, null);
loot.FillLoot(disenchant.Id, LootStorage.Disenchant, player, true, false, LootModes.Default, ItemContext.None);
if (!loot.AutoStore(player, ItemConst.NullBag, ItemConst.NullSlot, true))
{
uint maxSlot = loot.GetMaxSlotInLootFor(player);
for (uint i = 0; i < maxSlot; ++i)
{
LootItem disenchantLoot = loot.LootItemInSlot(i, player);
if (disenchantLoot != null)
player.SendItemRetrievalMail(disenchantLoot.itemid, disenchantLoot.count, disenchantLoot.context);
}
}
else
m_loot.NotifyItemRemoved((byte)m_lootItem.LootListId, m_map);
}
else
player.StoreLootItem(m_loot.GetOwnerGUID(), (byte)m_lootListId, m_loot);
}
}
m_isStarted = false;
}
}
public class Loot
{
public Loot(Map map, ObjectGuid owner, LootType type, LootMethod lootMethod)
public Loot(Map map, ObjectGuid owner, LootType type, Group group)
{
loot_type = type;
maxDuplicates = 1;
_guid = map ? ObjectGuid.Create(HighGuid.LootObject, map.GetId(), 0, map.GenerateLowGuid(HighGuid.LootObject)) : ObjectGuid.Empty;
_owner = owner;
_itemContext = ItemContext.None;
_lootMethod = lootMethod;
_lootMethod = group != null ? group.GetLootMethod() : LootMethod.FreeForAll;
_lootMaster = group != null ? group.GetMasterLooterGuid() : ObjectGuid.Empty;
}
// Inserts the item into the loot (called by LootTemplate processors)
@@ -194,7 +610,7 @@ namespace Game.Loots
LootItem generatedLoot = new(item);
generatedLoot.context = _itemContext;
generatedLoot.count = (byte)Math.Min(count, proto.GetMaxStackSize());
generatedLoot.itemIndex = (uint)lootItems.Count;
generatedLoot.LootListId = (uint)lootItems.Count;
if (_itemContext != 0)
{
List<uint> bonusListIDs = Global.DB2Mgr.GetDefaultItemBonusTree(generatedLoot.itemid, _itemContext);
@@ -230,6 +646,63 @@ namespace Game.Loots
}
}
public bool AutoStore(Player player, byte bag, byte slot, bool broadcast, bool createdByPlayer = false)
{
bool allLooted = true;
uint max_slot = GetMaxSlotInLootFor(player);
for (uint i = 0; i < max_slot; ++i)
{
NotNormalLootItem qitem = null;
NotNormalLootItem ffaitem = null;
NotNormalLootItem conditem = null;
LootItem lootItem = LootItemInSlot(i, player, out qitem, out ffaitem, out conditem);
if (lootItem == null || lootItem.is_looted)
continue;
if (!lootItem.AllowedForPlayer(player))
continue;
if (qitem == null && lootItem.is_blocked)
continue;
// dont allow protected item to be looted by someone else
if (!lootItem.rollWinnerGUID.IsEmpty() && lootItem.rollWinnerGUID != GetGUID())
continue;
List<ItemPosCount> dest = new();
InventoryResult msg = player.CanStoreNewItem(bag, slot, dest, lootItem.itemid, lootItem.count);
if (msg != InventoryResult.Ok && slot != ItemConst.NullSlot)
msg = player.CanStoreNewItem(bag, ItemConst.NullSlot, dest, lootItem.itemid, lootItem.count);
if (msg != InventoryResult.Ok && bag != ItemConst.NullBag)
msg = player.CanStoreNewItem(ItemConst.NullBag, ItemConst.NullSlot, dest, lootItem.itemid, lootItem.count);
if (msg != InventoryResult.Ok)
{
player.SendEquipError(msg, null, null, lootItem.itemid);
allLooted = false;
continue;
}
if (qitem != null)
qitem.is_looted = true;
else if (ffaitem != null)
ffaitem.is_looted = true;
else if (conditem != null)
conditem.is_looted = true;
if (!lootItem.freeforall)
lootItem.is_looted = true;
--unlootedCount;
Item pItem = player.StoreNewItem(dest, lootItem.itemid, true, lootItem.randomBonusListId, null, lootItem.context, lootItem.BonusListIDs);
player.SendNewItem(pItem, lootItem.count, false, createdByPlayer, broadcast);
player.ApplyItemLootedSpell(pItem, true);
}
return allLooted;
}
public LootItem GetItemInSlot(uint lootSlot)
{
if (lootSlot < items.Count)
@@ -271,28 +744,69 @@ namespace Game.Loots
{
Player player = refe.GetSource();
if (player) // should actually be looted object instead of lootOwner but looter has to be really close so doesnt really matter
if (player.IsInMap(lootOwner))
FillNotNormalLootFor(player, player.IsAtGroupRewardDistance(lootOwner));
if (player.IsAtGroupRewardDistance(lootOwner))
FillNotNormalLootFor(player);
}
for (byte i = 0; i < items.Count; ++i)
void processLootItem(LootItem item)
{
ItemTemplate proto = Global.ObjectMgr.GetItemTemplate(items[i].itemid);
ItemTemplate proto = Global.ObjectMgr.GetItemTemplate(item.itemid);
if (proto != null)
{
if (proto.GetQuality() < group.GetLootThreshold())
items[i].is_underthreshold = true;
item.is_underthreshold = true;
else
{
switch (_lootMethod)
{
case LootMethod.MasterLoot:
case LootMethod.GroupLoot:
case LootMethod.NeedBeforeGreed:
{
item.is_blocked = true;
break;
}
default:
break;
}
}
}
};
foreach (LootItem item in items)
{
if (item.freeforall)
continue;
processLootItem(item);
}
foreach (LootItem item in quest_items)
{
if (!item.follow_loot_rules)
continue;
processLootItem(item);
}
}
// ... for personal loot
else
FillNotNormalLootFor(lootOwner, true);
FillNotNormalLootFor(lootOwner);
return true;
}
void FillNotNormalLootFor(Player player, bool presentAtLooting)
public void Update()
{
foreach (var pair in _rolls.ToList())
if (pair.Value.UpdateRoll())
_rolls.Remove(pair.Key);
}
void FillNotNormalLootFor(Player player)
{
ObjectGuid plguid = player.GetGUID();
_allowedLooters.Add(plguid);
var questItemList = PlayerQuestItems.LookupByKey(plguid);
if (questItemList.Empty())
@@ -304,7 +818,7 @@ namespace Game.Loots
questItemList = PlayerNonQuestNonFFAConditionalItems.LookupByKey(plguid);
if (questItemList.Empty())
FillNonQuestNonFFAConditionalLoot(player, presentAtLooting);
FillNonQuestNonFFAConditionalLoot(player);
}
List<NotNormalLootItem> FillFFALoot(Player player)
@@ -341,6 +855,8 @@ namespace Game.Loots
if (!item.is_looted && (item.AllowedForPlayer(player) || (item.follow_loot_rules && player.GetGroup() && ((GetLootMethod() == LootMethod.MasterLoot && player.GetGroup().GetMasterLooterGuid() == player.GetGUID()) || GetLootMethod() != LootMethod.MasterLoot))))
{
item.AddAllowedLooter(player);
ql.Add(new NotNormalLootItem(i));
// quest items get blocked when they first appear in a
@@ -349,7 +865,7 @@ namespace Game.Loots
// increase once if one looter only, looter-times if free for all
if (item.freeforall || !item.is_blocked)
++unlootedCount;
if (!player.GetGroup() || GetLootMethod() != LootMethod.GroupLoot)
if (!player.GetGroup() || GetLootMethod() != LootMethod.GroupLoot && GetLootMethod() != LootMethod.RoundRobin)
item.is_blocked = true;
if (items.Count + ql.Count == SharedConst.MaxNRLootItems)
@@ -363,7 +879,7 @@ namespace Game.Loots
return ql;
}
List<NotNormalLootItem> FillNonQuestNonFFAConditionalLoot(Player player, bool presentAtLooting)
List<NotNormalLootItem> FillNonQuestNonFFAConditionalLoot(Player player)
{
List<NotNormalLootItem> ql = new();
@@ -372,8 +888,7 @@ namespace Game.Loots
LootItem item = items[i];
if (!item.is_looted && !item.freeforall && item.AllowedForPlayer(player))
{
if (presentAtLooting)
item.AddAllowedLooter(player);
item.AddAllowedLooter(player);
if (!item.conditions.Empty())
{
ql.Add(new NotNormalLootItem(i));
@@ -447,6 +962,65 @@ namespace Game.Loots
}
}
public void OnLootOpened(Map map, ObjectGuid looter)
{
AddLooter(looter);
if (!_wasOpened)
{
_wasOpened = true;
if (_lootMethod == LootMethod.GroupLoot || _lootMethod == LootMethod.NeedBeforeGreed)
{
ushort maxEnchantingSkill = 0;
foreach (ObjectGuid allowedLooterGuid in _allowedLooters)
{
Player allowedLooter = Global.ObjAccessor.GetPlayer(map, allowedLooterGuid);
if (allowedLooter != null)
maxEnchantingSkill = Math.Max(maxEnchantingSkill, allowedLooter.GetSkillValue(SkillType.Enchanting));
}
uint lootListId = 0;
for (; lootListId < items.Count; ++lootListId)
{
LootItem item = items[(int)lootListId];
if (!item.is_blocked)
continue;
LootRoll lootRoll = new();
var inserted = _rolls.TryAdd(lootListId, lootRoll);
if (!lootRoll.TryToStart(map, this, lootListId, maxEnchantingSkill))
_rolls.Remove(lootListId);
}
for (; lootListId - items.Count < quest_items.Count; ++lootListId)
{
LootItem item = quest_items[(int)lootListId - items.Count];
if (!item.is_blocked)
continue;
LootRoll lootRoll = new();
var inserted = _rolls.TryAdd(lootListId, lootRoll);
if (!lootRoll.TryToStart(map, this, lootListId, maxEnchantingSkill))
_rolls.Remove(lootListId);
}
}
else if (_lootMethod == LootMethod.MasterLoot)
{
if (looter == _lootMaster)
{
Player lootMaster = Global.ObjAccessor.GetPlayer(map, looter);
if (lootMaster != null)
{
MasterLootCandidateList masterLootCandidateList = new();
masterLootCandidateList.LootObj = GetGUID();
masterLootCandidateList.Players = _allowedLooters;
lootMaster.SendPacket(masterLootCandidateList);
}
}
}
}
}
public void GenerateMoneyLoot(uint minAmount, uint maxAmount)
{
if (maxAmount > 0)
@@ -619,82 +1193,102 @@ namespace Game.Loots
case PermissionTypes.Group:
case PermissionTypes.Master:
case PermissionTypes.Restricted:
{
// if you are not the round-robin group looter, you can only see
// blocked rolled items and quest items, and !ffa items
for (byte i = 0; i < items.Count; ++i)
{
// if you are not the round-robin group looter, you can only see
// blocked rolled items and quest items, and !ffa items
for (byte i = 0; i < items.Count; ++i)
if (!items[i].is_looted && !items[i].freeforall && items[i].conditions.Empty() && items[i].AllowedForPlayer(viewer))
{
if (!items[i].is_looted && !items[i].freeforall && items[i].conditions.Empty() && items[i].AllowedForPlayer(viewer))
{
LootSlotType slot_type;
LootSlotType slot_type;
if (items[i].is_blocked) // for ML & restricted is_blocked = !is_underthreshold
if (items[i].is_blocked) // for ML & restricted is_blocked = !is_underthreshold
{
switch (permission)
{
switch (permission)
case PermissionTypes.Group:
slot_type = LootSlotType.RollOngoing;
break;
case PermissionTypes.Master:
{
case PermissionTypes.Group:
slot_type = LootSlotType.RollOngoing;
break;
case PermissionTypes.Master:
{
if (viewer.GetGroup() && viewer.GetGroup().GetMasterLooterGuid() == viewer.GetGUID())
slot_type = LootSlotType.Master;
else
slot_type = LootSlotType.Locked;
break;
}
case PermissionTypes.Restricted:
if (viewer.GetGroup() && viewer.GetGroup().GetMasterLooterGuid() == viewer.GetGUID())
slot_type = LootSlotType.Master;
else
slot_type = LootSlotType.Locked;
break;
default:
continue;
break;
}
}
else if (roundRobinPlayer.IsEmpty() || viewer.GetGUID() == roundRobinPlayer || !items[i].is_underthreshold)
{
// no round robin owner or he has released the loot
// or it IS the round robin group owner
// => item is lootable
slot_type = LootSlotType.AllowLoot;
}
else if (!items[i].rollWinnerGUID.IsEmpty())
{
if (items[i].rollWinnerGUID == viewer.GetGUID())
slot_type = LootSlotType.Owner;
else
case PermissionTypes.Restricted:
slot_type = LootSlotType.Locked;
break;
default:
continue;
}
else
// item shall not be displayed.
continue;
LootItemData lootItem = new();
lootItem.LootListID = (byte)(i + 1);
lootItem.UIType = slot_type;
lootItem.Quantity = items[i].count;
lootItem.Loot = new ItemInstance(items[i]);
packet.Items.Add(lootItem);
}
else if (roundRobinPlayer.IsEmpty() || viewer.GetGUID() == roundRobinPlayer || !items[i].is_underthreshold)
{
// no round robin owner or he has released the loot
// or it IS the round robin group owner
// => item is lootable
slot_type = LootSlotType.AllowLoot;
}
else if (!items[i].rollWinnerGUID.IsEmpty())
{
if (items[i].rollWinnerGUID == viewer.GetGUID())
slot_type = LootSlotType.Owner;
else
continue;
}
else
// item shall not be displayed.
continue;
LootItemData lootItem = new();
lootItem.LootListID = (byte)(i + 1);
lootItem.UIType = slot_type;
lootItem.Quantity = items[i].count;
lootItem.Loot = new ItemInstance(items[i]);
packet.Items.Add(lootItem);
}
break;
}
break;
}
case PermissionTypes.RoundRobin:
{
for (var i = 0; i < items.Count; ++i)
{
if (!items[i].is_looted && !items[i].freeforall && items[i].conditions.Empty() && items[i].AllowedForPlayer(viewer))
{
if (!roundRobinPlayer.IsEmpty() && viewer.GetGUID() != roundRobinPlayer)
// item shall not be displayed.
continue;
LootItemData lootItem = new();
lootItem.LootListID = (byte)(i + 1);
lootItem.UIType = LootSlotType.AllowLoot;
lootItem.Quantity = items[i].count;
lootItem.Loot = new(items[i]);
packet.Items.Add(lootItem);
}
}
break;
}
case PermissionTypes.All:
case PermissionTypes.Owner:
{
for (byte i = 0; i < items.Count; ++i)
{
for (byte i = 0; i < items.Count; ++i)
if (!items[i].is_looted && !items[i].freeforall && items[i].conditions.Empty() && items[i].AllowedForPlayer(viewer))
{
if (!items[i].is_looted && !items[i].freeforall && items[i].conditions.Empty() && items[i].AllowedForPlayer(viewer))
{
LootItemData lootItem = new();
lootItem.LootListID = (byte)(i + 1);
lootItem.UIType = (permission == PermissionTypes.Owner ? LootSlotType.Owner : LootSlotType.AllowLoot);
lootItem.Quantity = items[i].count;
lootItem.Loot = new ItemInstance(items[i]);
packet.Items.Add(lootItem);
}
LootItemData lootItem = new();
lootItem.LootListID = (byte)(i + 1);
lootItem.UIType = (permission == PermissionTypes.Owner ? LootSlotType.Owner : LootSlotType.AllowLoot);
lootItem.Quantity = items[i].count;
lootItem.Loot = new ItemInstance(items[i]);
packet.Items.Add(lootItem);
}
break;
}
break;
}
default:
return;
}
@@ -724,6 +1318,7 @@ namespace Game.Loots
lootItem.UIType = item.is_blocked ? LootSlotType.Locked : LootSlotType.AllowLoot;
break;
case PermissionTypes.Group:
case PermissionTypes.RoundRobin:
if (!item.is_blocked)
lootItem.UIType = LootSlotType.AllowLoot;
else
@@ -783,6 +1378,7 @@ namespace Game.Loots
lootItem.UIType = item.is_blocked ? LootSlotType.Locked : LootSlotType.AllowLoot;
break;
case PermissionTypes.Group:
case PermissionTypes.RoundRobin:
if (!item.is_blocked)
lootItem.UIType = LootSlotType.AllowLoot;
else
@@ -802,11 +1398,6 @@ namespace Game.Loots
}
}
public void AddLootValidatorRef(LootValidatorRef pLootValidatorRef)
{
i_LootValidatorRefManager.InsertFirst(pLootValidatorRef);
}
public void Clear()
{
PlayerQuestItems.Clear();
@@ -828,8 +1419,31 @@ namespace Game.Loots
gold = 0;
unlootedCount = 0;
roundRobinPlayer = ObjectGuid.Empty;
i_LootValidatorRefManager.ClearReferences();
_itemContext = 0;
_rolls.Clear();
}
public void NotifyLootList(Map map)
{
LootList lootList = new();
lootList.Owner = GetOwnerGUID();
lootList.LootObj = GetGUID();
if (GetLootMethod() == LootMethod.MasterLoot && HasOverThresholdItem())
lootList.Master = GetLootMasterGUID();
if (!roundRobinPlayer.IsEmpty())
lootList.RoundRobinWinner = roundRobinPlayer;
lootList.Write();
foreach (ObjectGuid allowedLooterGuid in _allowedLooters)
{
Player allowedLooter = Global.ObjAccessor.GetPlayer(map, allowedLooterGuid);
if (allowedLooter != null)
allowedLooter.SendPacket(lootList);
}
}
public bool Empty() { return items.Empty() && gold == 0; }
@@ -841,7 +1455,9 @@ namespace Game.Loots
public ObjectGuid GetGUID() { return _guid; }
public ObjectGuid GetOwnerGUID() { return _owner; }
public LootMethod GetLootMethod() { return _lootMethod; }
public ObjectGuid GetLootMasterGUID() { return _lootMaster; }
public MultiMap<ObjectGuid, NotNormalLootItem> GetPlayerQuestItems() { return PlayerQuestItems; }
public MultiMap<ObjectGuid, NotNormalLootItem> GetPlayerFFAItems() { return PlayerFFAItems; }
public MultiMap<ObjectGuid, NotNormalLootItem> GetPlayerNonQuestNonFFAConditionalItems() { return PlayerNonQuestNonFFAConditionalItems; }
@@ -859,14 +1475,15 @@ namespace Game.Loots
MultiMap<ObjectGuid, NotNormalLootItem> PlayerFFAItems = new();
MultiMap<ObjectGuid, NotNormalLootItem> PlayerNonQuestNonFFAConditionalItems = new();
// All rolls are registered here. They need to know, when the loot is not valid anymore
LootValidatorRefManager i_LootValidatorRefManager = new();
// Loot GUID
ObjectGuid _guid;
ObjectGuid _owner; // The WorldObject that holds this loot
ItemContext _itemContext;
LootMethod _lootMethod;
Dictionary<uint, LootRoll> _rolls = new(); // used if an item is under rolling
ObjectGuid _lootMaster;
List<ObjectGuid> _allowedLooters = new();
bool _wasOpened; // true if at least one player received the loot content
}
public class AELootResult
+4 -4
View File
@@ -50,7 +50,7 @@ namespace Game.Loots
LootItem lootItem = new();
lootItem.itemid = result.Read<uint>(1);
lootItem.count = result.Read<byte>(2);
lootItem.itemIndex = result.Read<uint>(3);
lootItem.LootListId = result.Read<uint>(3);
lootItem.follow_loot_rules = result.Read<bool>(4);
lootItem.freeforall = result.Read<bool>(5);
lootItem.is_blocked = result.Read<bool>(6);
@@ -114,7 +114,7 @@ namespace Game.Loots
LootItem li = new();
li.itemid = id;
li.count = (byte)storedItem.Count;
li.itemIndex = storedItem.ItemIndex;
li.LootListId = storedItem.ItemIndex;
li.follow_loot_rules = storedItem.FollowRules;
li.freeforall = storedItem.FFA;
li.is_blocked = storedItem.Blocked;
@@ -244,7 +244,7 @@ namespace Game.Loots
stmt.AddValue(0, _containerId);
stmt.AddValue(1, lootItem.itemid);
stmt.AddValue(2, lootItem.count);
stmt.AddValue(3, lootItem.itemIndex);
stmt.AddValue(3, lootItem.LootListId);
stmt.AddValue(4, lootItem.follow_loot_rules);
stmt.AddValue(5, lootItem.freeforall);
stmt.AddValue(6, lootItem.is_blocked);
@@ -325,7 +325,7 @@ namespace Game.Loots
{
ItemId = lootItem.itemid;
Count = lootItem.count;
ItemIndex = lootItem.itemIndex;
ItemIndex = lootItem.LootListId;
FollowRules = lootItem.follow_loot_rules;
FFA = lootItem.freeforall;
Blocked = lootItem.is_blocked;