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
|
namespace Framework.Constants
|
||||||
{
|
{
|
||||||
public enum RollType
|
public enum RollVote
|
||||||
{
|
{
|
||||||
Pass = 0,
|
Pass = 0,
|
||||||
Need = 1,
|
Need = 1,
|
||||||
Greed = 2,
|
Greed = 2,
|
||||||
Disenchant = 3,
|
Disenchant = 3,
|
||||||
NotEmitedYet = 4,
|
NotEmitedYet = 4,
|
||||||
NotValid = 5,
|
NotValid = 5
|
||||||
|
|
||||||
MaxTypes = 4,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum RollMask
|
public enum RollMask
|
||||||
@@ -43,8 +41,10 @@ namespace Framework.Constants
|
|||||||
public enum LootMethod
|
public enum LootMethod
|
||||||
{
|
{
|
||||||
FreeForAll = 0,
|
FreeForAll = 0,
|
||||||
|
RoundRobin = 1,
|
||||||
MasterLoot = 2,
|
MasterLoot = 2,
|
||||||
GroupLoot = 3,
|
GroupLoot = 3,
|
||||||
|
NeedBeforeGreed = 4,
|
||||||
PersonalLoot = 5
|
PersonalLoot = 5
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,6 +64,7 @@ namespace Framework.Constants
|
|||||||
Group = 1,
|
Group = 1,
|
||||||
Master = 2,
|
Master = 2,
|
||||||
Restricted = 3,
|
Restricted = 3,
|
||||||
|
RoundRobin = 4,
|
||||||
Owner = 5,
|
Owner = 5,
|
||||||
None = 6
|
None = 6
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -92,6 +92,13 @@ namespace Game.Entities
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void Update(uint diff)
|
||||||
|
{
|
||||||
|
base.Update(diff);
|
||||||
|
|
||||||
|
loot?.Update();
|
||||||
|
}
|
||||||
|
|
||||||
public void SaveToDB()
|
public void SaveToDB()
|
||||||
{
|
{
|
||||||
// prevent DB data inconsistence problems and duplicates
|
// prevent DB data inconsistence problems and duplicates
|
||||||
|
|||||||
@@ -91,8 +91,6 @@ namespace Game.Entities
|
|||||||
List<VendorItemCount> m_vendorItemCounts = new();
|
List<VendorItemCount> m_vendorItemCounts = new();
|
||||||
|
|
||||||
public Loot loot;
|
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_lootRecipient;
|
||||||
ObjectGuid m_lootRecipientGroup;
|
ObjectGuid m_lootRecipientGroup;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -507,20 +507,9 @@ namespace Game.Entities
|
|||||||
if (IsEngaged())
|
if (IsEngaged())
|
||||||
AIUpdateTick(diff);
|
AIUpdateTick(diff);
|
||||||
|
|
||||||
if (loot != null && m_groupLootTimer != 0 && !lootingGroupLowGUID.IsEmpty())
|
loot?.Update();
|
||||||
{
|
|
||||||
if (m_groupLootTimer <= diff)
|
|
||||||
{
|
|
||||||
Group group = Global.GroupMgr.GetGroupByGUID(lootingGroupLowGUID);
|
|
||||||
if (group)
|
|
||||||
group.EndRoll(loot, GetMap());
|
|
||||||
|
|
||||||
m_groupLootTimer = 0;
|
if (m_corpseRemoveTime <= GameTime.GetGameTime())
|
||||||
lootingGroupLowGUID.Clear();
|
|
||||||
}
|
|
||||||
else m_groupLootTimer -= diff;
|
|
||||||
}
|
|
||||||
else if (m_corpseRemoveTime <= GameTime.GetGameTime())
|
|
||||||
{
|
{
|
||||||
RemoveCorpse(false);
|
RemoveCorpse(false);
|
||||||
Log.outDebug(LogFilter.Unit, "Removing corpse... {0} ", GetEntry());
|
Log.outDebug(LogFilter.Unit, "Removing corpse... {0} ", GetEntry());
|
||||||
|
|||||||
@@ -697,20 +697,7 @@ namespace Game.Entities
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case GameObjectTypes.Chest:
|
case GameObjectTypes.Chest:
|
||||||
if (loot != null && m_groupLootTimer != 0)
|
loot?.Update();
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Non-consumable chest was partially looted and restock time passed, restock all loot now
|
// Non-consumable chest was partially looted and restock time passed, restock all loot now
|
||||||
if (GetGoInfo().Chest.consumable == 0 && GameTime.GetGameTime() >= m_restockTime)
|
if (GetGoInfo().Chest.consumable == 0 && GameTime.GetGameTime() >= m_restockTime)
|
||||||
@@ -3423,8 +3410,6 @@ namespace Game.Entities
|
|||||||
ObjectGuid m_lootRecipientGroup;
|
ObjectGuid m_lootRecipientGroup;
|
||||||
LootModes m_LootMode; // bitmask, default LOOT_MODE_DEFAULT, determines what loot will be lootable
|
LootModes m_LootMode; // bitmask, default LOOT_MODE_DEFAULT, determines what loot will be lootable
|
||||||
uint m_lootGenerationTime;
|
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;
|
long m_packedRotation;
|
||||||
Quaternion m_localRotation;
|
Quaternion m_localRotation;
|
||||||
public Position StationaryPosition { get; set; }
|
public Position StationaryPosition { get; set; }
|
||||||
|
|||||||
@@ -240,6 +240,7 @@ namespace Game.Entities
|
|||||||
SceneMgr m_sceneMgr;
|
SceneMgr m_sceneMgr;
|
||||||
|
|
||||||
Dictionary<ObjectGuid, Loot> m_AELootView = new();
|
Dictionary<ObjectGuid, Loot> m_AELootView = new();
|
||||||
|
List<LootRoll> m_lootRolls = new(); // loot rolls waiting for answer
|
||||||
|
|
||||||
CUFProfile[] _CUFProfiles = new CUFProfile[PlayerConst.MaxCUFProfiles];
|
CUFProfile[] _CUFProfiles = new CUFProfile[PlayerConst.MaxCUFProfiles];
|
||||||
float[] m_powerFraction = new float[(int)PowerType.MaxPerClass];
|
float[] m_powerFraction = new float[(int)PowerType.MaxPerClass];
|
||||||
|
|||||||
@@ -71,7 +71,8 @@ namespace Game.Entities
|
|||||||
if (state == LfgState.FinishedDungeon)
|
if (state == LfgState.FinishedDungeon)
|
||||||
return PartyResult.PartyLfgBootDungeonComplete;
|
return PartyResult.PartyLfgBootDungeonComplete;
|
||||||
|
|
||||||
if (grp.IsRollLootActive())
|
Player player = Global.ObjAccessor.FindConnectedPlayer(guidMember);
|
||||||
|
if (!player.m_lootRolls.Empty())
|
||||||
return PartyResult.PartyLfgBootLootRolls;
|
return PartyResult.PartyLfgBootLootRolls;
|
||||||
|
|
||||||
// @todo Should also be sent when anyone has recently left combat, with an aprox ~5 seconds timer.
|
// @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())
|
if (!GetGroup() || !GetGroup().IsLFGGroup())
|
||||||
return InventoryResult.Ok; // not in LFG group
|
return InventoryResult.Ok; // not in LFG group
|
||||||
|
|
||||||
// check if looted object is inside the lfg dungeon
|
// check if looted object is inside the lfg dungeon
|
||||||
Map map = lootedObject.GetMap();
|
|
||||||
if (!Global.LFGMgr.InLfgDungeonMap(GetGroup().GetGUID(), map.GetId(), map.GetDifficultyID()))
|
if (!Global.LFGMgr.InLfgDungeonMap(GetGroup().GetGUID(), map.GetId(), map.GetDifficultyID()))
|
||||||
return InventoryResult.Ok;
|
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))
|
if (item.GetTemplate().HasFlag(ItemFlags.Legacy))
|
||||||
return;
|
return;
|
||||||
@@ -4104,6 +4103,18 @@ namespace Game.Entities
|
|||||||
return null;
|
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
|
//Inventory
|
||||||
public bool IsInventoryPos(ushort pos)
|
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); }
|
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)
|
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);
|
loot.FillLoot(loot_id, store, this, true, false, LootModes.Default, context);
|
||||||
|
|
||||||
uint max_slot = loot.GetMaxSlotInLootFor(this);
|
loot.AutoStore(this, bag, slot, broadcast, createdByPlayer);
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
Unit.ProcSkillsAndAuras(this, null, new ProcFlagsInit(ProcFlags.Looted), new ProcFlagsInit(ProcFlags.None), ProcFlagsSpellType.MaskAll, ProcFlagsSpellPhase.None, ProcFlagsHit.None, null, null, null);
|
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.
|
// LootItem is being removed (looted) from the container, delete it from the DB.
|
||||||
if (loot.loot_type == LootType.Item)
|
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);
|
ApplyItemLootedSpell(newitem, true);
|
||||||
}
|
}
|
||||||
@@ -6092,7 +6083,7 @@ namespace Game.Entities
|
|||||||
// Now we must make bones lootable, and send player loot
|
// Now we must make bones lootable, and send player loot
|
||||||
bones.SetCorpseDynamicFlag(CorpseDynFlags.Lootable);
|
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
|
// For AV Achievement
|
||||||
Battleground bg = GetBattleground();
|
Battleground bg = GetBattleground();
|
||||||
@@ -6192,7 +6183,7 @@ namespace Game.Entities
|
|||||||
Group group = GetGroup();
|
Group group = GetGroup();
|
||||||
bool groupRules = (group != null && go.GetGoInfo().type == GameObjectTypes.Chest && go.GetGoInfo().Chest.usegrouplootrules != 0);
|
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())
|
if (go.GetMap().Is25ManRaid())
|
||||||
loot.maxDuplicates = 3;
|
loot.maxDuplicates = 3;
|
||||||
|
|
||||||
@@ -6224,22 +6215,6 @@ namespace Game.Entities
|
|||||||
else if (loot_type == LootType.FishingJunk)
|
else if (loot_type == LootType.FishingJunk)
|
||||||
go.GetFishLootJunk(loot, this);
|
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);
|
go.SetLootState(LootState.Activated, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -6253,6 +6228,9 @@ namespace Game.Entities
|
|||||||
case LootMethod.MasterLoot:
|
case LootMethod.MasterLoot:
|
||||||
permission = group.GetMasterLooterGuid() == GetGUID() ? PermissionTypes.Master : PermissionTypes.Restricted;
|
permission = group.GetMasterLooterGuid() == GetGUID() ? PermissionTypes.Master : PermissionTypes.Restricted;
|
||||||
break;
|
break;
|
||||||
|
case LootMethod.RoundRobin:
|
||||||
|
permission = PermissionTypes.RoundRobin;
|
||||||
|
break;
|
||||||
case LootMethod.FreeForAll:
|
case LootMethod.FreeForAll:
|
||||||
permission = PermissionTypes.All;
|
permission = PermissionTypes.All;
|
||||||
break;
|
break;
|
||||||
@@ -6284,7 +6262,7 @@ namespace Game.Entities
|
|||||||
if (!item.m_lootGenerated && !Global.LootItemStorage.LoadStoredLoot(item, this))
|
if (!item.m_lootGenerated && !Global.LootItemStorage.LoadStoredLoot(item, this))
|
||||||
{
|
{
|
||||||
item.m_lootGenerated = true;
|
item.m_lootGenerated = true;
|
||||||
loot = new Loot(GetMap(), guid, loot_type, LootMethod.FreeForAll);
|
loot = new Loot(GetMap(), guid, loot_type, null);
|
||||||
item.loot = loot;
|
item.loot = loot;
|
||||||
|
|
||||||
switch (loot_type)
|
switch (loot_type)
|
||||||
@@ -6355,7 +6333,7 @@ namespace Game.Entities
|
|||||||
{
|
{
|
||||||
creature.StartPickPocketRefillTimer();
|
creature.StartPickPocketRefillTimer();
|
||||||
|
|
||||||
loot = new Loot(GetMap(), creature.GetGUID(), LootType.Pickpocketing, LootMethod.FreeForAll);
|
loot = new Loot(GetMap(), creature.GetGUID(), LootType.Pickpocketing, null);
|
||||||
creature.loot = loot;
|
creature.loot = loot;
|
||||||
uint lootid = creature.GetCreatureTemplate().PickPocketId;
|
uint lootid = creature.GetCreatureTemplate().PickPocketId;
|
||||||
if (lootid != 0)
|
if (lootid != 0)
|
||||||
@@ -6392,27 +6370,6 @@ namespace Game.Entities
|
|||||||
return;
|
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)
|
if (loot.loot_type == LootType.Skinning)
|
||||||
{
|
{
|
||||||
loot_type = LootType.Skinning;
|
loot_type = LootType.Skinning;
|
||||||
@@ -6443,6 +6400,9 @@ namespace Game.Entities
|
|||||||
case LootMethod.FreeForAll:
|
case LootMethod.FreeForAll:
|
||||||
permission = PermissionTypes.All;
|
permission = PermissionTypes.All;
|
||||||
break;
|
break;
|
||||||
|
case LootMethod.RoundRobin:
|
||||||
|
permission = PermissionTypes.RoundRobin;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
permission = PermissionTypes.Group;
|
permission = PermissionTypes.Group;
|
||||||
break;
|
break;
|
||||||
@@ -6475,7 +6435,7 @@ namespace Game.Entities
|
|||||||
SendPacket(packet);
|
SendPacket(packet);
|
||||||
|
|
||||||
// add 'this' player as one of the players that are looting 'loot'
|
// add 'this' player as one of the players that are looting 'loot'
|
||||||
loot.AddLooter(GetGUID());
|
loot.OnLootOpened(GetMap(), GetGUID());
|
||||||
m_AELootView[loot.GetGUID()] = loot;
|
m_AELootView[loot.GetGUID()] = loot;
|
||||||
|
|
||||||
if (loot_type == LootType.Corpse && !guid.IsItem())
|
if (loot_type == LootType.Corpse && !guid.IsItem())
|
||||||
|
|||||||
@@ -757,6 +757,7 @@ namespace Game.Entities
|
|||||||
UnsummonPetTemporaryIfAny();
|
UnsummonPetTemporaryIfAny();
|
||||||
ClearComboPoints();
|
ClearComboPoints();
|
||||||
GetSession().DoLootReleaseAll();
|
GetSession().DoLootReleaseAll();
|
||||||
|
m_lootRolls.Clear();
|
||||||
Global.OutdoorPvPMgr.HandlePlayerLeaveZone(this, m_zoneUpdateId);
|
Global.OutdoorPvPMgr.HandlePlayerLeaveZone(this, m_zoneUpdateId);
|
||||||
Global.BattleFieldMgr.HandlePlayerLeaveZone(this, m_zoneUpdateId);
|
Global.BattleFieldMgr.HandlePlayerLeaveZone(this, m_zoneUpdateId);
|
||||||
}
|
}
|
||||||
@@ -3140,7 +3141,15 @@ namespace Game.Entities
|
|||||||
case LootMethod.MasterLoot:
|
case LootMethod.MasterLoot:
|
||||||
case LootMethod.FreeForAll:
|
case LootMethod.FreeForAll:
|
||||||
return true;
|
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.GroupLoot:
|
||||||
|
case LootMethod.NeedBeforeGreed:
|
||||||
// may only loot if the player is the loot roundrobin player
|
// may only loot if the player is the loot roundrobin player
|
||||||
// or item over threshold (so roll(s) can be launched)
|
// or item over threshold (so roll(s) can be launched)
|
||||||
// or if there are free/quest/conditional item for the player
|
// or if there are free/quest/conditional item for the player
|
||||||
|
|||||||
@@ -752,7 +752,6 @@ namespace Game.Entities
|
|||||||
|
|
||||||
Player looter = player;
|
Player looter = player;
|
||||||
var group = player.GetGroup();
|
var group = player.GetGroup();
|
||||||
bool hasLooterGuid = false;
|
|
||||||
if (group)
|
if (group)
|
||||||
{
|
{
|
||||||
group.BroadcastPacket(partyKillLog, group.GetMemberGroup(player.GetGUID()) != 0);
|
group.BroadcastPacket(partyKillLog, group.GetMemberGroup(player.GetGUID()) != 0);
|
||||||
@@ -764,10 +763,7 @@ namespace Game.Entities
|
|||||||
{
|
{
|
||||||
looter = Global.ObjAccessor.FindPlayer(group.GetLooterGuid());
|
looter = Global.ObjAccessor.FindPlayer(group.GetLooterGuid());
|
||||||
if (looter)
|
if (looter)
|
||||||
{
|
|
||||||
hasLooterGuid = true;
|
|
||||||
creature.SetLootRecipient(looter); // update creature loot recipient to the allowed looter.
|
creature.SetLootRecipient(looter); // update creature loot recipient to the allowed looter.
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -777,7 +773,7 @@ namespace Game.Entities
|
|||||||
// Generate loot before updating looter
|
// Generate loot before updating looter
|
||||||
if (creature)
|
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;
|
Loot loot = creature.loot;
|
||||||
if (creature.GetMap().Is25ManRaid())
|
if (creature.GetMap().Is25ManRaid())
|
||||||
loot.maxDuplicates = 3;
|
loot.maxDuplicates = 3;
|
||||||
@@ -789,24 +785,11 @@ namespace Game.Entities
|
|||||||
if (creature.GetLootMode() > 0)
|
if (creature.GetLootMode() > 0)
|
||||||
loot.GenerateMoneyLoot(creature.GetCreatureTemplate().MinGold, creature.GetCreatureTemplate().MaxGold);
|
loot.GenerateMoneyLoot(creature.GetCreatureTemplate().MinGold, creature.GetCreatureTemplate().MaxGold);
|
||||||
|
|
||||||
if (group)
|
loot.NotifyLootList(creature.GetMap());
|
||||||
{
|
|
||||||
if (hasLooterGuid)
|
|
||||||
group.SendLooter(creature, looter);
|
|
||||||
else
|
|
||||||
group.SendLooter(creature, null);
|
|
||||||
|
|
||||||
// Update round robin looter only if the creature had loot
|
// Update round robin looter only if the creature had loot
|
||||||
if (!loot.Empty())
|
if (group != null && !loot.Empty())
|
||||||
group.UpdateLooterGuid(creature);
|
group.UpdateLooterGuid(creature);
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LootList lootList = new();
|
|
||||||
lootList.Owner = creature.GetGUID();
|
|
||||||
lootList.LootObj = creature.loot.GetGUID();
|
|
||||||
player.SendMessageToSet(lootList, true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
player.RewardPlayerAndGroupAtKill(victim, false);
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -603,33 +600,6 @@ namespace Game.Groups
|
|||||||
DelinkMember(guid);
|
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
|
// Update subgroups
|
||||||
var slot = _getMemberSlot(guid);
|
var slot = _getMemberSlot(guid);
|
||||||
if (slot != null)
|
if (slot != null)
|
||||||
@@ -829,7 +799,7 @@ namespace Game.Groups
|
|||||||
|
|
||||||
_homebindIfInstance(player);
|
_homebindIfInstance(player);
|
||||||
}
|
}
|
||||||
RollId.Clear();
|
|
||||||
m_memberSlots.Clear();
|
m_memberSlots.Clear();
|
||||||
|
|
||||||
RemoveAllInvites();
|
RemoveAllInvites();
|
||||||
@@ -862,541 +832,6 @@ namespace Game.Groups
|
|||||||
Global.GroupMgr.RemoveGroup(this);
|
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)
|
public void SetTargetIcon(byte symbol, ObjectGuid target, ObjectGuid changedBy, sbyte partyIndex)
|
||||||
{
|
{
|
||||||
if (symbol >= MapConst.TargetIconsCount)
|
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)
|
public void SetLootMethod(LootMethod method)
|
||||||
{
|
{
|
||||||
m_lootMethod = method;
|
m_lootMethod = method;
|
||||||
@@ -2466,8 +1889,6 @@ namespace Game.Groups
|
|||||||
return GetMembersCount() > 0;
|
return GetMembersCount() > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsRollLootActive() { return !RollId.Empty(); }
|
|
||||||
|
|
||||||
public ObjectGuid GetLeaderGUID()
|
public ObjectGuid GetLeaderGUID()
|
||||||
{
|
{
|
||||||
return m_leaderGuid;
|
return m_leaderGuid;
|
||||||
@@ -2623,15 +2044,6 @@ namespace Game.Groups
|
|||||||
SendUpdate();
|
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)
|
public void LinkMember(GroupReference pRef)
|
||||||
{
|
{
|
||||||
m_memberMgr.InsertFirst(pRef);
|
m_memberMgr.InsertFirst(pRef);
|
||||||
@@ -2761,11 +2173,9 @@ namespace Game.Groups
|
|||||||
ItemQuality m_lootThreshold;
|
ItemQuality m_lootThreshold;
|
||||||
ObjectGuid m_looterGuid;
|
ObjectGuid m_looterGuid;
|
||||||
ObjectGuid m_masterLooterGuid;
|
ObjectGuid m_masterLooterGuid;
|
||||||
List<Roll> RollId = new();
|
|
||||||
Dictionary<Difficulty, Dictionary<uint, InstanceBind>> m_boundInstances = new();
|
Dictionary<Difficulty, Dictionary<uint, InstanceBind>> m_boundInstances = new();
|
||||||
byte[] m_subGroupsCounts;
|
byte[] m_subGroupsCounts;
|
||||||
ObjectGuid m_guid;
|
ObjectGuid m_guid;
|
||||||
uint m_maxEnchantingLevel;
|
|
||||||
uint m_dbStoreId;
|
uint m_dbStoreId;
|
||||||
bool _isLeaderOffline;
|
bool _isLeaderOffline;
|
||||||
TimeTracker _leaderOfflineTimer = new();
|
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 class MemberSlot
|
||||||
{
|
{
|
||||||
public ObjectGuid guid;
|
public ObjectGuid guid;
|
||||||
|
|||||||
@@ -869,7 +869,6 @@ namespace Game
|
|||||||
if (group)
|
if (group)
|
||||||
{
|
{
|
||||||
group.SendUpdate();
|
group.SendUpdate();
|
||||||
group.ResetMaxEnchantingLevel();
|
|
||||||
if (group.GetLeaderGUID() == pCurrChar.GetGUID())
|
if (group.GetLeaderGUID() == pCurrChar.GetGUID())
|
||||||
group.StopLeaderOfflineTimer();
|
group.StopLeaderOfflineTimer();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -385,26 +385,6 @@ namespace Game
|
|||||||
group.SendUpdate();
|
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)]
|
[WorldPacketHandler(ClientOpcodes.MinimapPing)]
|
||||||
void HandleMinimapPing(MinimapPingClient packet)
|
void HandleMinimapPing(MinimapPingClient packet)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -362,22 +362,14 @@ namespace Game
|
|||||||
if (player.GetGUID() == loot.roundRobinPlayer)
|
if (player.GetGUID() == loot.roundRobinPlayer)
|
||||||
{
|
{
|
||||||
loot.roundRobinPlayer.Clear();
|
loot.roundRobinPlayer.Clear();
|
||||||
|
loot.NotifyLootList(creature.GetMap());
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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()
|
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)]
|
[WorldPacketHandler(ClientOpcodes.SetLootSpecialization)]
|
||||||
void HandleSetLootSpecialization(SetLootSpecialization packet)
|
void HandleSetLootSpecialization(SetLootSpecialization packet)
|
||||||
{
|
{
|
||||||
|
|||||||
+715
-98
@@ -16,14 +16,15 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
using Framework.Constants;
|
using Framework.Constants;
|
||||||
using Framework.Dynamic;
|
|
||||||
using Game.Conditions;
|
using Game.Conditions;
|
||||||
|
using Game.DataStorage;
|
||||||
using Game.Entities;
|
using Game.Entities;
|
||||||
using Game.Groups;
|
using Game.Groups;
|
||||||
|
using Game.Maps;
|
||||||
using Game.Networking.Packets;
|
using Game.Networking.Packets;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Game.Maps;
|
using System.Linq;
|
||||||
|
|
||||||
namespace Game.Loots
|
namespace Game.Loots
|
||||||
{
|
{
|
||||||
@@ -37,7 +38,7 @@ namespace Game.Loots
|
|||||||
|
|
||||||
ItemTemplate proto = Global.ObjectMgr.GetItemTemplate(itemid);
|
ItemTemplate proto = Global.ObjectMgr.GetItemTemplate(itemid);
|
||||||
freeforall = proto != null && proto.HasFlag(ItemFlags.MultiDrop);
|
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;
|
needs_quest = li.needs_quest;
|
||||||
|
|
||||||
@@ -110,7 +111,7 @@ namespace Game.Loots
|
|||||||
public List<ObjectGuid> GetAllowedLooters() { return allowedGUIDs; }
|
public List<ObjectGuid> GetAllowedLooters() { return allowedGUIDs; }
|
||||||
|
|
||||||
public uint itemid;
|
public uint itemid;
|
||||||
public uint itemIndex;
|
public uint LootListId;
|
||||||
public uint randomBonusListId;
|
public uint randomBonusListId;
|
||||||
public List<uint> BonusListIDs = new();
|
public List<uint> BonusListIDs = new();
|
||||||
public ItemContext context;
|
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()
|
||||||
{
|
{
|
||||||
|
Vote = RollVote.NotValid;
|
||||||
}
|
RollNumber = 0;
|
||||||
|
|
||||||
public override void SourceObjectDestroyLink()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class LootValidatorRefManager : RefManager<Loot, LootValidatorRef>
|
public class LootRoll
|
||||||
{
|
{
|
||||||
public new LootValidatorRef GetFirst() { return (LootValidatorRef)base.GetFirst(); }
|
static TimeSpan LOOT_ROLL_TIMEOUT = TimeSpan.FromMinutes(1);
|
||||||
public new LootValidatorRef GetLast() { return (LootValidatorRef)base.GetLast(); }
|
|
||||||
|
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 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;
|
loot_type = type;
|
||||||
maxDuplicates = 1;
|
maxDuplicates = 1;
|
||||||
_guid = map ? ObjectGuid.Create(HighGuid.LootObject, map.GetId(), 0, map.GenerateLowGuid(HighGuid.LootObject)) : ObjectGuid.Empty;
|
_guid = map ? ObjectGuid.Create(HighGuid.LootObject, map.GetId(), 0, map.GenerateLowGuid(HighGuid.LootObject)) : ObjectGuid.Empty;
|
||||||
_owner = owner;
|
_owner = owner;
|
||||||
_itemContext = ItemContext.None;
|
_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)
|
// Inserts the item into the loot (called by LootTemplate processors)
|
||||||
@@ -194,7 +610,7 @@ namespace Game.Loots
|
|||||||
LootItem generatedLoot = new(item);
|
LootItem generatedLoot = new(item);
|
||||||
generatedLoot.context = _itemContext;
|
generatedLoot.context = _itemContext;
|
||||||
generatedLoot.count = (byte)Math.Min(count, proto.GetMaxStackSize());
|
generatedLoot.count = (byte)Math.Min(count, proto.GetMaxStackSize());
|
||||||
generatedLoot.itemIndex = (uint)lootItems.Count;
|
generatedLoot.LootListId = (uint)lootItems.Count;
|
||||||
if (_itemContext != 0)
|
if (_itemContext != 0)
|
||||||
{
|
{
|
||||||
List<uint> bonusListIDs = Global.DB2Mgr.GetDefaultItemBonusTree(generatedLoot.itemid, _itemContext);
|
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)
|
public LootItem GetItemInSlot(uint lootSlot)
|
||||||
{
|
{
|
||||||
if (lootSlot < items.Count)
|
if (lootSlot < items.Count)
|
||||||
@@ -271,28 +744,69 @@ namespace Game.Loots
|
|||||||
{
|
{
|
||||||
Player player = refe.GetSource();
|
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) // should actually be looted object instead of lootOwner but looter has to be really close so doesnt really matter
|
||||||
if (player.IsInMap(lootOwner))
|
if (player.IsAtGroupRewardDistance(lootOwner))
|
||||||
FillNotNormalLootFor(player, 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 != null)
|
||||||
|
{
|
||||||
if (proto.GetQuality() < group.GetLootThreshold())
|
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
|
// ... for personal loot
|
||||||
else
|
else
|
||||||
FillNotNormalLootFor(lootOwner, true);
|
FillNotNormalLootFor(lootOwner);
|
||||||
|
|
||||||
return true;
|
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();
|
ObjectGuid plguid = player.GetGUID();
|
||||||
|
_allowedLooters.Add(plguid);
|
||||||
|
|
||||||
var questItemList = PlayerQuestItems.LookupByKey(plguid);
|
var questItemList = PlayerQuestItems.LookupByKey(plguid);
|
||||||
if (questItemList.Empty())
|
if (questItemList.Empty())
|
||||||
@@ -304,7 +818,7 @@ namespace Game.Loots
|
|||||||
|
|
||||||
questItemList = PlayerNonQuestNonFFAConditionalItems.LookupByKey(plguid);
|
questItemList = PlayerNonQuestNonFFAConditionalItems.LookupByKey(plguid);
|
||||||
if (questItemList.Empty())
|
if (questItemList.Empty())
|
||||||
FillNonQuestNonFFAConditionalLoot(player, presentAtLooting);
|
FillNonQuestNonFFAConditionalLoot(player);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<NotNormalLootItem> FillFFALoot(Player 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))))
|
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));
|
ql.Add(new NotNormalLootItem(i));
|
||||||
|
|
||||||
// quest items get blocked when they first appear in a
|
// 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
|
// increase once if one looter only, looter-times if free for all
|
||||||
if (item.freeforall || !item.is_blocked)
|
if (item.freeforall || !item.is_blocked)
|
||||||
++unlootedCount;
|
++unlootedCount;
|
||||||
if (!player.GetGroup() || GetLootMethod() != LootMethod.GroupLoot)
|
if (!player.GetGroup() || GetLootMethod() != LootMethod.GroupLoot && GetLootMethod() != LootMethod.RoundRobin)
|
||||||
item.is_blocked = true;
|
item.is_blocked = true;
|
||||||
|
|
||||||
if (items.Count + ql.Count == SharedConst.MaxNRLootItems)
|
if (items.Count + ql.Count == SharedConst.MaxNRLootItems)
|
||||||
@@ -363,7 +879,7 @@ namespace Game.Loots
|
|||||||
return ql;
|
return ql;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<NotNormalLootItem> FillNonQuestNonFFAConditionalLoot(Player player, bool presentAtLooting)
|
List<NotNormalLootItem> FillNonQuestNonFFAConditionalLoot(Player player)
|
||||||
{
|
{
|
||||||
List<NotNormalLootItem> ql = new();
|
List<NotNormalLootItem> ql = new();
|
||||||
|
|
||||||
@@ -372,8 +888,7 @@ namespace Game.Loots
|
|||||||
LootItem item = items[i];
|
LootItem item = items[i];
|
||||||
if (!item.is_looted && !item.freeforall && item.AllowedForPlayer(player))
|
if (!item.is_looted && !item.freeforall && item.AllowedForPlayer(player))
|
||||||
{
|
{
|
||||||
if (presentAtLooting)
|
item.AddAllowedLooter(player);
|
||||||
item.AddAllowedLooter(player);
|
|
||||||
if (!item.conditions.Empty())
|
if (!item.conditions.Empty())
|
||||||
{
|
{
|
||||||
ql.Add(new NotNormalLootItem(i));
|
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)
|
public void GenerateMoneyLoot(uint minAmount, uint maxAmount)
|
||||||
{
|
{
|
||||||
if (maxAmount > 0)
|
if (maxAmount > 0)
|
||||||
@@ -619,82 +1193,102 @@ namespace Game.Loots
|
|||||||
case PermissionTypes.Group:
|
case PermissionTypes.Group:
|
||||||
case PermissionTypes.Master:
|
case PermissionTypes.Master:
|
||||||
case PermissionTypes.Restricted:
|
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
|
if (!items[i].is_looted && !items[i].freeforall && items[i].conditions.Empty() && items[i].AllowedForPlayer(viewer))
|
||||||
// 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))
|
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:
|
if (viewer.GetGroup() && viewer.GetGroup().GetMasterLooterGuid() == viewer.GetGUID())
|
||||||
slot_type = LootSlotType.RollOngoing;
|
slot_type = LootSlotType.Master;
|
||||||
break;
|
else
|
||||||
case PermissionTypes.Master:
|
|
||||||
{
|
|
||||||
if (viewer.GetGroup() && viewer.GetGroup().GetMasterLooterGuid() == viewer.GetGUID())
|
|
||||||
slot_type = LootSlotType.Master;
|
|
||||||
else
|
|
||||||
slot_type = LootSlotType.Locked;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case PermissionTypes.Restricted:
|
|
||||||
slot_type = LootSlotType.Locked;
|
slot_type = LootSlotType.Locked;
|
||||||
break;
|
break;
|
||||||
default:
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
}
|
case PermissionTypes.Restricted:
|
||||||
else if (roundRobinPlayer.IsEmpty() || viewer.GetGUID() == roundRobinPlayer || !items[i].is_underthreshold)
|
slot_type = LootSlotType.Locked;
|
||||||
{
|
break;
|
||||||
// no round robin owner or he has released the loot
|
default:
|
||||||
// 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;
|
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.All:
|
||||||
case PermissionTypes.Owner:
|
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);
|
||||||
LootItemData lootItem = new();
|
lootItem.UIType = (permission == PermissionTypes.Owner ? LootSlotType.Owner : LootSlotType.AllowLoot);
|
||||||
lootItem.LootListID = (byte)(i + 1);
|
lootItem.Quantity = items[i].count;
|
||||||
lootItem.UIType = (permission == PermissionTypes.Owner ? LootSlotType.Owner : LootSlotType.AllowLoot);
|
lootItem.Loot = new ItemInstance(items[i]);
|
||||||
lootItem.Quantity = items[i].count;
|
packet.Items.Add(lootItem);
|
||||||
lootItem.Loot = new ItemInstance(items[i]);
|
|
||||||
packet.Items.Add(lootItem);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -724,6 +1318,7 @@ namespace Game.Loots
|
|||||||
lootItem.UIType = item.is_blocked ? LootSlotType.Locked : LootSlotType.AllowLoot;
|
lootItem.UIType = item.is_blocked ? LootSlotType.Locked : LootSlotType.AllowLoot;
|
||||||
break;
|
break;
|
||||||
case PermissionTypes.Group:
|
case PermissionTypes.Group:
|
||||||
|
case PermissionTypes.RoundRobin:
|
||||||
if (!item.is_blocked)
|
if (!item.is_blocked)
|
||||||
lootItem.UIType = LootSlotType.AllowLoot;
|
lootItem.UIType = LootSlotType.AllowLoot;
|
||||||
else
|
else
|
||||||
@@ -783,6 +1378,7 @@ namespace Game.Loots
|
|||||||
lootItem.UIType = item.is_blocked ? LootSlotType.Locked : LootSlotType.AllowLoot;
|
lootItem.UIType = item.is_blocked ? LootSlotType.Locked : LootSlotType.AllowLoot;
|
||||||
break;
|
break;
|
||||||
case PermissionTypes.Group:
|
case PermissionTypes.Group:
|
||||||
|
case PermissionTypes.RoundRobin:
|
||||||
if (!item.is_blocked)
|
if (!item.is_blocked)
|
||||||
lootItem.UIType = LootSlotType.AllowLoot;
|
lootItem.UIType = LootSlotType.AllowLoot;
|
||||||
else
|
else
|
||||||
@@ -802,11 +1398,6 @@ namespace Game.Loots
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddLootValidatorRef(LootValidatorRef pLootValidatorRef)
|
|
||||||
{
|
|
||||||
i_LootValidatorRefManager.InsertFirst(pLootValidatorRef);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Clear()
|
public void Clear()
|
||||||
{
|
{
|
||||||
PlayerQuestItems.Clear();
|
PlayerQuestItems.Clear();
|
||||||
@@ -828,8 +1419,31 @@ namespace Game.Loots
|
|||||||
gold = 0;
|
gold = 0;
|
||||||
unlootedCount = 0;
|
unlootedCount = 0;
|
||||||
roundRobinPlayer = ObjectGuid.Empty;
|
roundRobinPlayer = ObjectGuid.Empty;
|
||||||
i_LootValidatorRefManager.ClearReferences();
|
|
||||||
_itemContext = 0;
|
_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; }
|
public bool Empty() { return items.Empty() && gold == 0; }
|
||||||
@@ -841,7 +1455,9 @@ namespace Game.Loots
|
|||||||
public ObjectGuid GetGUID() { return _guid; }
|
public ObjectGuid GetGUID() { return _guid; }
|
||||||
public ObjectGuid GetOwnerGUID() { return _owner; }
|
public ObjectGuid GetOwnerGUID() { return _owner; }
|
||||||
public LootMethod GetLootMethod() { return _lootMethod; }
|
public LootMethod GetLootMethod() { return _lootMethod; }
|
||||||
|
|
||||||
|
public ObjectGuid GetLootMasterGUID() { return _lootMaster; }
|
||||||
|
|
||||||
public MultiMap<ObjectGuid, NotNormalLootItem> GetPlayerQuestItems() { return PlayerQuestItems; }
|
public MultiMap<ObjectGuid, NotNormalLootItem> GetPlayerQuestItems() { return PlayerQuestItems; }
|
||||||
public MultiMap<ObjectGuid, NotNormalLootItem> GetPlayerFFAItems() { return PlayerFFAItems; }
|
public MultiMap<ObjectGuid, NotNormalLootItem> GetPlayerFFAItems() { return PlayerFFAItems; }
|
||||||
public MultiMap<ObjectGuid, NotNormalLootItem> GetPlayerNonQuestNonFFAConditionalItems() { return PlayerNonQuestNonFFAConditionalItems; }
|
public MultiMap<ObjectGuid, NotNormalLootItem> GetPlayerNonQuestNonFFAConditionalItems() { return PlayerNonQuestNonFFAConditionalItems; }
|
||||||
@@ -859,14 +1475,15 @@ namespace Game.Loots
|
|||||||
MultiMap<ObjectGuid, NotNormalLootItem> PlayerFFAItems = new();
|
MultiMap<ObjectGuid, NotNormalLootItem> PlayerFFAItems = new();
|
||||||
MultiMap<ObjectGuid, NotNormalLootItem> PlayerNonQuestNonFFAConditionalItems = 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
|
// Loot GUID
|
||||||
ObjectGuid _guid;
|
ObjectGuid _guid;
|
||||||
ObjectGuid _owner; // The WorldObject that holds this loot
|
ObjectGuid _owner; // The WorldObject that holds this loot
|
||||||
ItemContext _itemContext;
|
ItemContext _itemContext;
|
||||||
LootMethod _lootMethod;
|
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
|
public class AELootResult
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ namespace Game.Loots
|
|||||||
LootItem lootItem = new();
|
LootItem lootItem = new();
|
||||||
lootItem.itemid = result.Read<uint>(1);
|
lootItem.itemid = result.Read<uint>(1);
|
||||||
lootItem.count = result.Read<byte>(2);
|
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.follow_loot_rules = result.Read<bool>(4);
|
||||||
lootItem.freeforall = result.Read<bool>(5);
|
lootItem.freeforall = result.Read<bool>(5);
|
||||||
lootItem.is_blocked = result.Read<bool>(6);
|
lootItem.is_blocked = result.Read<bool>(6);
|
||||||
@@ -114,7 +114,7 @@ namespace Game.Loots
|
|||||||
LootItem li = new();
|
LootItem li = new();
|
||||||
li.itemid = id;
|
li.itemid = id;
|
||||||
li.count = (byte)storedItem.Count;
|
li.count = (byte)storedItem.Count;
|
||||||
li.itemIndex = storedItem.ItemIndex;
|
li.LootListId = storedItem.ItemIndex;
|
||||||
li.follow_loot_rules = storedItem.FollowRules;
|
li.follow_loot_rules = storedItem.FollowRules;
|
||||||
li.freeforall = storedItem.FFA;
|
li.freeforall = storedItem.FFA;
|
||||||
li.is_blocked = storedItem.Blocked;
|
li.is_blocked = storedItem.Blocked;
|
||||||
@@ -244,7 +244,7 @@ namespace Game.Loots
|
|||||||
stmt.AddValue(0, _containerId);
|
stmt.AddValue(0, _containerId);
|
||||||
stmt.AddValue(1, lootItem.itemid);
|
stmt.AddValue(1, lootItem.itemid);
|
||||||
stmt.AddValue(2, lootItem.count);
|
stmt.AddValue(2, lootItem.count);
|
||||||
stmt.AddValue(3, lootItem.itemIndex);
|
stmt.AddValue(3, lootItem.LootListId);
|
||||||
stmt.AddValue(4, lootItem.follow_loot_rules);
|
stmt.AddValue(4, lootItem.follow_loot_rules);
|
||||||
stmt.AddValue(5, lootItem.freeforall);
|
stmt.AddValue(5, lootItem.freeforall);
|
||||||
stmt.AddValue(6, lootItem.is_blocked);
|
stmt.AddValue(6, lootItem.is_blocked);
|
||||||
@@ -325,7 +325,7 @@ namespace Game.Loots
|
|||||||
{
|
{
|
||||||
ItemId = lootItem.itemid;
|
ItemId = lootItem.itemid;
|
||||||
Count = lootItem.count;
|
Count = lootItem.count;
|
||||||
ItemIndex = lootItem.itemIndex;
|
ItemIndex = lootItem.LootListId;
|
||||||
FollowRules = lootItem.follow_loot_rules;
|
FollowRules = lootItem.follow_loot_rules;
|
||||||
FFA = lootItem.freeforall;
|
FFA = lootItem.freeforall;
|
||||||
Blocked = lootItem.is_blocked;
|
Blocked = lootItem.is_blocked;
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ namespace Game.Mails
|
|||||||
if (m_mailTemplateId == 123)
|
if (m_mailTemplateId == 123)
|
||||||
m_money = 1000000;
|
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
|
// can be empty
|
||||||
mailLoot.FillLoot(m_mailTemplateId, LootStorage.Mail, receiver, true, true, LootModes.Default, ItemContext.None);
|
mailLoot.FillLoot(m_mailTemplateId, LootStorage.Mail, receiver, true, true, LootModes.Default, ItemContext.None);
|
||||||
|
|||||||
@@ -188,20 +188,20 @@ namespace Game.Networking.Packets
|
|||||||
public ObjectGuid LootObj;
|
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()
|
public override void Read()
|
||||||
{
|
{
|
||||||
LootObj = _worldPacket.ReadPackedGuid();
|
LootObj = _worldPacket.ReadPackedGuid();
|
||||||
LootListID = _worldPacket.ReadUInt8();
|
LootListID = _worldPacket.ReadUInt8();
|
||||||
RollType = (RollType)_worldPacket.ReadUInt8();
|
RollType = (RollVote)_worldPacket.ReadUInt8();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ObjectGuid LootObj;
|
public ObjectGuid LootObj;
|
||||||
public byte LootListID;
|
public byte LootListID;
|
||||||
public RollType RollType;
|
public RollVote RollType;
|
||||||
}
|
}
|
||||||
|
|
||||||
class LootReleaseResponse : ServerPacket
|
class LootReleaseResponse : ServerPacket
|
||||||
@@ -303,7 +303,7 @@ namespace Game.Networking.Packets
|
|||||||
public ObjectGuid LootObj;
|
public ObjectGuid LootObj;
|
||||||
public ObjectGuid Player;
|
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 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 LootItemData Item = new();
|
||||||
public bool Autopassed; // Triggers message |HlootHistory:%d|h[Loot]|h: You automatically passed on: %s because you cannot loot that item.
|
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 LootObj;
|
||||||
public ObjectGuid Winner;
|
public ObjectGuid Winner;
|
||||||
public int Roll;
|
public int Roll;
|
||||||
public RollType RollType;
|
public RollVote RollType;
|
||||||
public LootItemData Item = new();
|
public LootItemData Item = new();
|
||||||
public bool MainSpec;
|
public bool MainSpec;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -193,7 +193,6 @@ namespace Game
|
|||||||
if (group != null)
|
if (group != null)
|
||||||
{
|
{
|
||||||
group.SendUpdate();
|
group.SendUpdate();
|
||||||
group.ResetMaxEnchantingLevel();
|
|
||||||
if (group.GetLeaderGUID() == _player.GetGUID())
|
if (group.GetLeaderGUID() == _player.GetGUID())
|
||||||
group.StartLeaderOfflineTimer();
|
group.StartLeaderOfflineTimer();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user