Core/Loot: Implemented personal loot and tag sharing for non-boss loot
Port From (https://github.com/TrinityCore/TrinityCore/commit/133334a902b705dae6f7e92bb1009b84cf1c51d2)
This commit is contained in:
@@ -200,6 +200,7 @@ namespace Framework.Constants
|
|||||||
public const int BoundaryVisualizeFailsafeLimit = 750;
|
public const int BoundaryVisualizeFailsafeLimit = 750;
|
||||||
public const int BoundaryVisualizeSpawnHeight = 5;
|
public const int BoundaryVisualizeSpawnHeight = 5;
|
||||||
public const uint AIDefaultCooldown = 5000;
|
public const uint AIDefaultCooldown = 5000;
|
||||||
|
public const uint CreatureTappersSoftCap = 5;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// GameObject Const
|
/// GameObject Const
|
||||||
|
|||||||
@@ -578,7 +578,7 @@ namespace Framework.Constants
|
|||||||
TrackUnit = 0x08,
|
TrackUnit = 0x08,
|
||||||
Tapped = 0x10, // Lua_UnitIsTapped
|
Tapped = 0x10, // Lua_UnitIsTapped
|
||||||
SpecialInfo = 0x20,
|
SpecialInfo = 0x20,
|
||||||
Unused = 0x40, // previously UNIT_DYNFLAG_DEAD
|
CanSkin = 0x40, // previously UNIT_DYNFLAG_DEAD
|
||||||
ReferAFriend = 0x80
|
ReferAFriend = 0x80
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -309,7 +309,7 @@ namespace Game.AI
|
|||||||
|
|
||||||
// sometimes bosses stuck in combat?
|
// sometimes bosses stuck in combat?
|
||||||
me.CombatStop(true);
|
me.CombatStop(true);
|
||||||
me.SetLootRecipient(null);
|
me.SetTappedBy(null);
|
||||||
me.ResetPlayerDamageReq();
|
me.ResetPlayerDamageReq();
|
||||||
me.SetLastDamagedTime(0);
|
me.SetLastDamagedTime(0);
|
||||||
me.SetCannotReachTarget(false);
|
me.SetCannotReachTarget(false);
|
||||||
|
|||||||
@@ -211,7 +211,7 @@ namespace Game.AI
|
|||||||
if (reset)
|
if (reset)
|
||||||
{
|
{
|
||||||
who.LoadCreaturesAddon();
|
who.LoadCreaturesAddon();
|
||||||
who.SetLootRecipient(null);
|
who.SetTappedBy(null);
|
||||||
who.ResetPlayerDamageReq();
|
who.ResetPlayerDamageReq();
|
||||||
who.SetLastDamagedTime(0);
|
who.SetLastDamagedTime(0);
|
||||||
who.SetCannotReachTarget(false);
|
who.SetCannotReachTarget(false);
|
||||||
@@ -596,7 +596,7 @@ namespace Game.AI
|
|||||||
if (reset)
|
if (reset)
|
||||||
{
|
{
|
||||||
creature.LoadCreaturesAddon();
|
creature.LoadCreaturesAddon();
|
||||||
creature.SetLootRecipient(null);
|
creature.SetTappedBy(null);
|
||||||
creature.ResetPlayerDamageReq();
|
creature.ResetPlayerDamageReq();
|
||||||
creature.SetLastDamagedTime(0);
|
creature.SetLastDamagedTime(0);
|
||||||
creature.SetCannotReachTarget(false);
|
creature.SetCannotReachTarget(false);
|
||||||
|
|||||||
@@ -135,7 +135,7 @@ namespace Game.AI
|
|||||||
{
|
{
|
||||||
me.RemoveAllAuras();
|
me.RemoveAllAuras();
|
||||||
me.CombatStop(true);
|
me.CombatStop(true);
|
||||||
me.SetLootRecipient(null);
|
me.SetTappedBy(null);
|
||||||
|
|
||||||
EngagementOver();
|
EngagementOver();
|
||||||
|
|
||||||
|
|||||||
@@ -826,12 +826,14 @@ namespace Game.AI
|
|||||||
if (_me == null)
|
if (_me == null)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
Player player = _me.GetLootRecipient();
|
foreach (ObjectGuid tapperGuid in _me.GetTapList())
|
||||||
if (player != null)
|
|
||||||
{
|
{
|
||||||
player.RewardPlayerAndGroupAtEvent(e.Action.killedMonster.creature, player);
|
Player tapper = Global.ObjAccessor.GetPlayer(_me, tapperGuid);
|
||||||
Log.outDebug(LogFilter.ScriptsAi, "SmartScript.ProcessAction: SMART_ACTION_CALL_KILLEDMONSTER: Player {0}, Killcredit: {1}",
|
if (tapper != null)
|
||||||
player.GetGUID().ToString(), e.Action.killedMonster.creature);
|
{
|
||||||
|
tapper.KilledMonsterCredit(e.Action.killedMonster.creature, _me.GetGUID());
|
||||||
|
Log.outDebug(LogFilter.ScriptsAi, $"SmartScript::ProcessAction: SMART_ACTION_CALL_KILLEDMONSTER: Player {tapper.GetGUID()}, Killcredit: {e.Action.killedMonster.creature}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else // Specific target type
|
else // Specific target type
|
||||||
@@ -2984,22 +2986,11 @@ namespace Game.AI
|
|||||||
{
|
{
|
||||||
if (_me)
|
if (_me)
|
||||||
{
|
{
|
||||||
Group lootGroup = _me.GetLootRecipientGroup();
|
foreach (ObjectGuid tapperGuid in _me.GetTapList())
|
||||||
if (lootGroup)
|
|
||||||
{
|
{
|
||||||
for (GroupReference refe = lootGroup.GetFirstMember(); refe != null; refe = refe.Next())
|
Player tapper = Global.ObjAccessor.GetPlayer(_me, tapperGuid);
|
||||||
{
|
if (tapper != null)
|
||||||
Player recipient = refe.GetSource();
|
targets.Add(tapper);
|
||||||
if (recipient)
|
|
||||||
if (recipient.IsInMap(_me))
|
|
||||||
targets.Add(recipient);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Player recipient = _me.GetLootRecipient();
|
|
||||||
if (recipient)
|
|
||||||
targets.Add(recipient);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -1855,7 +1855,7 @@ namespace Game.BattleGrounds
|
|||||||
void RewardXPAtKill(Player killer, Player victim)
|
void RewardXPAtKill(Player killer, Player victim)
|
||||||
{
|
{
|
||||||
if (WorldConfig.GetBoolValue(WorldCfg.BgXpForKill) && killer && victim)
|
if (WorldConfig.GetBoolValue(WorldCfg.BgXpForKill) && killer && victim)
|
||||||
killer.RewardPlayerAndGroupAtKill(victim, true);
|
new KillRewarder(new[] { killer }, victim, true).Reward();
|
||||||
}
|
}
|
||||||
|
|
||||||
public uint GetTeamScore(int teamIndex)
|
public uint GetTeamScore(int teamIndex)
|
||||||
|
|||||||
@@ -626,8 +626,13 @@ namespace Game.Chat
|
|||||||
if (!target)
|
if (!target)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
handler.SendSysMessage("Loot recipient for creature {0} (GUID {1}, SpawnID {2}) is {3}", target.GetName(), target.GetGUID().ToString(), target.GetSpawnId(),
|
handler.SendSysMessage($"Loot recipients for creature {target.GetName()} ({target.GetGUID()}, SpawnID {target.GetSpawnId()}) are:");
|
||||||
target.HasLootRecipient() ? (target.GetLootRecipient() ? target.GetLootRecipient().GetName() : "offline") : "no loot recipient");
|
|
||||||
|
foreach (ObjectGuid tapperGuid in target.GetTapList())
|
||||||
|
{
|
||||||
|
Player tapper = Global.ObjAccessor.GetPlayer(target, tapperGuid);
|
||||||
|
handler.SendSysMessage($"* {(tapper != null ? tapper.GetName() : "offline")}");
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -324,8 +324,8 @@ namespace Game.Chat
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Loot loot = creatureTarget.loot;
|
Loot loot = creatureTarget._loot;
|
||||||
if (!creatureTarget.IsDead() || loot == null || loot.Empty())
|
if (!creatureTarget.IsDead() || loot == null || loot.IsLooted())
|
||||||
{
|
{
|
||||||
handler.SendSysMessage(CypherStrings.CommandNotDeadOrNoLoot, creatureTarget.GetName());
|
handler.SendSysMessage(CypherStrings.CommandNotDeadOrNoLoot, creatureTarget.GetName());
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -90,9 +90,9 @@ namespace Game.Entities
|
|||||||
// vendor items
|
// vendor items
|
||||||
List<VendorItemCount> m_vendorItemCounts = new();
|
List<VendorItemCount> m_vendorItemCounts = new();
|
||||||
|
|
||||||
public Loot loot;
|
internal Dictionary<ObjectGuid, Loot> m_personalLoot = new();
|
||||||
ObjectGuid m_lootRecipient;
|
public Loot _loot;
|
||||||
ObjectGuid m_lootRecipientGroup;
|
List<ObjectGuid> m_tapList = new();
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum ObjectCellMoveState
|
public enum ObjectCellMoveState
|
||||||
|
|||||||
@@ -160,7 +160,7 @@ namespace Game.Entities
|
|||||||
SetDeathState(DeathState.Dead);
|
SetDeathState(DeathState.Dead);
|
||||||
RemoveAllAuras();
|
RemoveAllAuras();
|
||||||
//DestroyForNearbyPlayers(); // old UpdateObjectVisibility()
|
//DestroyForNearbyPlayers(); // old UpdateObjectVisibility()
|
||||||
loot = null;
|
_loot = null;
|
||||||
uint respawnDelay = m_respawnDelay;
|
uint respawnDelay = m_respawnDelay;
|
||||||
CreatureAI ai = GetAI();
|
CreatureAI ai = GetAI();
|
||||||
if (ai != null)
|
if (ai != null)
|
||||||
@@ -507,7 +507,10 @@ namespace Game.Entities
|
|||||||
if (IsEngaged())
|
if (IsEngaged())
|
||||||
AIUpdateTick(diff);
|
AIUpdateTick(diff);
|
||||||
|
|
||||||
loot?.Update();
|
_loot?.Update();
|
||||||
|
|
||||||
|
foreach (var (playerOwner, loot) in m_personalLoot)
|
||||||
|
loot.Update();
|
||||||
|
|
||||||
if (m_corpseRemoveTime <= GameTime.GetGameTime())
|
if (m_corpseRemoveTime <= GameTime.GetGameTime())
|
||||||
{
|
{
|
||||||
@@ -1165,24 +1168,8 @@ namespace Game.Entities
|
|||||||
}
|
}
|
||||||
public void ResetPickPocketRefillTimer() { _pickpocketLootRestore = 0; }
|
public void ResetPickPocketRefillTimer() { _pickpocketLootRestore = 0; }
|
||||||
public bool CanGeneratePickPocketLoot() { return _pickpocketLootRestore <= GameTime.GetGameTime(); }
|
public bool CanGeneratePickPocketLoot() { return _pickpocketLootRestore <= GameTime.GetGameTime(); }
|
||||||
public ObjectGuid GetLootRecipientGUID() { return m_lootRecipient; }
|
|
||||||
|
|
||||||
public Player GetLootRecipient()
|
public void SetTappedBy(Unit unit, bool withGroup = true)
|
||||||
{
|
|
||||||
if (m_lootRecipient.IsEmpty())
|
|
||||||
return null;
|
|
||||||
return Global.ObjAccessor.FindPlayer(m_lootRecipient);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Group GetLootRecipientGroup()
|
|
||||||
{
|
|
||||||
if (m_lootRecipientGroup.IsEmpty())
|
|
||||||
return null;
|
|
||||||
|
|
||||||
return Global.GroupMgr.GetGroupByGUID(m_lootRecipientGroup);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetLootRecipient(Unit unit, bool withGroup = true)
|
|
||||||
{
|
{
|
||||||
// set the player whose group should receive the right
|
// set the player whose group should receive the right
|
||||||
// to loot the creature after it dies
|
// to loot the creature after it dies
|
||||||
@@ -1190,12 +1177,14 @@ namespace Game.Entities
|
|||||||
|
|
||||||
if (unit == null)
|
if (unit == null)
|
||||||
{
|
{
|
||||||
m_lootRecipient.Clear();
|
m_tapList.Clear();
|
||||||
m_lootRecipientGroup.Clear();
|
|
||||||
RemoveDynamicFlag(UnitDynFlags.Lootable | UnitDynFlags.Tapped);
|
RemoveDynamicFlag(UnitDynFlags.Lootable | UnitDynFlags.Tapped);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_tapList.Count >= SharedConst.CreatureTappersSoftCap)
|
||||||
|
return;
|
||||||
|
|
||||||
if (!unit.IsTypeId(TypeId.Player) && !unit.IsVehicle())
|
if (!unit.IsTypeId(TypeId.Player) && !unit.IsVehicle())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -1203,31 +1192,62 @@ namespace Game.Entities
|
|||||||
if (player == null) // normal creature, no player involved
|
if (player == null) // normal creature, no player involved
|
||||||
return;
|
return;
|
||||||
|
|
||||||
m_lootRecipient = player.GetGUID();
|
m_tapList.Add(player.GetGUID());
|
||||||
if (withGroup)
|
if (withGroup)
|
||||||
{
|
{
|
||||||
Group group = player.GetGroup();
|
Group group = player.GetGroup();
|
||||||
if (group)
|
if (group != null)
|
||||||
m_lootRecipientGroup = group.GetGUID();
|
for (var itr = group.GetFirstMember(); itr != null; itr = itr.Next())
|
||||||
|
if (GetMap().IsRaid() || group.SameSubGroup(player, itr.GetSource()))
|
||||||
|
m_tapList.Add(itr.GetSource().GetGUID());
|
||||||
}
|
}
|
||||||
else
|
|
||||||
m_lootRecipientGroup = ObjectGuid.Empty;
|
|
||||||
|
|
||||||
SetDynamicFlag(UnitDynFlags.Tapped);
|
if (m_tapList.Count >= SharedConst.CreatureTappersSoftCap)
|
||||||
|
SetDynamicFlag(UnitDynFlags.Tapped);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsTappedBy(Player player)
|
public bool IsTappedBy(Player player)
|
||||||
{
|
{
|
||||||
if (player.GetGUID() == m_lootRecipient)
|
return m_tapList.Contains(player.GetGUID());
|
||||||
return true;
|
}
|
||||||
|
|
||||||
Group playerGroup = player.GetGroup();
|
public override Loot GetLootForPlayer(Player player)
|
||||||
if (!playerGroup || playerGroup != GetLootRecipientGroup()) // if we dont have a group we arent the recipient
|
{
|
||||||
return false; // if creature doesnt have group bound it means it was solo killed by someone else
|
if (m_personalLoot.Empty())
|
||||||
|
return _loot;
|
||||||
|
|
||||||
|
var loot = m_personalLoot.LookupByKey(player.GetGUID());
|
||||||
|
if (loot != null)
|
||||||
|
return loot;
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsFullyLooted()
|
||||||
|
{
|
||||||
|
if (_loot != null && !_loot.IsLooted())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
foreach (var (_, loot) in m_personalLoot)
|
||||||
|
if (!loot.IsLooted())
|
||||||
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool IsSkinnedBy(Player player)
|
||||||
|
{
|
||||||
|
Loot loot = GetLootForPlayer(player);
|
||||||
|
if (loot != null)
|
||||||
|
return loot.loot_type == LootType.Skinning;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ObjectGuid> GetTapList() { return m_tapList; }
|
||||||
|
public void SetTapList(List<ObjectGuid> tapList) { m_tapList = tapList; }
|
||||||
|
public bool HasLootRecipient() { return !m_tapList.Empty(); }
|
||||||
|
|
||||||
public void SaveToDB()
|
public void SaveToDB()
|
||||||
{
|
{
|
||||||
// this should only be used when the creature has already been loaded
|
// this should only be used when the creature has already been loaded
|
||||||
@@ -1867,7 +1887,7 @@ namespace Game.Entities
|
|||||||
else
|
else
|
||||||
SetSpawnHealth();
|
SetSpawnHealth();
|
||||||
|
|
||||||
SetLootRecipient(null);
|
SetTappedBy(null);
|
||||||
ResetPlayerDamageReq();
|
ResetPlayerDamageReq();
|
||||||
|
|
||||||
SetCannotReachTarget(false);
|
SetCannotReachTarget(false);
|
||||||
@@ -1926,7 +1946,7 @@ namespace Game.Entities
|
|||||||
Log.outDebug(LogFilter.Unit, "Respawning creature {0} ({1})", GetName(), GetGUID().ToString());
|
Log.outDebug(LogFilter.Unit, "Respawning creature {0} ({1})", GetName(), GetGUID().ToString());
|
||||||
m_respawnTime = 0;
|
m_respawnTime = 0;
|
||||||
ResetPickPocketRefillTimer();
|
ResetPickPocketRefillTimer();
|
||||||
loot = null;
|
_loot = null;
|
||||||
|
|
||||||
if (m_originalEntry != GetEntry())
|
if (m_originalEntry != GetEntry())
|
||||||
UpdateEntry(m_originalEntry);
|
UpdateEntry(m_originalEntry);
|
||||||
@@ -2560,13 +2580,9 @@ namespace Game.Entities
|
|||||||
{
|
{
|
||||||
return !_isMissingCanSwimFlagOutOfCombat;
|
return !_isMissingCanSwimFlagOutOfCombat;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AllLootRemovedFromCorpse()
|
public void AllLootRemovedFromCorpse()
|
||||||
{
|
{
|
||||||
if ((loot == null || loot.loot_type != LootType.Skinning) && !IsPet() && GetCreatureTemplate().SkinLootId != 0 && HasLootRecipient())
|
|
||||||
if (LootStorage.Skinning.HaveLootFor(GetCreatureTemplate().SkinLootId))
|
|
||||||
SetUnitFlag(UnitFlags.Skinnable);
|
|
||||||
|
|
||||||
long now = GameTime.GetGameTime();
|
long now = GameTime.GetGameTime();
|
||||||
// Do not reset corpse remove time if corpse is already removed
|
// Do not reset corpse remove time if corpse is already removed
|
||||||
if (m_corpseRemoveTime <= now)
|
if (m_corpseRemoveTime <= now)
|
||||||
@@ -2576,7 +2592,19 @@ namespace Game.Entities
|
|||||||
float decayRate = m_ignoreCorpseDecayRatio ? 1.0f : WorldConfig.GetFloatValue(WorldCfg.RateCorpseDecayLooted);
|
float decayRate = m_ignoreCorpseDecayRatio ? 1.0f : WorldConfig.GetFloatValue(WorldCfg.RateCorpseDecayLooted);
|
||||||
|
|
||||||
// corpse skinnable, but without skinning flag, and then skinned, corpse will despawn next update
|
// corpse skinnable, but without skinning flag, and then skinned, corpse will despawn next update
|
||||||
if (loot != null && loot.loot_type == LootType.Skinning)
|
bool isFullySkinned()
|
||||||
|
{
|
||||||
|
if (_loot != null && _loot.loot_type == LootType.Skinning && _loot.IsLooted())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
foreach (var (_, loot) in m_personalLoot)
|
||||||
|
if (loot.loot_type != LootType.Skinning || !loot.IsLooted())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isFullySkinned())
|
||||||
m_corpseRemoveTime = now;
|
m_corpseRemoveTime = now;
|
||||||
else
|
else
|
||||||
m_corpseRemoveTime = now + (uint)(m_corpseDelay * decayRate);
|
m_corpseRemoveTime = now + (uint)(m_corpseDelay * decayRate);
|
||||||
@@ -3269,10 +3297,6 @@ namespace Game.Entities
|
|||||||
return false;
|
return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool HasLootRecipient() { return !m_lootRecipient.IsEmpty() || !m_lootRecipientGroup.IsEmpty(); }
|
|
||||||
|
|
||||||
public override Loot GetLootForPlayer(Player player) { return loot; }
|
|
||||||
|
|
||||||
public LootModes GetLootMode() { return m_LootMode; }
|
public LootModes GetLootMode() { return m_LootMode; }
|
||||||
public bool HasLootMode(LootModes lootMode) { return Convert.ToBoolean(m_LootMode & lootMode); }
|
public bool HasLootMode(LootModes lootMode) { return Convert.ToBoolean(m_LootMode & lootMode); }
|
||||||
@@ -3343,7 +3367,6 @@ namespace Game.Entities
|
|||||||
|
|
||||||
void SetDisableReputationGain(bool disable) { DisableReputationGain = disable; }
|
void SetDisableReputationGain(bool disable) { DisableReputationGain = disable; }
|
||||||
public bool IsReputationGainDisabled() { return DisableReputationGain; }
|
public bool IsReputationGainDisabled() { return DisableReputationGain; }
|
||||||
public bool IsDamageEnoughForLootingAndReward() { return m_creatureInfo.FlagsExtra.HasAnyFlag(CreatureFlagsExtra.NoPlayerDamageReq) || m_PlayerDamageReq == 0; }
|
|
||||||
|
|
||||||
// Part of Evade mechanics
|
// Part of Evade mechanics
|
||||||
long GetLastDamagedTime() { return _lastDamagedTime; }
|
long GetLastDamagedTime() { return _lastDamagedTime; }
|
||||||
|
|||||||
@@ -956,45 +956,47 @@ namespace Game.Entities
|
|||||||
|
|
||||||
public Loot GetFishLoot(Player lootOwner)
|
public Loot GetFishLoot(Player lootOwner)
|
||||||
{
|
{
|
||||||
uint zone, subzone;
|
|
||||||
uint defaultzone = 1;
|
uint defaultzone = 1;
|
||||||
GetZoneAndAreaId(out zone, out subzone);
|
|
||||||
|
|
||||||
Loot fishLoot = new(GetMap(), GetGUID(), LootType.Fishing, null);
|
Loot fishLoot = new(GetMap(), GetGUID(), LootType.Fishing, null);
|
||||||
|
|
||||||
// if subzone loot exist use it
|
uint areaId = GetAreaId();
|
||||||
fishLoot.FillLoot(subzone, LootStorage.Fishing, lootOwner, true, true);
|
AreaTableRecord areaEntry;
|
||||||
if (fishLoot.Empty())
|
while ((areaEntry = CliDB.AreaTableStorage.LookupByKey(areaId)) != null)
|
||||||
{
|
{
|
||||||
//subzone no result,use zone loot
|
fishLoot.FillLoot(areaId, LootStorage.Fishing, lootOwner, true, true);
|
||||||
fishLoot.FillLoot(zone, LootStorage.Fishing, lootOwner, true);
|
if (!fishLoot.IsLooted())
|
||||||
//use zone 1 as default, somewhere fishing got nothing,becase subzone and zone not set, like Off the coast of Storm Peaks.
|
break;
|
||||||
if (fishLoot.Empty())
|
|
||||||
fishLoot.FillLoot(defaultzone, LootStorage.Fishing, lootOwner, true, true);
|
areaId = areaEntry.ParentAreaID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (fishLoot.IsLooted())
|
||||||
|
fishLoot.FillLoot(defaultzone, LootStorage.Fishing, lootOwner, true, true);
|
||||||
|
|
||||||
return fishLoot;
|
return fishLoot;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Loot GetFishLootJunk(Player lootOwner)
|
public Loot GetFishLootJunk(Player lootOwner)
|
||||||
{
|
{
|
||||||
uint zone, subzone;
|
|
||||||
uint defaultzone = 1;
|
uint defaultzone = 1;
|
||||||
GetZoneAndAreaId(out zone, out subzone);
|
|
||||||
|
|
||||||
Loot fishLoot = new(GetMap(), GetGUID(), LootType.FishingJunk, null);
|
Loot fishLoot = new(GetMap(), GetGUID(), LootType.FishingJunk, null);
|
||||||
|
|
||||||
// if subzone loot exist use it
|
uint areaId = GetAreaId();
|
||||||
fishLoot.FillLoot(subzone, LootStorage.Fishing, lootOwner, true, true, LootModes.JunkFish);
|
AreaTableRecord areaEntry;
|
||||||
if (fishLoot.Empty()) //use this becase if zone or subzone has normal mask drop, then fishloot.FillLoot return true.
|
while ((areaEntry = CliDB.AreaTableStorage.LookupByKey(areaId)) != null)
|
||||||
{
|
{
|
||||||
//use zone loot
|
fishLoot.FillLoot(areaId, LootStorage.Fishing, lootOwner, true, true, LootModes.JunkFish);
|
||||||
fishLoot.FillLoot(zone, LootStorage.Fishing, lootOwner, true, true, LootModes.JunkFish);
|
if (!fishLoot.IsLooted())
|
||||||
if (fishLoot.Empty())
|
break;
|
||||||
//use zone 1 as default
|
|
||||||
fishLoot.FillLoot(defaultzone, LootStorage.Fishing, lootOwner, true, true, LootModes.JunkFish);
|
areaId = areaEntry.ParentAreaID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (fishLoot.IsLooted())
|
||||||
|
fishLoot.FillLoot(defaultzone, LootStorage.Fishing, lootOwner, true, true, LootModes.JunkFish);
|
||||||
|
|
||||||
return fishLoot;
|
return fishLoot;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2886,51 +2888,6 @@ namespace Game.Entities
|
|||||||
GetMap().InsertGameObjectModel(m_model);
|
GetMap().InsertGameObjectModel(m_model);
|
||||||
}
|
}
|
||||||
|
|
||||||
Player GetLootRecipient()
|
|
||||||
{
|
|
||||||
if (m_lootRecipient.IsEmpty())
|
|
||||||
return null;
|
|
||||||
return Global.ObjAccessor.FindPlayer(m_lootRecipient);
|
|
||||||
}
|
|
||||||
|
|
||||||
Group GetLootRecipientGroup()
|
|
||||||
{
|
|
||||||
if (m_lootRecipientGroup.IsEmpty())
|
|
||||||
return Global.GroupMgr.GetGroupByGUID(m_lootRecipientGroup);
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetLootRecipient(Unit unit, Group group)
|
|
||||||
{
|
|
||||||
// set the player whose group should receive the right
|
|
||||||
// to loot the creature after it dies
|
|
||||||
// should be set to null after the loot disappears
|
|
||||||
|
|
||||||
if (unit == null)
|
|
||||||
{
|
|
||||||
m_lootRecipient.Clear();
|
|
||||||
m_lootRecipientGroup = group ? group.GetGUID() : ObjectGuid.Empty;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!unit.IsTypeId(TypeId.Player) && !unit.IsVehicle())
|
|
||||||
return;
|
|
||||||
|
|
||||||
Player player = unit.GetCharmerOrOwnerPlayerOrPlayerItself();
|
|
||||||
if (player == null) // normal creature, no player involved
|
|
||||||
return;
|
|
||||||
|
|
||||||
m_lootRecipient = player.GetGUID();
|
|
||||||
|
|
||||||
// either get the group from the passed parameter or from unit's one
|
|
||||||
Group unitGroup = player.GetGroup();
|
|
||||||
if (group)
|
|
||||||
m_lootRecipientGroup = group.GetGUID();
|
|
||||||
else if (unitGroup)
|
|
||||||
m_lootRecipientGroup = unitGroup.GetGUID();
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsLootAllowedFor(Player player)
|
public bool IsLootAllowedFor(Player player)
|
||||||
{
|
{
|
||||||
Loot loot = GetLootForPlayer(player);
|
Loot loot = GetLootForPlayer(player);
|
||||||
@@ -2942,15 +2899,8 @@ namespace Game.Entities
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_lootRecipient.IsEmpty() && m_lootRecipientGroup.IsEmpty())
|
if (HasLootRecipient())
|
||||||
return true;
|
return m_tapList.Contains(player.GetGUID()); // if go doesnt have group bound it means it was solo killed by someone else
|
||||||
|
|
||||||
if (player.GetGUID() == m_lootRecipient)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
Group playerGroup = player.GetGroup();
|
|
||||||
if (!playerGroup || playerGroup != GetLootRecipientGroup()) // if we dont have a group we arent the recipient
|
|
||||||
return false; // if go doesnt have group bound it means it was solo killed by someone else
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -3444,7 +3394,10 @@ namespace Game.Entities
|
|||||||
public uint GetUseCount() { return m_usetimes; }
|
public uint GetUseCount() { return m_usetimes; }
|
||||||
uint GetUniqueUseCount() { return (uint)m_unique_users.Count; }
|
uint GetUniqueUseCount() { return (uint)m_unique_users.Count; }
|
||||||
|
|
||||||
bool HasLootRecipient() { return !m_lootRecipient.IsEmpty() || !m_lootRecipientGroup.IsEmpty(); }
|
List<ObjectGuid> GetTapList() { return m_tapList; }
|
||||||
|
void SetTapList(List<ObjectGuid> tapList) { m_tapList = tapList; }
|
||||||
|
|
||||||
|
bool HasLootRecipient() { return !m_tapList.Empty(); }
|
||||||
|
|
||||||
public override uint GetLevelForTarget(WorldObject target)
|
public override uint GetLevelForTarget(WorldObject target)
|
||||||
{
|
{
|
||||||
@@ -3552,8 +3505,7 @@ namespace Game.Entities
|
|||||||
List<ObjectGuid> m_unique_users = new();
|
List<ObjectGuid> m_unique_users = new();
|
||||||
uint m_usetimes;
|
uint m_usetimes;
|
||||||
|
|
||||||
ObjectGuid m_lootRecipient;
|
List<ObjectGuid> m_tapList = new();
|
||||||
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
|
||||||
long m_packedRotation;
|
long m_packedRotation;
|
||||||
Quaternion m_localRotation;
|
Quaternion m_localRotation;
|
||||||
|
|||||||
@@ -99,8 +99,6 @@ namespace Game.Entities
|
|||||||
Unit unit = obj.ToUnit();
|
Unit unit = obj.ToUnit();
|
||||||
if (unit != null)
|
if (unit != null)
|
||||||
{
|
{
|
||||||
unitDynFlags &= ~(uint)UnitDynFlags.Tapped;
|
|
||||||
|
|
||||||
Creature creature = obj.ToCreature();
|
Creature creature = obj.ToCreature();
|
||||||
if (creature != null)
|
if (creature != null)
|
||||||
{
|
{
|
||||||
@@ -109,6 +107,9 @@ namespace Game.Entities
|
|||||||
|
|
||||||
if (!receiver.IsAllowedToLoot(creature))
|
if (!receiver.IsAllowedToLoot(creature))
|
||||||
unitDynFlags &= ~(uint)UnitDynFlags.Lootable;
|
unitDynFlags &= ~(uint)UnitDynFlags.Lootable;
|
||||||
|
|
||||||
|
if ((unitDynFlags & (uint)UnitDynFlags.CanSkin) != 0 && creature.IsSkinnedBy(receiver))
|
||||||
|
unitDynFlags &= ~(uint)UnitDynFlags.CanSkin;
|
||||||
}
|
}
|
||||||
|
|
||||||
// unit UNIT_DYNFLAG_TRACK_UNIT should only be sent to caster of SPELL_AURA_MOD_STALKED auras
|
// unit UNIT_DYNFLAG_TRACK_UNIT should only be sent to caster of SPELL_AURA_MOD_STALKED auras
|
||||||
@@ -1327,7 +1328,7 @@ namespace Game.Entities
|
|||||||
|
|
||||||
data.WriteUInt32(GetViewerDependentFlags(this, owner, receiver));
|
data.WriteUInt32(GetViewerDependentFlags(this, owner, receiver));
|
||||||
data.WriteUInt32(Flags2);
|
data.WriteUInt32(Flags2);
|
||||||
data.WriteUInt32(Flags3);
|
data.WriteUInt32(GetViewerDependentFlags3(this, owner, receiver));
|
||||||
data.WriteUInt32(GetViewerDependentAuraState(this, owner, receiver));
|
data.WriteUInt32(GetViewerDependentAuraState(this, owner, receiver));
|
||||||
for (int i = 0; i < 2; ++i)
|
for (int i = 0; i < 2; ++i)
|
||||||
data.WriteUInt32(AttackRoundBaseTime[i]);
|
data.WriteUInt32(AttackRoundBaseTime[i]);
|
||||||
@@ -1712,7 +1713,7 @@ namespace Game.Entities
|
|||||||
}
|
}
|
||||||
if (changesMask[45])
|
if (changesMask[45])
|
||||||
{
|
{
|
||||||
data.WriteUInt32(Flags3);
|
data.WriteUInt32(GetViewerDependentFlags3(this, owner, receiver));
|
||||||
}
|
}
|
||||||
if (changesMask[46])
|
if (changesMask[46])
|
||||||
{
|
{
|
||||||
@@ -2308,6 +2309,14 @@ namespace Game.Entities
|
|||||||
|
|
||||||
return flags;
|
return flags;
|
||||||
}
|
}
|
||||||
|
uint GetViewerDependentFlags3(UnitData unitData, Unit unit, Player receiver)
|
||||||
|
{
|
||||||
|
uint flags = unitData.Flags3;
|
||||||
|
if ((flags & (uint)UnitFlags3.AlreadySkinned) != 0 && unit.IsCreature() && !unit.ToCreature().IsSkinnedBy(receiver))
|
||||||
|
flags &= ~(uint)UnitFlags3.AlreadySkinned;
|
||||||
|
|
||||||
|
return flags;
|
||||||
|
}
|
||||||
uint GetViewerDependentAuraState(UnitData unitData, Unit unit, Player receiver)
|
uint GetViewerDependentAuraState(UnitData unitData, Unit unit, Player receiver)
|
||||||
{
|
{
|
||||||
// Check per caster aura states to not enable using a spell in client if specified aura is not by target
|
// Check per caster aura states to not enable using a spell in client if specified aura is not by target
|
||||||
|
|||||||
@@ -21,14 +21,14 @@ using Game.Groups;
|
|||||||
using Game.Maps;
|
using Game.Maps;
|
||||||
using Game.Scenarios;
|
using Game.Scenarios;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace Game.Entities
|
namespace Game.Entities
|
||||||
{
|
{
|
||||||
public class KillRewarder
|
public class KillRewarder
|
||||||
{
|
{
|
||||||
Player _killer;
|
Player[] _killers;
|
||||||
Unit _victim;
|
Unit _victim;
|
||||||
Group _group;
|
|
||||||
float _groupRate;
|
float _groupRate;
|
||||||
Player _maxNotGrayMember;
|
Player _maxNotGrayMember;
|
||||||
uint _count;
|
uint _count;
|
||||||
@@ -39,11 +39,10 @@ namespace Game.Entities
|
|||||||
bool _isBattleground;
|
bool _isBattleground;
|
||||||
bool _isPvP;
|
bool _isPvP;
|
||||||
|
|
||||||
public KillRewarder(Player killer, Unit victim, bool isBattleground)
|
public KillRewarder(Player[] killers, Unit victim, bool isBattleground)
|
||||||
{
|
{
|
||||||
_killer = killer;
|
_killers = killers;
|
||||||
_victim = victim;
|
_victim = victim;
|
||||||
_group = killer.GetGroup();
|
|
||||||
_groupRate = 1.0f;
|
_groupRate = 1.0f;
|
||||||
_maxNotGrayMember = null;
|
_maxNotGrayMember = null;
|
||||||
_count = 0;
|
_count = 0;
|
||||||
@@ -60,27 +59,37 @@ namespace Game.Entities
|
|||||||
// or if its owned by player and its not a vehicle
|
// or if its owned by player and its not a vehicle
|
||||||
else if (victim.GetCharmerOrOwnerGUID().IsPlayer())
|
else if (victim.GetCharmerOrOwnerGUID().IsPlayer())
|
||||||
_isPvP = !victim.IsVehicle();
|
_isPvP = !victim.IsVehicle();
|
||||||
|
|
||||||
_InitGroupData();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Reward()
|
public void Reward()
|
||||||
{
|
{
|
||||||
// 3. Reward killer (and group, if necessary).
|
SortedSet<Group> processedGroups = new();
|
||||||
if (_group)
|
foreach (Player killer in _killers)
|
||||||
// 3.1. If killer is in group, reward group.
|
|
||||||
_RewardGroup();
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
// 3.2. Reward single killer (not group case).
|
_InitGroupData(killer);
|
||||||
// 3.2.1. Initialize initial XP amount based on killer's level.
|
|
||||||
_InitXP(_killer);
|
// 3. Reward killer (and group, if necessary).
|
||||||
// To avoid unnecessary calculations and calls,
|
Group group = killer.GetGroup();
|
||||||
// proceed only if XP is not ZERO or player is not on Battleground
|
if (group != null)
|
||||||
// (Battlegroundrewards only XP, that's why).
|
{
|
||||||
if (!_isBattleground || _xp != 0)
|
if (!processedGroups.Add(group))
|
||||||
// 3.2.2. Reward killer.
|
continue;
|
||||||
_RewardPlayer(_killer, false);
|
|
||||||
|
// 3.1. If killer is in group, reward group.
|
||||||
|
_RewardGroup(group, killer);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 3.2. Reward single killer (not group case).
|
||||||
|
// 3.2.1. Initialize initial XP amount based on killer's level.
|
||||||
|
_InitXP(killer, killer);
|
||||||
|
// To avoid unnecessary calculations and calls,
|
||||||
|
// proceed only if XP is not ZERO or player is not on battleground
|
||||||
|
// (battleground rewards only XP, that's why).
|
||||||
|
if (!_isBattleground || _xp != 0)
|
||||||
|
// 3.2.2. Reward killer.
|
||||||
|
_RewardPlayer(killer, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5. Credit instance encounter.
|
// 5. Credit instance encounter.
|
||||||
@@ -99,25 +108,26 @@ namespace Game.Entities
|
|||||||
uint guildId = victim.GetMap().GetOwnerGuildId();
|
uint guildId = victim.GetMap().GetOwnerGuildId();
|
||||||
var guild = Global.GuildMgr.GetGuildById(guildId);
|
var guild = Global.GuildMgr.GetGuildById(guildId);
|
||||||
if (guild != null)
|
if (guild != null)
|
||||||
guild.UpdateCriteria(CriteriaType.KillCreature, victim.GetEntry(), 1, 0, victim, _killer);
|
guild.UpdateCriteria(CriteriaType.KillCreature, victim.GetEntry(), 1, 0, victim, _killers.First());
|
||||||
|
|
||||||
Scenario scenario = victim.GetScenario();
|
Scenario scenario = victim.GetScenario();
|
||||||
if (scenario != null)
|
if (scenario != null)
|
||||||
scenario.UpdateCriteria(CriteriaType.KillCreature, victim.GetEntry(), 1, 0, victim, _killer);
|
scenario.UpdateCriteria(CriteriaType.KillCreature, victim.GetEntry(), 1, 0, victim, _killers.First());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _InitGroupData()
|
void _InitGroupData(Player killer)
|
||||||
{
|
{
|
||||||
if (_group)
|
Group group = killer.GetGroup();
|
||||||
|
if (group != null)
|
||||||
{
|
{
|
||||||
// 2. In case when player is in group, initialize variables necessary for group calculations:
|
// 2. In case when player is in group, initialize variables necessary for group calculations:
|
||||||
for (GroupReference refe = _group.GetFirstMember(); refe != null; refe = refe.Next())
|
for (GroupReference refe = group.GetFirstMember(); refe != null; refe = refe.Next())
|
||||||
{
|
{
|
||||||
Player member = refe.GetSource();
|
Player member = refe.GetSource();
|
||||||
if (member)
|
if (member != null)
|
||||||
{
|
{
|
||||||
if (_killer == member || (member.IsAtGroupRewardDistance(_victim) && member.IsAlive()))
|
if (killer == member || (member.IsAtGroupRewardDistance(_victim) && member.IsAlive()))
|
||||||
{
|
{
|
||||||
uint lvl = member.GetLevel();
|
uint lvl = member.GetLevel();
|
||||||
// 2.1. _count - number of alive group members within reward distance;
|
// 2.1. _count - number of alive group members within reward distance;
|
||||||
@@ -143,14 +153,14 @@ namespace Game.Entities
|
|||||||
_count = 1;
|
_count = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _InitXP(Player player)
|
void _InitXP(Player player, Player killer)
|
||||||
{
|
{
|
||||||
// Get initial value of XP for kill.
|
// Get initial value of XP for kill.
|
||||||
// XP is given:
|
// XP is given:
|
||||||
// * on Battlegrounds;
|
// * on Battlegrounds;
|
||||||
// * otherwise, not in PvP;
|
// * otherwise, not in PvP;
|
||||||
// * not if killer is on vehicle.
|
// * not if killer is on vehicle.
|
||||||
if (_isBattleground || (!_isPvP && _killer.GetVehicle() == null))
|
if (_isBattleground || (!_isPvP && killer.GetVehicle() == null))
|
||||||
_xp = Formulas.XPGain(player, _victim, _isBattleground);
|
_xp = Formulas.XPGain(player, _victim, _isBattleground);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -164,7 +174,7 @@ namespace Game.Entities
|
|||||||
void _RewardXP(Player player, float rate)
|
void _RewardXP(Player player, float rate)
|
||||||
{
|
{
|
||||||
uint xp = _xp;
|
uint xp = _xp;
|
||||||
if (_group)
|
if (player.GetGroup() != null)
|
||||||
{
|
{
|
||||||
// 4.2.1. If player is in group, adjust XP:
|
// 4.2.1. If player is in group, adjust XP:
|
||||||
// * set to 0 if player's level is more than maximum level of not gray member;
|
// * set to 0 if player's level is more than maximum level of not gray member;
|
||||||
@@ -188,7 +198,7 @@ namespace Game.Entities
|
|||||||
Pet pet = player.GetPet();
|
Pet pet = player.GetPet();
|
||||||
if (pet)
|
if (pet)
|
||||||
// 4.2.4. If player has pet, reward pet with XP (100% for single player, 50% for group case).
|
// 4.2.4. If player has pet, reward pet with XP (100% for single player, 50% for group case).
|
||||||
pet.GivePetXP(_group ? xp / 2 : xp);
|
pet.GivePetXP(player.GetGroup() != null ? xp / 2 : xp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -202,7 +212,7 @@ namespace Game.Entities
|
|||||||
void _RewardKillCredit(Player player)
|
void _RewardKillCredit(Player player)
|
||||||
{
|
{
|
||||||
// 4.4. Give kill credit (player must not be in group, or he must be alive or without corpse).
|
// 4.4. Give kill credit (player must not be in group, or he must be alive or without corpse).
|
||||||
if (!_group || player.IsAlive() || player.GetCorpse() == null)
|
if (player.GetGroup() == null || player.IsAlive() || player.GetCorpse() == null)
|
||||||
{
|
{
|
||||||
Creature target = _victim.ToCreature();
|
Creature target = _victim.ToCreature();
|
||||||
if (target != null)
|
if (target != null)
|
||||||
@@ -228,7 +238,7 @@ namespace Game.Entities
|
|||||||
// Give reputation and kill credit only in PvE.
|
// Give reputation and kill credit only in PvE.
|
||||||
if (!_isPvP || _isBattleground)
|
if (!_isPvP || _isBattleground)
|
||||||
{
|
{
|
||||||
float rate = _group ? _groupRate * player.GetLevel() / _sumLevel : 1.0f;
|
float rate = player.GetGroup() != null ? _groupRate * player.GetLevel() / _sumLevel : 1.0f;
|
||||||
if (_xp != 0)
|
if (_xp != 0)
|
||||||
// 4.2. Give XP.
|
// 4.2. Give XP.
|
||||||
_RewardXP(player, rate);
|
_RewardXP(player, rate);
|
||||||
@@ -241,34 +251,34 @@ namespace Game.Entities
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _RewardGroup()
|
void _RewardGroup(Group group, Player killer)
|
||||||
{
|
{
|
||||||
if (_maxLevel != 0)
|
if (_maxLevel != 0)
|
||||||
{
|
{
|
||||||
if (_maxNotGrayMember != null)
|
if (_maxNotGrayMember != null)
|
||||||
// 3.1.1. Initialize initial XP amount based on maximum level of group member,
|
// 3.1.1. Initialize initial XP amount based on maximum level of group member,
|
||||||
// for whom victim is not gray.
|
// for whom victim is not gray.
|
||||||
_InitXP(_maxNotGrayMember);
|
_InitXP(_maxNotGrayMember, killer);
|
||||||
// To avoid unnecessary calculations and calls,
|
// To avoid unnecessary calculations and calls,
|
||||||
// proceed only if XP is not ZERO or player is not on Battleground
|
// proceed only if XP is not ZERO or player is not on Battleground
|
||||||
// (Battlegroundrewards only XP, that's why).
|
// (Battlegroundrewards only XP, that's why).
|
||||||
if (!_isBattleground || _xp != 0)
|
if (!_isBattleground || _xp != 0)
|
||||||
{
|
{
|
||||||
bool isDungeon = !_isPvP && CliDB.MapStorage.LookupByKey(_killer.GetMapId()).IsDungeon();
|
bool isDungeon = !_isPvP && CliDB.MapStorage.LookupByKey(killer.GetMapId()).IsDungeon();
|
||||||
if (!_isBattleground)
|
if (!_isBattleground)
|
||||||
{
|
{
|
||||||
// 3.1.2. Alter group rate if group is in raid (not for Battlegrounds).
|
// 3.1.2. Alter group rate if group is in raid (not for Battlegrounds).
|
||||||
bool isRaid = !_isPvP && CliDB.MapStorage.LookupByKey(_killer.GetMapId()).IsRaid() && _group.IsRaidGroup();
|
bool isRaid = !_isPvP && CliDB.MapStorage.LookupByKey(killer.GetMapId()).IsRaid() && group.IsRaidGroup();
|
||||||
_groupRate = Formulas.XPInGroupRate(_count, isRaid);
|
_groupRate = Formulas.XPInGroupRate(_count, isRaid);
|
||||||
}
|
}
|
||||||
// 3.1.3. Reward each group member (even dead or corpse) within reward distance.
|
// 3.1.3. Reward each group member (even dead or corpse) within reward distance.
|
||||||
for (GroupReference refe = _group.GetFirstMember(); refe != null; refe = refe.Next())
|
for (GroupReference refe = group.GetFirstMember(); refe != null; refe = refe.Next())
|
||||||
{
|
{
|
||||||
Player member = refe.GetSource();
|
Player member = refe.GetSource();
|
||||||
if (member)
|
if (member)
|
||||||
{
|
{
|
||||||
// Killer may not be at reward distance, check directly
|
// Killer may not be at reward distance, check directly
|
||||||
if (_killer == member || member.IsAtGroupRewardDistance(_victim))
|
if (killer == member || member.IsAtGroupRewardDistance(_victim))
|
||||||
_RewardPlayer(member, isDungeon);
|
_RewardPlayer(member, isDungeon);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,11 +44,6 @@ namespace Game.Entities
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RewardPlayerAndGroupAtKill(Unit victim, bool isBattleground)
|
|
||||||
{
|
|
||||||
new KillRewarder(this, victim, isBattleground).Reward();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RewardPlayerAndGroupAtEvent(uint creature_id, WorldObject pRewardSource)
|
public void RewardPlayerAndGroupAtEvent(uint creature_id, WorldObject pRewardSource)
|
||||||
{
|
{
|
||||||
if (pRewardSource == null)
|
if (pRewardSource == null)
|
||||||
|
|||||||
@@ -3126,7 +3126,7 @@ namespace Game.Entities
|
|||||||
|
|
||||||
public bool IsAllowedToLoot(Creature creature)
|
public bool IsAllowedToLoot(Creature creature)
|
||||||
{
|
{
|
||||||
if (!creature.IsDead() || !creature.IsDamageEnoughForLootingAndReward())
|
if (!creature.IsDead())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (HasPendingBind())
|
if (HasPendingBind())
|
||||||
@@ -3139,15 +3139,6 @@ namespace Game.Entities
|
|||||||
if (!loot.HasAllowedLooter(GetGUID()) || (!loot.HasItemForAll() && !loot.HasItemFor(this))) // no loot in creature for this player
|
if (!loot.HasAllowedLooter(GetGUID()) || (!loot.HasItemForAll() && !loot.HasItemFor(this))) // no loot in creature for this player
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (loot.loot_type == LootType.Skinning)
|
|
||||||
return creature.GetLootRecipientGUID() == GetGUID();
|
|
||||||
|
|
||||||
Group thisGroup = GetGroup();
|
|
||||||
if (!thisGroup)
|
|
||||||
return this == creature.GetLootRecipient();
|
|
||||||
else if (thisGroup != creature.GetLootRecipientGroup())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
switch (loot.GetLootMethod())
|
switch (loot.GetLootMethod())
|
||||||
{
|
{
|
||||||
case LootMethod.PersonalLoot:// @todo implement personal loot (http://wow.gamepedia.com/Loot#Personal_Loot)
|
case LootMethod.PersonalLoot:// @todo implement personal loot (http://wow.gamepedia.com/Loot#Personal_Loot)
|
||||||
|
|||||||
@@ -706,36 +706,16 @@ namespace Game.Entities
|
|||||||
|
|
||||||
bool isRewardAllowed = true;
|
bool isRewardAllowed = true;
|
||||||
if (creature != null)
|
if (creature != null)
|
||||||
{
|
isRewardAllowed = !creature.GetTapList().Empty();
|
||||||
isRewardAllowed = creature.IsDamageEnoughForLootingAndReward();
|
|
||||||
if (!isRewardAllowed)
|
|
||||||
creature.SetLootRecipient(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
List<Player> tappers = new();
|
||||||
if (isRewardAllowed && creature)
|
if (isRewardAllowed && creature)
|
||||||
{
|
{
|
||||||
Player lootRecipient = creature.GetLootRecipient();
|
foreach (ObjectGuid tapperGuid in creature.GetTapList())
|
||||||
if (lootRecipient != null)
|
|
||||||
{
|
{
|
||||||
// Loot recipient can be in a different map
|
Player tapper = Global.ObjAccessor.GetPlayer(creature, tapperGuid);
|
||||||
if (!creature.IsInMap(lootRecipient))
|
if (tapper != null)
|
||||||
{
|
tappers.Add(tapper);
|
||||||
Group group = creature.GetLootRecipientGroup();
|
|
||||||
if (group != null)
|
|
||||||
{
|
|
||||||
for (GroupReference itr = group.GetFirstMember(); itr != null; itr = itr.Next())
|
|
||||||
{
|
|
||||||
Player member = itr.GetSource();
|
|
||||||
if (!member || !creature.IsInMap(member))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
player = member;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
player = creature.GetLootRecipient();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -745,64 +725,92 @@ namespace Game.Entities
|
|||||||
|
|
||||||
// Reward player, his pets, and group/raid members
|
// Reward player, his pets, and group/raid members
|
||||||
// call kill spell proc event (before real die and combat stop to triggering auras removed at death/combat stop)
|
// call kill spell proc event (before real die and combat stop to triggering auras removed at death/combat stop)
|
||||||
if (isRewardAllowed && player != null && player != victim)
|
if (isRewardAllowed)
|
||||||
{
|
{
|
||||||
PartyKillLog partyKillLog = new();
|
PartyKillLog partyKillLog = new();
|
||||||
partyKillLog.Player = player.GetGUID();
|
partyKillLog.Player = player.GetGUID();
|
||||||
partyKillLog.Victim = victim.GetGUID();
|
partyKillLog.Victim = victim.GetGUID();
|
||||||
|
partyKillLog.Write();
|
||||||
|
|
||||||
Player looter = player;
|
HashSet<Group> groups = new();
|
||||||
var group = player.GetGroup();
|
foreach (Player tapper in tappers)
|
||||||
if (group)
|
|
||||||
{
|
{
|
||||||
group.BroadcastPacket(partyKillLog, group.GetMemberGroup(player.GetGUID()) != 0);
|
Group tapperGroup = tapper.GetGroup();
|
||||||
|
if (tapperGroup != null)
|
||||||
if (creature)
|
|
||||||
{
|
{
|
||||||
group.UpdateLooterGuid(creature, true);
|
if (groups.Add(tapperGroup))
|
||||||
if (!group.GetLooterGuid().IsEmpty())
|
|
||||||
{
|
{
|
||||||
looter = Global.ObjAccessor.FindPlayer(group.GetLooterGuid());
|
tapperGroup.BroadcastPacket(partyKillLog, tapperGroup.GetMemberGroup(tapper.GetGUID()) != 0);
|
||||||
if (looter)
|
|
||||||
creature.SetLootRecipient(looter); // update creature loot recipient to the allowed looter.
|
if (creature)
|
||||||
|
tapperGroup.UpdateLooterGuid(creature, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
tapper.SendPacket(partyKillLog);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
player.SendPacket(partyKillLog);
|
|
||||||
|
|
||||||
// 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);
|
DungeonEncounterRecord dungeonEncounter = null;
|
||||||
Loot loot = creature.loot;
|
|
||||||
if (creature.GetMap().Is25ManRaid())
|
|
||||||
loot.maxDuplicates = 3;
|
|
||||||
|
|
||||||
InstanceScript instance = creature.GetInstanceScript();
|
InstanceScript instance = creature.GetInstanceScript();
|
||||||
if (instance != null)
|
if (instance != null)
|
||||||
|
dungeonEncounter = instance.GetBossDungeonEncounter(creature);
|
||||||
|
|
||||||
|
if (creature.GetMap().IsDungeon())
|
||||||
{
|
{
|
||||||
DungeonEncounterRecord dungeonEncounter = instance.GetBossDungeonEncounter(creature);
|
Group group = !groups.Empty() ? groups.First() : null;
|
||||||
|
Player looter = group ? Global.ObjAccessor.GetPlayer(creature, group.GetLooterGuid()) : tappers[0];
|
||||||
|
|
||||||
|
Loot loot = new(creature.GetMap(), creature.GetGUID(), LootType.Corpse, dungeonEncounter != null ? group : null);
|
||||||
|
|
||||||
if (dungeonEncounter != null)
|
if (dungeonEncounter != null)
|
||||||
loot.SetDungeonEncounterId(dungeonEncounter.Id);
|
loot.SetDungeonEncounterId(dungeonEncounter.Id);
|
||||||
|
|
||||||
|
uint lootid = creature.GetCreatureTemplate().LootId;
|
||||||
|
if (lootid != 0)
|
||||||
|
loot.FillLoot(lootid, LootStorage.Creature, looter, dungeonEncounter != null, false, creature.GetLootMode(), creature.GetMap().GetDifficultyLootItemContext());
|
||||||
|
|
||||||
|
if (creature.GetLootMode() > 0)
|
||||||
|
loot.GenerateMoneyLoot(creature.GetCreatureTemplate().MinGold, creature.GetCreatureTemplate().MaxGold);
|
||||||
|
|
||||||
|
if (group)
|
||||||
|
loot.NotifyLootList(creature.GetMap());
|
||||||
|
|
||||||
|
if (dungeonEncounter != null || groups.Empty())
|
||||||
|
creature._loot = loot; // TODO: personal boss loot
|
||||||
|
else
|
||||||
|
creature.m_personalLoot[looter.GetGUID()] = loot; // trash mob loot is personal, generated with round robin rules
|
||||||
|
|
||||||
|
// Update round robin looter only if the creature had loot
|
||||||
|
if (!loot.IsLooted())
|
||||||
|
foreach (Group tapperGroup in groups)
|
||||||
|
tapperGroup.UpdateLooterGuid(creature);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach (Player tapper in tappers)
|
||||||
|
{
|
||||||
|
Loot loot = new(creature.GetMap(), creature.GetGUID(), LootType.Corpse, null);
|
||||||
|
|
||||||
uint lootid = creature.GetCreatureTemplate().LootId;
|
if (dungeonEncounter != null)
|
||||||
if (lootid != 0)
|
loot.SetDungeonEncounterId(dungeonEncounter.Id);
|
||||||
loot.FillLoot(lootid, LootStorage.Creature, looter, false, false, creature.GetLootMode(), creature.GetMap().GetDifficultyLootItemContext());
|
|
||||||
|
|
||||||
if (creature.GetLootMode() > 0)
|
uint lootid = creature.GetCreatureTemplate().LootId;
|
||||||
loot.GenerateMoneyLoot(creature.GetCreatureTemplate().MinGold, creature.GetCreatureTemplate().MaxGold);
|
if (lootid != 0)
|
||||||
|
loot.FillLoot(lootid, LootStorage.Creature, tapper, true, false, creature.GetLootMode(), creature.GetMap().GetDifficultyLootItemContext());
|
||||||
|
|
||||||
loot.NotifyLootList(creature.GetMap());
|
if (creature.GetLootMode() > 0)
|
||||||
|
loot.GenerateMoneyLoot(creature.GetCreatureTemplate().MinGold, creature.GetCreatureTemplate().MaxGold);
|
||||||
|
|
||||||
// Update round robin looter only if the creature had loot
|
creature.m_personalLoot[tapper.GetGUID()] = loot;
|
||||||
if (group != null && !loot.Empty())
|
}
|
||||||
group.UpdateLooterGuid(creature);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
player.RewardPlayerAndGroupAtKill(victim, false);
|
new KillRewarder(tappers.ToArray(), victim, false).Reward();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do KILL and KILLED procs. KILL proc is called only for the unit who landed the killing blow (and its owner - for pets and totems) regardless of who tapped the victim
|
// Do KILL and KILLED procs. KILL proc is called only for the unit who landed the killing blow (and its owner - for pets and totems) regardless of who tapped the victim
|
||||||
if (attacker != null && (attacker.IsPet() || attacker.IsTotem()))
|
if (attacker != null && (attacker.IsPet() || attacker.IsTotem()))
|
||||||
@@ -816,16 +824,10 @@ namespace Game.Entities
|
|||||||
if (!victim.IsCritter())
|
if (!victim.IsCritter())
|
||||||
{
|
{
|
||||||
ProcSkillsAndAuras(attacker, victim, new ProcFlagsInit(ProcFlags.Kill), new ProcFlagsInit(ProcFlags.Heartbeat), ProcFlagsSpellType.MaskAll, ProcFlagsSpellPhase.None, ProcFlagsHit.None, null, null, null);
|
ProcSkillsAndAuras(attacker, victim, new ProcFlagsInit(ProcFlags.Kill), new ProcFlagsInit(ProcFlags.Heartbeat), ProcFlagsSpellType.MaskAll, ProcFlagsSpellPhase.None, ProcFlagsHit.None, null, null, null);
|
||||||
if (player != null && player.GetGroup() != null)
|
|
||||||
{
|
foreach (Player tapper in tappers)
|
||||||
for (GroupReference itr = player.GetGroup().GetFirstMember(); itr != null; itr = itr.Next())
|
if (tapper.IsAtGroupRewardDistance(victim))
|
||||||
{
|
Unit.ProcSkillsAndAuras(tapper, victim, new ProcFlagsInit(ProcFlags.None, ProcFlags2.TargetDies), new ProcFlagsInit(), ProcFlagsSpellType.MaskAll, ProcFlagsSpellPhase.None, ProcFlagsHit.None, null, null, null);
|
||||||
Player member = itr.GetSource();
|
|
||||||
if (member != null)
|
|
||||||
if (member.IsAtGroupRewardDistance(victim))
|
|
||||||
Unit.ProcSkillsAndAuras(member, victim, new ProcFlagsInit(ProcFlags.None, ProcFlags2.TargetDies), new ProcFlagsInit(ProcFlags.None), ProcFlagsSpellType.MaskAll, ProcFlagsSpellPhase.None, ProcFlagsHit.None, null, null, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Proc auras on death - must be before aura/combat remove
|
// Proc auras on death - must be before aura/combat remove
|
||||||
@@ -849,9 +851,9 @@ namespace Game.Entities
|
|||||||
// Inform pets (if any) when player kills target)
|
// Inform pets (if any) when player kills target)
|
||||||
// MUST come after victim.setDeathState(JUST_DIED); or pet next target
|
// MUST come after victim.setDeathState(JUST_DIED); or pet next target
|
||||||
// selection will get stuck on same target and break pet react state
|
// selection will get stuck on same target and break pet react state
|
||||||
if (player != null)
|
foreach (Player tapper in tappers)
|
||||||
{
|
{
|
||||||
Pet pet = player.GetPet();
|
Pet pet = tapper.GetPet();
|
||||||
if (pet != null && pet.IsAlive() && pet.IsControlled())
|
if (pet != null && pet.IsAlive() && pet.IsControlled())
|
||||||
{
|
{
|
||||||
if (pet.IsAIEnabled())
|
if (pet.IsAIEnabled())
|
||||||
@@ -899,10 +901,16 @@ namespace Game.Entities
|
|||||||
if (!creature.IsPet())
|
if (!creature.IsPet())
|
||||||
{
|
{
|
||||||
// must be after setDeathState which resets dynamic flags
|
// must be after setDeathState which resets dynamic flags
|
||||||
if (creature.loot != null && !creature.loot.IsLooted())
|
if (!creature.IsFullyLooted())
|
||||||
creature.SetDynamicFlag(UnitDynFlags.Lootable);
|
creature.SetDynamicFlag(UnitDynFlags.Lootable);
|
||||||
else
|
else
|
||||||
creature.AllLootRemovedFromCorpse();
|
creature.AllLootRemovedFromCorpse();
|
||||||
|
|
||||||
|
if (LootStorage.Skinning.HaveLootFor(creature.GetCreatureTemplate().SkinLootId))
|
||||||
|
{
|
||||||
|
creature.SetDynamicFlag(UnitDynFlags.CanSkin);
|
||||||
|
creature.SetUnitFlag(UnitFlags.Skinnable);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call KilledUnit for creatures, this needs to be called after the lootable flag is set
|
// Call KilledUnit for creatures, this needs to be called after the lootable flag is set
|
||||||
|
|||||||
@@ -2574,8 +2574,7 @@ namespace Game.Entities
|
|||||||
|
|
||||||
if (victim.GetTypeId() != TypeId.Player && (!victim.IsControlledByPlayer() || victim.IsVehicle()))
|
if (victim.GetTypeId() != TypeId.Player && (!victim.IsControlledByPlayer() || victim.IsVehicle()))
|
||||||
{
|
{
|
||||||
if (!victim.ToCreature().HasLootRecipient())
|
victim.ToCreature().SetTappedBy(attacker);
|
||||||
victim.ToCreature().SetLootRecipient(attacker);
|
|
||||||
|
|
||||||
if (attacker == null || attacker.IsControlledByPlayer())
|
if (attacker == null || attacker.IsControlledByPlayer())
|
||||||
victim.ToCreature().LowerPlayerDamageReq(health < damage ? health : damage);
|
victim.ToCreature().LowerPlayerDamageReq(health < damage ? health : damage);
|
||||||
|
|||||||
@@ -355,11 +355,14 @@ namespace Game
|
|||||||
|
|
||||||
if (loot.IsLooted())
|
if (loot.IsLooted())
|
||||||
{
|
{
|
||||||
creature.RemoveDynamicFlag(UnitDynFlags.Lootable);
|
if (creature.IsFullyLooted())
|
||||||
|
{
|
||||||
|
creature.RemoveDynamicFlag(UnitDynFlags.Lootable);
|
||||||
|
|
||||||
// skip pickpocketing loot for speed, skinning timer reduction is no-op in fact
|
// skip pickpocketing loot for speed, skinning timer reduction is no-op in fact
|
||||||
if (!creature.IsAlive())
|
if (!creature.IsAlive())
|
||||||
creature.AllLootRemovedFromCorpse();
|
creature.AllLootRemovedFromCorpse();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -369,11 +372,11 @@ namespace Game
|
|||||||
loot.roundRobinPlayer.Clear();
|
loot.roundRobinPlayer.Clear();
|
||||||
loot.NotifyLootList(creature.GetMap());
|
loot.NotifyLootList(creature.GetMap());
|
||||||
}
|
}
|
||||||
|
|
||||||
// force dynflag update to update looter and lootable info
|
|
||||||
creature.m_values.ModifyValue(creature.m_objectData).ModifyValue(creature.m_objectData.DynamicFlags);
|
|
||||||
creature.ForceUpdateFieldChange();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// force dynflag update to update looter and lootable info
|
||||||
|
creature.m_values.ModifyValue(creature.m_objectData).ModifyValue(creature.m_objectData.DynamicFlags);
|
||||||
|
creature.ForceUpdateFieldChange();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -659,7 +659,6 @@ namespace Game.Loots
|
|||||||
public Loot(Map map, ObjectGuid owner, LootType type, Group group)
|
public Loot(Map map, ObjectGuid owner, LootType type, Group group)
|
||||||
{
|
{
|
||||||
loot_type = type;
|
loot_type = type;
|
||||||
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;
|
||||||
@@ -1052,26 +1051,6 @@ namespace Game.Loots
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Clear()
|
|
||||||
{
|
|
||||||
PlayerFFAItems.Clear();
|
|
||||||
|
|
||||||
foreach (ObjectGuid playerGuid in PlayersLooting)
|
|
||||||
{
|
|
||||||
Player player = Global.ObjAccessor.FindConnectedPlayer(playerGuid);
|
|
||||||
if (player != null)
|
|
||||||
player.GetSession().DoLootRelease(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
PlayersLooting.Clear();
|
|
||||||
items.Clear();
|
|
||||||
gold = 0;
|
|
||||||
unlootedCount = 0;
|
|
||||||
roundRobinPlayer = ObjectGuid.Empty;
|
|
||||||
_itemContext = 0;
|
|
||||||
_rolls.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void NotifyLootList(Map map)
|
public void NotifyLootList(Map map)
|
||||||
{
|
{
|
||||||
LootList lootList = new();
|
LootList lootList = new();
|
||||||
@@ -1095,7 +1074,6 @@ namespace Game.Loots
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Empty() { return items.Empty() && gold == 0; }
|
|
||||||
public bool IsLooted() { return gold == 0 && unlootedCount == 0; }
|
public bool IsLooted() { return gold == 0 && unlootedCount == 0; }
|
||||||
|
|
||||||
public void AddLooter(ObjectGuid guid) { PlayersLooting.Add(guid); }
|
public void AddLooter(ObjectGuid guid) { PlayersLooting.Add(guid); }
|
||||||
@@ -1117,7 +1095,6 @@ namespace Game.Loots
|
|||||||
public byte unlootedCount;
|
public byte unlootedCount;
|
||||||
public ObjectGuid roundRobinPlayer; // GUID of the player having the Round-Robin ownership for the loot. If 0, round robin owner has released.
|
public ObjectGuid roundRobinPlayer; // GUID of the player having the Round-Robin ownership for the loot. If 0, round robin owner has released.
|
||||||
public LootType loot_type; // required for achievement system
|
public LootType loot_type; // required for achievement system
|
||||||
public byte maxDuplicates; // Max amount of items with the same entry that can drop (default is 1; on 25 man raid mode 3)
|
|
||||||
|
|
||||||
List<ObjectGuid> PlayersLooting = new();
|
List<ObjectGuid> PlayersLooting = new();
|
||||||
MultiMap<ObjectGuid, NotNormalLootItem> PlayerFFAItems = new();
|
MultiMap<ObjectGuid, NotNormalLootItem> PlayerFFAItems = new();
|
||||||
|
|||||||
@@ -1100,12 +1100,6 @@ namespace Game.Loots
|
|||||||
if (!Convert.ToBoolean(item.lootmode & _lootMode))
|
if (!Convert.ToBoolean(item.lootmode & _lootMode))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
byte foundDuplicates = 0;
|
|
||||||
foreach (var lootItem in _loot.items)
|
|
||||||
if (lootItem.itemid == item.itemid)
|
|
||||||
if (++foundDuplicates == _loot.maxDuplicates)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1213,8 +1213,8 @@ namespace Game.Spells
|
|||||||
break;
|
break;
|
||||||
case Targets.UnitTargetTapList:
|
case Targets.UnitTargetTapList:
|
||||||
Creature creatureCaster = m_caster.ToCreature();
|
Creature creatureCaster = m_caster.ToCreature();
|
||||||
if (creatureCaster != null)
|
if (creatureCaster != null && !creatureCaster.GetTapList().Empty())
|
||||||
target = creatureCaster.GetLootRecipient();
|
target = Global.ObjAccessor.GetWorldObject(creatureCaster, creatureCaster.GetTapList().SelectRandom());
|
||||||
break;
|
break;
|
||||||
case Targets.UnitOwnCritter:
|
case Targets.UnitOwnCritter:
|
||||||
{
|
{
|
||||||
@@ -5188,7 +5188,7 @@ namespace Game.Spells
|
|||||||
|
|
||||||
Creature creature = m_targets.GetUnitTarget().ToCreature();
|
Creature creature = m_targets.GetUnitTarget().ToCreature();
|
||||||
Loot loot = creature.GetLootForPlayer(m_caster.ToPlayer());
|
Loot loot = creature.GetLootForPlayer(m_caster.ToPlayer());
|
||||||
if (loot != null && !loot.IsLooted())
|
if (loot != null && (!loot.IsLooted() || loot.loot_type == LootType.Skinning))
|
||||||
return SpellCastResult.TargetNotLooted;
|
return SpellCastResult.TargetNotLooted;
|
||||||
|
|
||||||
SkillType skill = creature.GetCreatureTemplate().GetRequiredLootSkill();
|
SkillType skill = creature.GetCreatureTemplate().GetRequiredLootSkill();
|
||||||
@@ -8557,8 +8557,8 @@ namespace Game.Spells
|
|||||||
{
|
{
|
||||||
Creature targetCreature = unit.ToCreature();
|
Creature targetCreature = unit.ToCreature();
|
||||||
if (targetCreature != null)
|
if (targetCreature != null)
|
||||||
if (!targetCreature.HasLootRecipient() && unitCaster.IsPlayer())
|
if (unitCaster.IsPlayer())
|
||||||
targetCreature.SetLootRecipient(unitCaster);
|
targetCreature.SetTappedBy(unitCaster);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1844,25 +1844,25 @@ namespace Game.Spells
|
|||||||
{
|
{
|
||||||
creature.StartPickPocketRefillTimer();
|
creature.StartPickPocketRefillTimer();
|
||||||
|
|
||||||
creature.loot = new Loot(creature.GetMap(), creature.GetGUID(), LootType.Pickpocketing, null);
|
creature._loot = new Loot(creature.GetMap(), creature.GetGUID(), LootType.Pickpocketing, null);
|
||||||
uint lootid = creature.GetCreatureTemplate().PickPocketId;
|
uint lootid = creature.GetCreatureTemplate().PickPocketId;
|
||||||
if (lootid != 0)
|
if (lootid != 0)
|
||||||
creature.loot.FillLoot(lootid, LootStorage.Pickpocketing, player, true);
|
creature._loot.FillLoot(lootid, LootStorage.Pickpocketing, player, true);
|
||||||
|
|
||||||
// Generate extra money for pick pocket loot
|
// Generate extra money for pick pocket loot
|
||||||
uint a = RandomHelper.URand(0, creature.GetLevel() / 2);
|
uint a = RandomHelper.URand(0, creature.GetLevel() / 2);
|
||||||
uint b = RandomHelper.URand(0, player.GetLevel() / 2);
|
uint b = RandomHelper.URand(0, player.GetLevel() / 2);
|
||||||
creature.loot.gold = (uint)(10 * (a + b) * WorldConfig.GetFloatValue(WorldCfg.RateDropMoney));
|
creature._loot.gold = (uint)(10 * (a + b) * WorldConfig.GetFloatValue(WorldCfg.RateDropMoney));
|
||||||
}
|
}
|
||||||
else if (creature.loot != null)
|
else if (creature._loot != null)
|
||||||
{
|
{
|
||||||
if (creature.loot.loot_type == LootType.Pickpocketing && creature.loot.IsLooted())
|
if (creature._loot.loot_type == LootType.Pickpocketing && creature._loot.IsLooted())
|
||||||
player.SendLootError(creature.loot.GetGUID(), creature.GetGUID(), LootError.AlreadPickPocketed);
|
player.SendLootError(creature._loot.GetGUID(), creature.GetGUID(), LootError.AlreadPickPocketed);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
player.SendLoot(creature.loot);
|
player.SendLoot(creature._loot);
|
||||||
}
|
}
|
||||||
|
|
||||||
[SpellEffectHandler(SpellEffectName.AddFarsight)]
|
[SpellEffectHandler(SpellEffectName.AddFarsight)]
|
||||||
@@ -3493,12 +3493,12 @@ namespace Game.Spells
|
|||||||
|
|
||||||
SkillType skill = creature.GetCreatureTemplate().GetRequiredLootSkill();
|
SkillType skill = creature.GetCreatureTemplate().GetRequiredLootSkill();
|
||||||
|
|
||||||
creature.RemoveUnitFlag(UnitFlags.Skinnable);
|
creature.SetUnitFlag3(UnitFlags3.AlreadySkinned);
|
||||||
creature.SetDynamicFlag(UnitDynFlags.Lootable);
|
creature.SetDynamicFlag(UnitDynFlags.Lootable);
|
||||||
creature.loot = new Loot(creature.GetMap(), creature.GetGUID(), LootType.Skinning, null);
|
Loot loot = new(creature.GetMap(), creature.GetGUID(), LootType.Skinning, null);
|
||||||
creature.loot.FillLoot(creature.GetCreatureTemplate().SkinLootId, LootStorage.Skinning, player, true);
|
creature.m_personalLoot[player.GetGUID()] = loot;
|
||||||
creature.SetLootRecipient(player, false);
|
loot.FillLoot(creature.GetCreatureTemplate().SkinLootId, LootStorage.Skinning, player, true);
|
||||||
player.SendLoot(creature.loot);
|
player.SendLoot(loot);
|
||||||
|
|
||||||
if (skill == SkillType.Skinning)
|
if (skill == SkillType.Skinning)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user