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
+5 -4
View File
@@ -17,16 +17,14 @@
namespace Framework.Constants
{
public enum RollType
public enum RollVote
{
Pass = 0,
Need = 1,
Greed = 2,
Disenchant = 3,
NotEmitedYet = 4,
NotValid = 5,
MaxTypes = 4,
NotValid = 5
}
public enum RollMask
@@ -43,8 +41,10 @@ namespace Framework.Constants
public enum LootMethod
{
FreeForAll = 0,
RoundRobin = 1,
MasterLoot = 2,
GroupLoot = 3,
NeedBeforeGreed = 4,
PersonalLoot = 5
}
@@ -64,6 +64,7 @@ namespace Framework.Constants
Group = 1,
Master = 2,
Restricted = 3,
RoundRobin = 4,
Owner = 5,
None = 6
}
+7
View File
@@ -92,6 +92,13 @@ namespace Game.Entities
return true;
}
public override void Update(uint diff)
{
base.Update(diff);
loot?.Update();
}
public void SaveToDB()
{
// prevent DB data inconsistence problems and duplicates
@@ -91,8 +91,6 @@ namespace Game.Entities
List<VendorItemCount> m_vendorItemCounts = new();
public Loot loot;
public uint m_groupLootTimer; // (msecs)timer used for group loot
public ObjectGuid lootingGroupLowGUID; // used to find group which is looting corpse
ObjectGuid m_lootRecipient;
ObjectGuid m_lootRecipientGroup;
}
+2 -13
View File
@@ -507,20 +507,9 @@ namespace Game.Entities
if (IsEngaged())
AIUpdateTick(diff);
if (loot != null && m_groupLootTimer != 0 && !lootingGroupLowGUID.IsEmpty())
{
if (m_groupLootTimer <= diff)
{
Group group = Global.GroupMgr.GetGroupByGUID(lootingGroupLowGUID);
if (group)
group.EndRoll(loot, GetMap());
loot?.Update();
m_groupLootTimer = 0;
lootingGroupLowGUID.Clear();
}
else m_groupLootTimer -= diff;
}
else if (m_corpseRemoveTime <= GameTime.GetGameTime())
if (m_corpseRemoveTime <= GameTime.GetGameTime())
{
RemoveCorpse(false);
Log.outDebug(LogFilter.Unit, "Removing corpse... {0} ", GetEntry());
+1 -16
View File
@@ -697,20 +697,7 @@ namespace Game.Entities
}
break;
case GameObjectTypes.Chest:
if (loot != null && m_groupLootTimer != 0)
{
if (m_groupLootTimer <= diff)
{
Group group = Global.GroupMgr.GetGroupByGUID(lootingGroupLowGUID);
if (group)
group.EndRoll(loot, GetMap());
m_groupLootTimer = 0;
lootingGroupLowGUID.Clear();
}
else
m_groupLootTimer -= diff;
}
loot?.Update();
// Non-consumable chest was partially looted and restock time passed, restock all loot now
if (GetGoInfo().Chest.consumable == 0 && GameTime.GetGameTime() >= m_restockTime)
@@ -3423,8 +3410,6 @@ namespace Game.Entities
ObjectGuid m_lootRecipientGroup;
LootModes m_LootMode; // bitmask, default LOOT_MODE_DEFAULT, determines what loot will be lootable
uint m_lootGenerationTime;
public uint m_groupLootTimer; // (msecs)timer used for group loot
public ObjectGuid lootingGroupLowGUID; // used to find group which is looting
long m_packedRotation;
Quaternion m_localRotation;
public Position StationaryPosition { get; set; }
@@ -240,6 +240,7 @@ namespace Game.Entities
SceneMgr m_sceneMgr;
Dictionary<ObjectGuid, Loot> m_AELootView = new();
List<LootRoll> m_lootRolls = new(); // loot rolls waiting for answer
CUFProfile[] _CUFProfiles = new CUFProfile[PlayerConst.MaxCUFProfiles];
float[] m_powerFraction = new float[(int)PowerType.MaxPerClass];
+2 -1
View File
@@ -71,7 +71,8 @@ namespace Game.Entities
if (state == LfgState.FinishedDungeon)
return PartyResult.PartyLfgBootDungeonComplete;
if (grp.IsRollLootActive())
Player player = Global.ObjAccessor.FindConnectedPlayer(guidMember);
if (!player.m_lootRolls.Empty())
return PartyResult.PartyLfgBootLootRolls;
// @todo Should also be sent when anyone has recently left combat, with an aprox ~5 seconds timer.
+29 -69
View File
@@ -2949,13 +2949,12 @@ namespace Game.Entities
}
}
public InventoryResult CanRollForItemInLFG(ItemTemplate proto, WorldObject lootedObject)
public InventoryResult CanRollForItemInLFG(ItemTemplate proto, Map map)
{
if (!GetGroup() || !GetGroup().IsLFGGroup())
return InventoryResult.Ok; // not in LFG group
// check if looted object is inside the lfg dungeon
Map map = lootedObject.GetMap();
if (!Global.LFGMgr.InLfgDungeonMap(GetGroup().GetGUID(), map.GetId(), map.GetDifficultyID()))
return InventoryResult.Ok;
@@ -3946,7 +3945,7 @@ namespace Game.Entities
}
}
void ApplyItemLootedSpell(Item item, bool apply)
public void ApplyItemLootedSpell(Item item, bool apply)
{
if (item.GetTemplate().HasFlag(ItemFlags.Legacy))
return;
@@ -4104,6 +4103,18 @@ namespace Game.Entities
return null;
}
public LootRoll GetLootRoll(ObjectGuid lootObjectGuid, byte lootListId)
{
return m_lootRolls.Find(roll => roll.IsLootItem(lootObjectGuid, lootListId));
}
public void AddLootRoll(LootRoll roll) { m_lootRolls.Add(roll); }
public void RemoveLootRoll(LootRoll roll)
{
m_lootRolls.Remove(roll);
}
//Inventory
public bool IsInventoryPos(ushort pos)
{
@@ -5881,33 +5892,13 @@ namespace Game.Entities
}
}
public void AutoStoreLoot(uint loot_id, LootStore store, ItemContext context = 0, bool broadcast = false, bool createdByPlayer = false) { AutoStoreLoot(ItemConst.NullBag, ItemConst.NullSlot, loot_id, store, context, broadcast); }
void AutoStoreLoot(byte bag, byte slot, uint loot_id, LootStore store, ItemContext context = 0, bool broadcast = false, bool createdByPlayer = false)
{
Loot loot = new(null, ObjectGuid.Empty, LootType.None, LootMethod.FreeForAll);
Loot loot = new(null, ObjectGuid.Empty, LootType.None, null);
loot.FillLoot(loot_id, store, this, true, false, LootModes.Default, context);
uint max_slot = loot.GetMaxSlotInLootFor(this);
for (uint i = 0; i < max_slot; ++i)
{
LootItem lootItem = loot.LootItemInSlot(i, this);
List<ItemPosCount> dest = new();
InventoryResult msg = CanStoreNewItem(bag, slot, dest, lootItem.itemid, lootItem.count);
if (msg != InventoryResult.Ok && slot != ItemConst.NullSlot)
msg = CanStoreNewItem(bag, ItemConst.NullSlot, dest, lootItem.itemid, lootItem.count);
if (msg != InventoryResult.Ok && bag != ItemConst.NullBag)
msg = CanStoreNewItem(ItemConst.NullBag, ItemConst.NullSlot, dest, lootItem.itemid, lootItem.count);
if (msg != InventoryResult.Ok)
{
SendEquipError(msg, null, null, lootItem.itemid);
continue;
}
Item pItem = StoreNewItem(dest, lootItem.itemid, true, lootItem.randomBonusListId, null, lootItem.context, lootItem.BonusListIDs);
SendNewItem(pItem, lootItem.count, false, createdByPlayer, broadcast);
ApplyItemLootedSpell(pItem, true);
}
loot.AutoStore(this, bag, slot, broadcast, createdByPlayer);
Unit.ProcSkillsAndAuras(this, null, new ProcFlagsInit(ProcFlags.Looted), new ProcFlagsInit(ProcFlags.None), ProcFlagsSpellType.MaskAll, ProcFlagsSpellPhase.None, ProcFlagsHit.None, null, null, null);
}
@@ -6052,7 +6043,7 @@ namespace Game.Entities
// LootItem is being removed (looted) from the container, delete it from the DB.
if (loot.loot_type == LootType.Item)
Global.LootItemStorage.RemoveStoredLootItemForContainer(lootWorldObjectGuid.GetCounter(), item.itemid, item.count, item.itemIndex);
Global.LootItemStorage.RemoveStoredLootItemForContainer(lootWorldObjectGuid.GetCounter(), item.itemid, item.count, item.LootListId);
ApplyItemLootedSpell(newitem, true);
}
@@ -6092,7 +6083,7 @@ namespace Game.Entities
// Now we must make bones lootable, and send player loot
bones.SetCorpseDynamicFlag(CorpseDynFlags.Lootable);
bones.loot = new Loot(GetMap(), bones.GetGUID(), LootType.Insignia, looterPlr.GetGroup() != null ? looterPlr.GetGroup().GetLootMethod() : LootMethod.FreeForAll);
bones.loot = new Loot(GetMap(), bones.GetGUID(), LootType.Insignia, looterPlr.GetGroup());
// For AV Achievement
Battleground bg = GetBattleground();
@@ -6192,7 +6183,7 @@ namespace Game.Entities
Group group = GetGroup();
bool groupRules = (group != null && go.GetGoInfo().type == GameObjectTypes.Chest && go.GetGoInfo().Chest.usegrouplootrules != 0);
loot = new Loot(GetMap(), guid, loot_type, groupRules ? group.GetLootMethod() : LootMethod.FreeForAll);
loot = new Loot(GetMap(), guid, loot_type, groupRules ? group : null);
if (go.GetMap().Is25ManRaid())
loot.maxDuplicates = 3;
@@ -6224,22 +6215,6 @@ namespace Game.Entities
else if (loot_type == LootType.FishingJunk)
go.GetFishLootJunk(loot, this);
if (go.GetGoInfo().type == GameObjectTypes.Chest && go.GetGoInfo().Chest.usegrouplootrules != 0)
{
switch (group.GetLootMethod())
{
case LootMethod.GroupLoot:
// GroupLoot: rolls items over threshold. Items with quality < threshold, round robin
group.GroupLoot(loot, go);
break;
case LootMethod.MasterLoot:
group.MasterLoot(loot, go);
break;
default:
break;
}
}
go.SetLootState(LootState.Activated, this);
}
@@ -6253,6 +6228,9 @@ namespace Game.Entities
case LootMethod.MasterLoot:
permission = group.GetMasterLooterGuid() == GetGUID() ? PermissionTypes.Master : PermissionTypes.Restricted;
break;
case LootMethod.RoundRobin:
permission = PermissionTypes.RoundRobin;
break;
case LootMethod.FreeForAll:
permission = PermissionTypes.All;
break;
@@ -6284,7 +6262,7 @@ namespace Game.Entities
if (!item.m_lootGenerated && !Global.LootItemStorage.LoadStoredLoot(item, this))
{
item.m_lootGenerated = true;
loot = new Loot(GetMap(), guid, loot_type, LootMethod.FreeForAll);
loot = new Loot(GetMap(), guid, loot_type, null);
item.loot = loot;
switch (loot_type)
@@ -6355,7 +6333,7 @@ namespace Game.Entities
{
creature.StartPickPocketRefillTimer();
loot = new Loot(GetMap(), creature.GetGUID(), LootType.Pickpocketing, LootMethod.FreeForAll);
loot = new Loot(GetMap(), creature.GetGUID(), LootType.Pickpocketing, null);
creature.loot = loot;
uint lootid = creature.GetCreatureTemplate().PickPocketId;
if (lootid != 0)
@@ -6392,27 +6370,6 @@ namespace Game.Entities
return;
}
if (loot.loot_type == LootType.None)
{
// for creature, loot is filled when creature is killed.
Group group = creature.GetLootRecipientGroup();
if (group)
{
switch (loot.GetLootMethod())
{
case LootMethod.GroupLoot:
// GroupLoot: rolls items over threshold. Items with quality < threshold, round robin
group.GroupLoot(loot, creature);
break;
case LootMethod.MasterLoot:
group.MasterLoot(loot, creature);
break;
default:
break;
}
}
}
if (loot.loot_type == LootType.Skinning)
{
loot_type = LootType.Skinning;
@@ -6443,6 +6400,9 @@ namespace Game.Entities
case LootMethod.FreeForAll:
permission = PermissionTypes.All;
break;
case LootMethod.RoundRobin:
permission = PermissionTypes.RoundRobin;
break;
default:
permission = PermissionTypes.Group;
break;
@@ -6475,7 +6435,7 @@ namespace Game.Entities
SendPacket(packet);
// add 'this' player as one of the players that are looting 'loot'
loot.AddLooter(GetGUID());
loot.OnLootOpened(GetMap(), GetGUID());
m_AELootView[loot.GetGUID()] = loot;
if (loot_type == LootType.Corpse && !guid.IsItem())
+9
View File
@@ -757,6 +757,7 @@ namespace Game.Entities
UnsummonPetTemporaryIfAny();
ClearComboPoints();
GetSession().DoLootReleaseAll();
m_lootRolls.Clear();
Global.OutdoorPvPMgr.HandlePlayerLeaveZone(this, m_zoneUpdateId);
Global.BattleFieldMgr.HandlePlayerLeaveZone(this, m_zoneUpdateId);
}
@@ -3140,7 +3141,15 @@ namespace Game.Entities
case LootMethod.MasterLoot:
case LootMethod.FreeForAll:
return true;
case LootMethod.RoundRobin:
// may only loot if the player is the loot roundrobin player
// or if there are free/quest/conditional item for the player
if (loot.roundRobinPlayer.IsEmpty() || loot.roundRobinPlayer == GetGUID())
return true;
return loot.HasItemFor(this);
case LootMethod.GroupLoot:
case LootMethod.NeedBeforeGreed:
// may only loot if the player is the loot roundrobin player
// or item over threshold (so roll(s) can be launched)
// or if there are free/quest/conditional item for the player
+5 -22
View File
@@ -752,7 +752,6 @@ namespace Game.Entities
Player looter = player;
var group = player.GetGroup();
bool hasLooterGuid = false;
if (group)
{
group.BroadcastPacket(partyKillLog, group.GetMemberGroup(player.GetGUID()) != 0);
@@ -764,10 +763,7 @@ namespace Game.Entities
{
looter = Global.ObjAccessor.FindPlayer(group.GetLooterGuid());
if (looter)
{
hasLooterGuid = true;
creature.SetLootRecipient(looter); // update creature loot recipient to the allowed looter.
}
}
}
}
@@ -777,7 +773,7 @@ namespace Game.Entities
// Generate loot before updating looter
if (creature)
{
creature.loot = new Loot(creature.GetMap(), creature.GetGUID(), LootType.Corpse, group != null ? group.GetLootMethod() : LootMethod.FreeForAll);
creature.loot = new Loot(creature.GetMap(), creature.GetGUID(), LootType.Corpse, group);
Loot loot = creature.loot;
if (creature.GetMap().Is25ManRaid())
loot.maxDuplicates = 3;
@@ -789,24 +785,11 @@ namespace Game.Entities
if (creature.GetLootMode() > 0)
loot.GenerateMoneyLoot(creature.GetCreatureTemplate().MinGold, creature.GetCreatureTemplate().MaxGold);
if (group)
{
if (hasLooterGuid)
group.SendLooter(creature, looter);
else
group.SendLooter(creature, null);
loot.NotifyLootList(creature.GetMap());
// Update round robin looter only if the creature had loot
if (!loot.Empty())
group.UpdateLooterGuid(creature);
}
else
{
LootList lootList = new();
lootList.Owner = creature.GetGUID();
lootList.LootObj = creature.loot.GetGUID();
player.SendMessageToSet(lootList, true);
}
// Update round robin looter only if the creature had loot
if (group != null && !loot.Empty())
group.UpdateLooterGuid(creature);
}
player.RewardPlayerAndGroupAtKill(victim, false);
+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;
-1
View File
@@ -869,7 +869,6 @@ namespace Game
if (group)
{
group.SendUpdate();
group.ResetMaxEnchantingLevel();
if (group.GetLeaderGUID() == pCurrChar.GetGUID())
group.StopLeaderOfflineTimer();
}
-20
View File
@@ -385,26 +385,6 @@ namespace Game
group.SendUpdate();
}
[WorldPacketHandler(ClientOpcodes.LootRoll)]
void HandleLootRoll(LootRoll packet)
{
Group group = GetPlayer().GetGroup();
if (!group)
return;
group.CountRollVote(GetPlayer().GetGUID(), packet.LootObj, (byte)(packet.LootListID - 1), packet.RollType);
switch (packet.RollType)
{
case RollType.Need:
GetPlayer().UpdateCriteria(CriteriaType.RollAnyNeed, 1);
break;
case RollType.Greed:
GetPlayer().UpdateCriteria(CriteriaType.RollAnyGreed, 1);
break;
}
}
[WorldPacketHandler(ClientOpcodes.MinimapPing)]
void HandleMinimapPing(MinimapPingClient packet)
{
+15 -13
View File
@@ -362,22 +362,14 @@ namespace Game
if (player.GetGUID() == loot.roundRobinPlayer)
{
loot.roundRobinPlayer.Clear();
Group group = player.GetGroup();
if (group)
{
if (group.GetLootMethod() != LootMethod.MasterLoot)
group.SendLooter(creature, null);
}
// force dynflag update to update looter and lootable info
creature.m_values.ModifyValue(creature.m_objectData).ModifyValue(creature.m_objectData.DynamicFlags);
creature.ForceUpdateFieldChange();
loot.NotifyLootList(creature.GetMap());
}
// force dynflag update to update looter and lootable info
creature.m_values.ModifyValue(creature.m_objectData).ModifyValue(creature.m_objectData.DynamicFlags);
creature.ForceUpdateFieldChange();
}
}
//Player is not looking at loot list, he doesn't need to see updates on the loot list
loot.RemoveLooter(player.GetGUID());
}
public void DoLootReleaseAll()
@@ -466,6 +458,16 @@ namespace Game
}
}
[WorldPacketHandler(ClientOpcodes.LootRoll)]
void HandleLootRoll(LootRollPacket packet)
{
LootRoll lootRoll = GetPlayer().GetLootRoll(packet.LootObj, (byte)(packet.LootListID - 1));
if (lootRoll == null)
return;
lootRoll.PlayerVote(GetPlayer(), packet.RollType);
}
[WorldPacketHandler(ClientOpcodes.SetLootSpecialization)]
void HandleSetLootSpecialization(SetLootSpecialization packet)
{
+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;
+1 -1
View File
@@ -60,7 +60,7 @@ namespace Game.Mails
if (m_mailTemplateId == 123)
m_money = 1000000;
Loot mailLoot = new(null, ObjectGuid.Empty, LootType.None, LootMethod.FreeForAll);
Loot mailLoot = new(null, ObjectGuid.Empty, LootType.None, null);
// can be empty
mailLoot.FillLoot(m_mailTemplateId, LootStorage.Mail, receiver, true, true, LootModes.Default, ItemContext.None);
@@ -188,20 +188,20 @@ namespace Game.Networking.Packets
public ObjectGuid LootObj;
}
class LootRoll : ClientPacket
class LootRollPacket : ClientPacket
{
public LootRoll(WorldPacket packet) : base(packet) { }
public LootRollPacket(WorldPacket packet) : base(packet) { }
public override void Read()
{
LootObj = _worldPacket.ReadPackedGuid();
LootListID = _worldPacket.ReadUInt8();
RollType = (RollType)_worldPacket.ReadUInt8();
RollType = (RollVote)_worldPacket.ReadUInt8();
}
public ObjectGuid LootObj;
public byte LootListID;
public RollType RollType;
public RollVote RollType;
}
class LootReleaseResponse : ServerPacket
@@ -303,7 +303,7 @@ namespace Game.Networking.Packets
public ObjectGuid LootObj;
public ObjectGuid Player;
public int Roll; // Roll value can be negative, it means that it is an "offspec" roll but only during roll selection broadcast (not when sending the result)
public RollType RollType;
public RollVote RollType;
public LootItemData Item = new();
public bool Autopassed; // Triggers message |HlootHistory:%d|h[Loot]|h: You automatically passed on: %s because you cannot loot that item.
}
@@ -326,7 +326,7 @@ namespace Game.Networking.Packets
public ObjectGuid LootObj;
public ObjectGuid Winner;
public int Roll;
public RollType RollType;
public RollVote RollType;
public LootItemData Item = new();
public bool MainSpec;
}
-1
View File
@@ -193,7 +193,6 @@ namespace Game
if (group != null)
{
group.SendUpdate();
group.ResetMaxEnchantingLevel();
if (group.GetLeaderGUID() == _player.GetGUID())
group.StartLeaderOfflineTimer();
}