Core/Loot: Move loot rolls from Group to Loot
Port From (https://github.com/TrinityCore/TrinityCore/commit/3ef5079feeedfdafc9d3c1d9f865e96dbc77ecc8)
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -869,7 +869,6 @@ namespace Game
|
||||
if (group)
|
||||
{
|
||||
group.SendUpdate();
|
||||
group.ResetMaxEnchantingLevel();
|
||||
if (group.GetLeaderGUID() == pCurrChar.GetGUID())
|
||||
group.StopLeaderOfflineTimer();
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -193,7 +193,6 @@ namespace Game
|
||||
if (group != null)
|
||||
{
|
||||
group.SendUpdate();
|
||||
group.ResetMaxEnchantingLevel();
|
||||
if (group.GetLeaderGUID() == _player.GetGUID())
|
||||
group.StartLeaderOfflineTimer();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user