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
+1 -661
View File
@@ -532,9 +532,6 @@ namespace Game.Groups
}
}
if (m_maxEnchantingLevel < player.GetSkillValue(SkillType.Enchanting))
m_maxEnchantingLevel = player.GetSkillValue(SkillType.Enchanting);
return true;
}
@@ -603,33 +600,6 @@ namespace Game.Groups
DelinkMember(guid);
}
// Reevaluate group enchanter if the leaving player had enchanting skill or the player is offline
if (!player || player.GetSkillValue(SkillType.Enchanting) != 0)
ResetMaxEnchantingLevel();
// Remove player from loot rolls
foreach (var roll in RollId)
{
if (!roll.playerVote.ContainsKey(guid))
continue;
var vote = roll.playerVote[guid];
if (vote == RollType.Greed || vote == RollType.Disenchant)
--roll.totalGreed;
else if (vote == RollType.Need)
--roll.totalNeed;
else if (vote == RollType.Pass)
--roll.totalPass;
if (vote != RollType.NotValid)
--roll.totalPlayersRolling;
roll.playerVote.Remove(guid);
if (roll.totalPass + roll.totalNeed + roll.totalGreed >= roll.totalPlayersRolling)
CountTheRoll(roll, null);
}
// Update subgroups
var slot = _getMemberSlot(guid);
if (slot != null)
@@ -829,7 +799,7 @@ namespace Game.Groups
_homebindIfInstance(player);
}
RollId.Clear();
m_memberSlots.Clear();
RemoveAllInvites();
@@ -862,541 +832,6 @@ namespace Game.Groups
Global.GroupMgr.RemoveGroup(this);
}
void SendLootStartRollToPlayer(uint countDown, uint mapId, Player p, bool canNeed, Roll r)
{
StartLootRoll startLootRoll = new();
startLootRoll.LootObj = r.GetTarget().GetGUID();
startLootRoll.MapID = (int)mapId;
startLootRoll.RollTime = countDown;
startLootRoll.ValidRolls = r.rollTypeMask;
if (!canNeed)
startLootRoll.ValidRolls &= ~RollMask.Need;
startLootRoll.Method = GetLootMethod();
r.FillPacket(startLootRoll.Item);
ItemDisenchantLootRecord disenchant = r.GetItemDisenchantLoot(p);
if (disenchant != null)
if (m_maxEnchantingLevel >= disenchant.SkillRequired)
startLootRoll.ValidRolls |= RollMask.Disenchant;
p.SendPacket(startLootRoll);
}
void SendLootRoll(ObjectGuid playerGuid, int rollNumber, RollType rollType, Roll roll, bool autoPass = false)
{
LootRollBroadcast lootRoll = new();
lootRoll.LootObj = roll.GetTarget().GetGUID();
lootRoll.Player = playerGuid;
lootRoll.Roll = rollNumber;
lootRoll.RollType = rollType;
lootRoll.Autopassed = autoPass;
roll.FillPacket(lootRoll.Item);
foreach (var pair in roll.playerVote)
{
Player p = Global.ObjAccessor.FindConnectedPlayer(pair.Key);
if (!p || !p.GetSession())
continue;
if (pair.Value != RollType.NotValid)
p.SendPacket(lootRoll);
}
}
void SendLootRollWon(ObjectGuid winnerGuid, int rollNumber, RollType rollType, Roll roll)
{
LootRollWon lootRollWon = new();
lootRollWon.LootObj = roll.GetTarget().GetGUID();
lootRollWon.Winner = winnerGuid;
lootRollWon.Roll = rollNumber;
lootRollWon.RollType = rollType;
roll.FillPacket(lootRollWon.Item);
lootRollWon.MainSpec = true; // offspec rolls not implemented
foreach (var pair in roll.playerVote)
{
Player p = Global.ObjAccessor.FindConnectedPlayer(pair.Key);
if (!p || !p.GetSession())
continue;
if (pair.Value != RollType.NotValid)
p.SendPacket(lootRollWon);
}
}
void SendLootAllPassed(Roll roll)
{
LootAllPassed lootAllPassed = new();
lootAllPassed.LootObj = roll.GetTarget().GetGUID();
roll.FillPacket(lootAllPassed.Item);
foreach (var pair in roll.playerVote)
{
Player player = Global.ObjAccessor.FindConnectedPlayer(pair.Key);
if (!player || !player.GetSession())
continue;
if (pair.Value != RollType.NotValid)
player.SendPacket(lootAllPassed);
}
}
void SendLootRollsComplete(Roll roll)
{
LootRollsComplete lootRollsComplete = new();
lootRollsComplete.LootObj = roll.GetTarget().GetGUID();
lootRollsComplete.LootListID = (byte)(roll.itemSlot + 1);
foreach (var pair in roll.playerVote)
{
Player player = Global.ObjAccessor.FindConnectedPlayer(pair.Key);
if (!player || !player.GetSession())
continue;
if (pair.Value != RollType.NotValid)
player.SendPacket(lootRollsComplete);
}
}
// notify group members which player is the allowed looter for the given creature
public void SendLooter(Creature creature, Player groupLooter)
{
Cypher.Assert(creature);
LootList lootList = new();
lootList.Owner = creature.GetGUID();
lootList.LootObj = creature.loot.GetGUID();
if (creature.loot.GetLootMethod() == LootMethod.MasterLoot && creature.loot.HasOverThresholdItem())
lootList.Master = GetMasterLooterGuid();
if (groupLooter)
lootList.RoundRobinWinner = groupLooter.GetGUID();
BroadcastPacket(lootList, false);
}
public bool CanRollOnItem(LootItem item, Player player)
{
// Players can't roll on unique items if they already reached the maximum quantity of that item
ItemTemplate proto = Global.ObjectMgr.GetItemTemplate(item.itemid);
if (proto == null)
return false;
uint itemCount = player.GetItemCount(item.itemid);
if (proto.GetMaxCount() > 0 && itemCount >= proto.GetMaxCount())
return false;
if (!item.AllowedForPlayer(player))
return false;
return true;
}
public void GroupLoot(Loot loot, WorldObject lootedObject)
{
byte itemSlot = 0;
for (var i = 0; i < loot.items.Count; ++i, ++itemSlot)
{
LootItem lootItem = loot.items[i];
if (lootItem.freeforall)
continue;
ItemTemplate item = Global.ObjectMgr.GetItemTemplate(lootItem.itemid);
if (item == null)
continue;
//roll for over-threshold item if it's one-player loot
if (item.GetQuality() >= m_lootThreshold)
{
Roll r = new(lootItem);
for (GroupReference refe = GetFirstMember(); refe != null; refe = refe.Next())
{
Player playerToRoll = refe.GetSource();
if (!playerToRoll || playerToRoll.GetSession() == null)
continue;
if (playerToRoll.IsAtGroupRewardDistance(lootedObject))
{
r.totalPlayersRolling++;
RollType vote = playerToRoll.GetPassOnGroupLoot() ? RollType.Pass : RollType.NotEmitedYet;
if (!CanRollOnItem(lootItem, playerToRoll))
{
vote = RollType.Pass;
r.totalPass++; // Can't broadcast the pass now. need to wait until all rolling players are known
}
r.playerVote[playerToRoll.GetGUID()] = vote;
}
}
if (r.totalPlayersRolling > 0)
{
r.SetLoot(loot);
r.itemSlot = itemSlot;
if (item.HasFlag(ItemFlags2.CanOnlyRollGreed))
r.rollTypeMask &= ~RollMask.Need;
loot.items[itemSlot].is_blocked = true;
//Broadcast Pass and Send Rollstart
foreach (var pair in r.playerVote)
{
Player p = Global.ObjAccessor.FindPlayer(pair.Key);
if (!p || p.GetSession() == null)
continue;
if (pair.Value == RollType.Pass)
SendLootRoll(p.GetGUID(), -1, RollType.Pass, r, true);
else
SendLootStartRollToPlayer(60000, lootedObject.GetMapId(), p, p.CanRollForItemInLFG(item, lootedObject) == InventoryResult.Ok, r);
}
RollId.Add(r);
Creature creature = lootedObject.ToCreature();
GameObject go = lootedObject.ToGameObject();
if (creature)
{
creature.m_groupLootTimer = 60000;
creature.lootingGroupLowGUID = GetGUID();
}
else if (go)
{
go.m_groupLootTimer = 60000;
go.lootingGroupLowGUID = GetGUID();
}
}
}
else
lootItem.is_underthreshold = true;
}
foreach (var i in loot.quest_items)
{
if (!i.follow_loot_rules)
continue;
ItemTemplate item = Global.ObjectMgr.GetItemTemplate(i.itemid);
Roll r = new(i);
for (var refe = GetFirstMember(); refe != null; refe = refe.Next())
{
Player playerToRoll = refe.GetSource();
if (!playerToRoll || playerToRoll.GetSession() == null)
continue;
if (playerToRoll.IsAtGroupRewardDistance(lootedObject))
{
r.totalPlayersRolling++;
RollType vote = RollType.NotEmitedYet;
if (!CanRollOnItem(i, playerToRoll))
{
vote = RollType.Pass;
++r.totalPass;
}
r.playerVote[playerToRoll.GetGUID()] = vote;
}
}
if (r.totalPlayersRolling > 0)
{
r.SetLoot(loot);
r.itemSlot = itemSlot;
loot.quest_items[itemSlot - loot.items.Count].is_blocked = true;
//Broadcast Pass and Send Rollstart
foreach (var pair in r.playerVote)
{
Player p = Global.ObjAccessor.FindPlayer(pair.Key);
if (!p || p.GetSession() == null)
continue;
if (pair.Value == RollType.Pass)
SendLootRoll(p.GetGUID(), -1, RollType.Pass, r, true);
else
SendLootStartRollToPlayer(60000, lootedObject.GetMapId(), p, p.CanRollForItemInLFG(item, lootedObject) == InventoryResult.Ok, r);
}
RollId.Add(r);
Creature creature = lootedObject.ToCreature();
GameObject go = lootedObject.ToGameObject();
if (creature)
{
creature.m_groupLootTimer = 60000;
creature.lootingGroupLowGUID = GetGUID();
}
else if (go)
{
go.m_groupLootTimer = 60000;
go.lootingGroupLowGUID = GetGUID();
}
}
}
}
public void MasterLoot(Loot loot, WorldObject pLootedObject)
{
MasterLootCandidateList masterLootCandidateList = new();
masterLootCandidateList.LootObj = loot.GetGUID();
for (GroupReference refe = GetFirstMember(); refe != null; refe = refe.Next())
{
Player looter = refe.GetSource();
if (!looter.IsInWorld)
continue;
if (looter.IsAtGroupRewardDistance(pLootedObject))
masterLootCandidateList.Players.Add(looter.GetGUID());
}
masterLootCandidateList.Write();
for (GroupReference refe = GetFirstMember(); refe != null; refe = refe.Next())
{
Player looter = refe.GetSource();
if (looter.IsAtGroupRewardDistance(pLootedObject))
looter.SendPacket(masterLootCandidateList);
}
}
public void CountRollVote(ObjectGuid playerGuid, ObjectGuid lootObjectGuid, byte lootListId, RollType choice)
{
var roll = GetRoll(lootObjectGuid, lootListId);
if (roll == null)
return;
// this condition means that player joins to the party after roll begins
if (!roll.playerVote.ContainsKey(playerGuid))
return;
if (roll.GetLoot() != null)
if (roll.GetLoot().items.Empty())
return;
RollType rollType = RollType.MaxTypes;
switch (choice)
{
case RollType.Pass: // Player choose pass
SendLootRoll(playerGuid, -1, RollType.Pass, roll);
++roll.totalPass;
rollType = RollType.Pass;
break;
case RollType.Need: // player choose Need
SendLootRoll(playerGuid, 0, RollType.Need, roll);
++roll.totalNeed;
rollType = RollType.Need;
break;
case RollType.Greed: // player choose Greed
SendLootRoll(playerGuid, -7, RollType.Greed, roll);
++roll.totalGreed;
rollType = RollType.Greed;
break;
case RollType.Disenchant: // player choose Disenchant
SendLootRoll(playerGuid, -8, RollType.Disenchant, roll);
++roll.totalGreed;
rollType = RollType.Disenchant;
break;
}
roll.playerVote[playerGuid] = rollType;
if (roll.totalPass + roll.totalNeed + roll.totalGreed >= roll.totalPlayersRolling)
CountTheRoll(roll, null);
}
public void EndRoll(Loot pLoot, Map allowedMap)
{
foreach (var roll in RollId.ToList())
{
if (roll.GetLoot() == pLoot)
{
CountTheRoll(roll, allowedMap); //i don't have to edit player votes, who didn't vote ... he will pass
}
}
}
void CountTheRoll(Roll roll, Map allowedMap)
{
if (!roll.IsValid()) // is loot already deleted ?
{
RollId.Remove(roll);
return;
}
//end of the roll
if (roll.totalNeed > 0)
{
if (!roll.playerVote.Empty())
{
byte maxresul = 0;
ObjectGuid maxguid = ObjectGuid.Empty;
Player player;
foreach (var pair in roll.playerVote)
{
if (pair.Value != RollType.Need)
continue;
player = Global.ObjAccessor.FindPlayer(pair.Key);
if (!player || (allowedMap != null && player.GetMap() != allowedMap))
{
--roll.totalNeed;
continue;
}
byte randomN = (byte)RandomHelper.IRand(1, 100);
SendLootRoll(pair.Key, randomN, RollType.Need, roll);
if (maxresul < randomN)
{
maxguid = pair.Key;
maxresul = randomN;
}
}
if (!maxguid.IsEmpty())
{
SendLootRollWon(maxguid, maxresul, RollType.Need, roll);
player = Global.ObjAccessor.FindConnectedPlayer(maxguid);
if (player && player.GetSession())
{
player.UpdateCriteria(CriteriaType.RollNeed, roll.itemid, maxresul);
List<ItemPosCount> dest = new();
LootItem item = (roll.itemSlot >= roll.GetLoot().items.Count ? roll.GetLoot().quest_items[roll.itemSlot - roll.GetLoot().items.Count] : roll.GetLoot().items[roll.itemSlot]);
InventoryResult msg = player.CanStoreNewItem(ItemConst.NullBag, ItemConst.NullSlot, dest, roll.itemid, item.count);
if (msg == InventoryResult.Ok)
{
item.is_looted = true;
roll.GetLoot().NotifyItemRemoved(roll.itemSlot, allowedMap);
roll.GetLoot().unlootedCount--;
player.StoreNewItem(dest, roll.itemid, true, item.randomBonusListId, item.GetAllowedLooters(), item.context, item.BonusListIDs);
}
else
{
item.is_blocked = false;
item.rollWinnerGUID = player.GetGUID();
player.SendEquipError(msg, null, null, roll.itemid);
}
}
}
else
roll.totalNeed = 0;
}
}
if (roll.totalNeed == 0 && roll.totalGreed > 0) // if (roll->totalNeed == 0 && ...), not else if, because numbers can be modified above if player is on a different map
{
if (!roll.playerVote.Empty())
{
byte maxresul = 0;
ObjectGuid maxguid = ObjectGuid.Empty;
Player player;
RollType rollVote = RollType.NotValid;
foreach (var pair in roll.playerVote)
{
if (pair.Value != RollType.Greed && pair.Value != RollType.Disenchant)
continue;
player = Global.ObjAccessor.FindPlayer(pair.Key);
if (!player || (allowedMap != null && player.GetMap() != allowedMap))
{
--roll.totalGreed;
continue;
}
byte randomN = (byte)RandomHelper.IRand(1, 100);
SendLootRoll(pair.Key, randomN, pair.Value, roll);
if (maxresul < randomN)
{
maxguid = pair.Key;
maxresul = randomN;
rollVote = pair.Value;
}
}
if (!maxguid.IsEmpty())
{
SendLootRollWon(maxguid, maxresul, rollVote, roll);
player = Global.ObjAccessor.FindConnectedPlayer(maxguid);
if (player && player.GetSession() != null)
{
player.UpdateCriteria(CriteriaType.RollGreed, roll.itemid, maxresul);
LootItem item = roll.itemSlot >= roll.GetLoot().items.Count ? roll.GetLoot().quest_items[roll.itemSlot - roll.GetLoot().items.Count] : roll.GetLoot().items[roll.itemSlot];
if (rollVote == RollType.Greed)
{
List<ItemPosCount> dest = new();
InventoryResult msg = player.CanStoreNewItem(ItemConst.NullBag, ItemConst.NullSlot, dest, roll.itemid, item.count);
if (msg == InventoryResult.Ok)
{
item.is_looted = true;
roll.GetLoot().NotifyItemRemoved(roll.itemSlot, allowedMap);
roll.GetLoot().unlootedCount--;
player.StoreNewItem(dest, roll.itemid, true, item.randomBonusListId, item.GetAllowedLooters(), item.context, item.BonusListIDs);
}
else
{
item.is_blocked = false;
item.rollWinnerGUID = player.GetGUID();
player.SendEquipError(msg, null, null, roll.itemid);
}
}
else if (rollVote == RollType.Disenchant)
{
item.is_looted = true;
roll.GetLoot().NotifyItemRemoved(roll.itemSlot, allowedMap);
roll.GetLoot().unlootedCount--;
player.UpdateCriteria(CriteriaType.CastSpell, 13262); // Disenchant
ItemDisenchantLootRecord disenchant = roll.GetItemDisenchantLoot(player);
List<ItemPosCount> dest = new();
InventoryResult msg = player.CanStoreNewItem(ItemConst.NullBag, ItemConst.NullSlot, dest, roll.itemid, item.count);
if (msg == InventoryResult.Ok)
player.AutoStoreLoot(disenchant.Id, LootStorage.Disenchant, ItemContext.None, true);
else // If the player's inventory is full, send the disenchant result in a mail.
{
Loot loot = new(allowedMap, roll.GetLoot().GetOwnerGUID(), LootType.Disenchanting, roll.GetLoot().GetLootMethod());
loot.FillLoot(disenchant.Id, LootStorage.Disenchant, player, true);
uint max_slot = loot.GetMaxSlotInLootFor(player);
for (uint i = 0; i < max_slot; ++i)
{
LootItem lootItem = loot.LootItemInSlot(i, player);
player.SendEquipError(msg, null, null, lootItem.itemid);
player.SendItemRetrievalMail(lootItem.itemid, lootItem.count, lootItem.context);
}
}
}
}
}
else
roll.totalGreed = 0;
}
}
if (roll.totalNeed == 0 && roll.totalGreed == 0) // if, not else, because numbers can be modified above if player is on a different map
{
SendLootAllPassed(roll);
// remove is_blocked so that the item is lootable by all players
LootItem item = roll.itemSlot >= roll.GetLoot().items.Count ? roll.GetLoot().quest_items[roll.itemSlot - roll.GetLoot().items.Count] : roll.GetLoot().items[roll.itemSlot];
item.is_blocked = false;
}
SendLootRollsComplete(roll);
RollId.Remove(roll);
}
public void SetTargetIcon(byte symbol, ObjectGuid target, ObjectGuid changedBy, sbyte partyIndex)
{
if (symbol >= MapConst.TargetIconsCount)
@@ -2238,18 +1673,6 @@ namespace Game.Groups
}
}
public void ResetMaxEnchantingLevel()
{
m_maxEnchantingLevel = 0;
Player member;
foreach (var memberSlot in m_memberSlots)
{
member = Global.ObjAccessor.FindPlayer(memberSlot.guid);
if (member && m_maxEnchantingLevel < member.GetSkillValue(SkillType.Enchanting))
m_maxEnchantingLevel = member.GetSkillValue(SkillType.Enchanting);
}
}
public void SetLootMethod(LootMethod method)
{
m_lootMethod = method;
@@ -2466,8 +1889,6 @@ namespace Game.Groups
return GetMembersCount() > 0;
}
public bool IsRollLootActive() { return !RollId.Empty(); }
public ObjectGuid GetLeaderGUID()
{
return m_leaderGuid;
@@ -2623,15 +2044,6 @@ namespace Game.Groups
SendUpdate();
}
Roll GetRoll(ObjectGuid lootObjectGuid, byte lootListId)
{
foreach (var roll in RollId)
if (roll.IsValid() && roll.GetTarget().GetGUID() == lootObjectGuid && roll.itemSlot == lootListId)
return roll;
return null;
}
public void LinkMember(GroupReference pRef)
{
m_memberMgr.InsertFirst(pRef);
@@ -2761,11 +2173,9 @@ namespace Game.Groups
ItemQuality m_lootThreshold;
ObjectGuid m_looterGuid;
ObjectGuid m_masterLooterGuid;
List<Roll> RollId = new();
Dictionary<Difficulty, Dictionary<uint, InstanceBind>> m_boundInstances = new();
byte[] m_subGroupsCounts;
ObjectGuid m_guid;
uint m_maxEnchantingLevel;
uint m_dbStoreId;
bool _isLeaderOffline;
TimeTracker _leaderOfflineTimer = new();
@@ -2784,76 +2194,6 @@ namespace Game.Groups
}
}
public class Roll : LootValidatorRef
{
public Roll(LootItem li)
{
itemid = li.itemid;
itemRandomBonusListId = li.randomBonusListId;
itemCount = li.count;
rollTypeMask = RollMask.AllNoDisenchant;
}
public void SetLoot(Loot pLoot)
{
Link(pLoot, this);
}
public Loot GetLoot()
{
return GetTarget();
}
public override void TargetObjectBuildLink()
{
// called from link()
GetTarget().AddLootValidatorRef(this);
}
public void FillPacket(LootItemData lootItem)
{
lootItem.UIType = (totalPlayersRolling > totalNeed + totalGreed + totalPass) ? LootSlotType.RollOngoing : LootSlotType.AllowLoot;
lootItem.Quantity = itemCount;
lootItem.LootListID = (byte)(itemSlot + 1);
LootItem lootItemInSlot = GetTarget().GetItemInSlot(itemSlot);
if (lootItemInSlot != null)
{
lootItem.CanTradeToTapList = lootItemInSlot.allowedGUIDs.Count > 1;
lootItem.Loot = new ItemInstance(lootItemInSlot);
}
}
public ItemDisenchantLootRecord GetItemDisenchantLoot(Player player)
{
LootItem lootItemInSlot = GetTarget().GetItemInSlot(itemSlot);
if (lootItemInSlot != null)
{
ItemInstance itemInstance = new(lootItemInSlot);
BonusData bonusData = new(itemInstance);
if (!bonusData.CanDisenchant)
return null;
ItemTemplate itemTemplate = Global.ObjectMgr.GetItemTemplate(itemid);
uint itemLevel = Item.GetItemLevel(itemTemplate, bonusData, player.GetLevel(), 0, 0, 0, 0, false, 0);
return Item.GetDisenchantLoot(itemTemplate, (uint)bonusData.Quality, itemLevel);
}
return null;
}
public uint itemid;
public uint itemRandomBonusListId;
public byte itemCount;
public Dictionary<ObjectGuid, RollType> playerVote = new();
public byte totalPlayersRolling;
public byte totalNeed;
public byte totalGreed;
public byte totalPass;
public byte itemSlot;
public RollMask rollTypeMask;
}
public class MemberSlot
{
public ObjectGuid guid;