Core/Loot: Move loot generation out of Player::SendLoot
Port From (https://github.com/TrinityCore/TrinityCore/commit/8c20f620d7b070b13b40803095e5fa97f9349512)
This commit is contained in:
@@ -480,6 +480,8 @@ namespace Game.Entities
|
|||||||
// If there is no restock timer, or if the restock timer passed, the chest becomes ready to loot
|
// If there is no restock timer, or if the restock timer passed, the chest becomes ready to loot
|
||||||
m_restockTime = 0;
|
m_restockTime = 0;
|
||||||
m_lootState = LootState.Ready;
|
m_lootState = LootState.Ready;
|
||||||
|
loot = null;
|
||||||
|
m_personalLoot.Clear();
|
||||||
AddToObjectUpdateIfNeeded();
|
AddToObjectUpdateIfNeeded();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -699,11 +701,16 @@ namespace Game.Entities
|
|||||||
case GameObjectTypes.Chest:
|
case GameObjectTypes.Chest:
|
||||||
loot?.Update();
|
loot?.Update();
|
||||||
|
|
||||||
|
foreach (var (_, loot) in m_personalLoot)
|
||||||
|
loot.Update();
|
||||||
|
|
||||||
// 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 && GetGoInfo().Chest.chestRestockTime != 0 && GameTime.GetGameTime() >= m_restockTime)
|
||||||
{
|
{
|
||||||
m_restockTime = 0;
|
m_restockTime = 0;
|
||||||
m_lootState = LootState.Ready;
|
m_lootState = LootState.Ready;
|
||||||
|
loot = null;
|
||||||
|
m_personalLoot.Clear();
|
||||||
AddToObjectUpdateIfNeeded();
|
AddToObjectUpdateIfNeeded();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -789,6 +796,7 @@ namespace Game.Entities
|
|||||||
}
|
}
|
||||||
|
|
||||||
loot = null;
|
loot = null;
|
||||||
|
m_personalLoot.Clear();
|
||||||
|
|
||||||
// Do not delete chests or goobers that are not consumed on loot, while still allowing them to despawn when they expire if summoned
|
// Do not delete chests or goobers that are not consumed on loot, while still allowing them to despawn when they expire if summoned
|
||||||
bool isSummonedAndExpired = (GetOwner() != null || GetSpellId() != 0) && m_respawnTime == 0;
|
bool isSummonedAndExpired = (GetOwner() != null || GetSpellId() != 0) && m_respawnTime == 0;
|
||||||
@@ -940,44 +948,48 @@ namespace Game.Entities
|
|||||||
SendMessageToSet(packet, true);
|
SendMessageToSet(packet, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void GetFishLoot(Loot fishloot, Player loot_owner)
|
public Loot GetFishLoot(Player lootOwner)
|
||||||
{
|
{
|
||||||
fishloot.Clear();
|
|
||||||
|
|
||||||
uint zone, subzone;
|
uint zone, subzone;
|
||||||
uint defaultzone = 1;
|
uint defaultzone = 1;
|
||||||
GetZoneAndAreaId(out zone, out subzone);
|
GetZoneAndAreaId(out zone, out subzone);
|
||||||
|
|
||||||
|
Loot fishLoot = new(GetMap(), GetGUID(), LootType.Fishing, null);
|
||||||
|
|
||||||
// if subzone loot exist use it
|
// if subzone loot exist use it
|
||||||
fishloot.FillLoot(subzone, LootStorage.Fishing, loot_owner, true, true);
|
fishLoot.FillLoot(subzone, LootStorage.Fishing, lootOwner, true, true);
|
||||||
if (fishloot.Empty())
|
if (fishLoot.Empty())
|
||||||
{
|
{
|
||||||
//subzone no result,use zone loot
|
//subzone no result,use zone loot
|
||||||
fishloot.FillLoot(zone, LootStorage.Fishing, loot_owner, true);
|
fishLoot.FillLoot(zone, LootStorage.Fishing, lootOwner, true);
|
||||||
//use zone 1 as default, somewhere fishing got nothing,becase subzone and zone not set, like Off the coast of Storm Peaks.
|
//use zone 1 as default, somewhere fishing got nothing,becase subzone and zone not set, like Off the coast of Storm Peaks.
|
||||||
if (fishloot.Empty())
|
if (fishLoot.Empty())
|
||||||
fishloot.FillLoot(defaultzone, LootStorage.Fishing, loot_owner, true, true);
|
fishLoot.FillLoot(defaultzone, LootStorage.Fishing, lootOwner, true, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return fishLoot;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void GetFishLootJunk(Loot fishloot, Player loot_owner)
|
public Loot GetFishLootJunk(Player lootOwner)
|
||||||
{
|
{
|
||||||
fishloot.Clear();
|
|
||||||
|
|
||||||
uint zone, subzone;
|
uint zone, subzone;
|
||||||
uint defaultzone = 1;
|
uint defaultzone = 1;
|
||||||
GetZoneAndAreaId(out zone, out subzone);
|
GetZoneAndAreaId(out zone, out subzone);
|
||||||
|
|
||||||
|
Loot fishLoot = new(GetMap(), GetGUID(), LootType.FishingJunk, null);
|
||||||
|
|
||||||
// if subzone loot exist use it
|
// if subzone loot exist use it
|
||||||
fishloot.FillLoot(subzone, LootStorage.Fishing, loot_owner, true, true, LootModes.JunkFish);
|
fishLoot.FillLoot(subzone, LootStorage.Fishing, lootOwner, true, true, LootModes.JunkFish);
|
||||||
if (fishloot.Empty()) //use this becase if zone or subzone has normal mask drop, then fishloot.FillLoot return true.
|
if (fishLoot.Empty()) //use this becase if zone or subzone has normal mask drop, then fishloot.FillLoot return true.
|
||||||
{
|
{
|
||||||
//use zone loot
|
//use zone loot
|
||||||
fishloot.FillLoot(zone, LootStorage.Fishing, loot_owner, true, true, LootModes.JunkFish);
|
fishLoot.FillLoot(zone, LootStorage.Fishing, lootOwner, true, true, LootModes.JunkFish);
|
||||||
if (fishloot.Empty())
|
if (fishLoot.Empty())
|
||||||
//use zone 1 as default
|
//use zone 1 as default
|
||||||
fishloot.FillLoot(defaultzone, LootStorage.Fishing, loot_owner, true, true, LootModes.JunkFish);
|
fishLoot.FillLoot(defaultzone, LootStorage.Fishing, lootOwner, true, true, LootModes.JunkFish);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return fishLoot;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SaveToDB()
|
public void SaveToDB()
|
||||||
@@ -1886,10 +1898,16 @@ namespace Game.Entities
|
|||||||
SetLootState(LootState.JustDeactivated);
|
SetLootState(LootState.JustDeactivated);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
loot = GetFishLoot(player);
|
||||||
player.SendLoot(GetGUID(), LootType.Fishing);
|
player.SendLoot(GetGUID(), LootType.Fishing);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else// If fishing skill is too low, send junk loot.
|
else// If fishing skill is too low, send junk loot.
|
||||||
|
{
|
||||||
|
loot = GetFishLootJunk(player);
|
||||||
player.SendLoot(GetGUID(), LootType.FishingJunk);
|
player.SendLoot(GetGUID(), LootType.FishingJunk);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case LootState.JustDeactivated: // nothing to do, will be deleted at next update
|
case LootState.JustDeactivated: // nothing to do, will be deleted at next update
|
||||||
@@ -2099,6 +2117,10 @@ namespace Game.Entities
|
|||||||
|
|
||||||
Player player = user.ToPlayer();
|
Player player = user.ToPlayer();
|
||||||
|
|
||||||
|
Loot loot = new Loot(GetMap(), GetGUID(), LootType.Fishinghole, null);
|
||||||
|
loot.FillLoot(GetGoInfo().GetLootId(), LootStorage.Gameobject, player, true);
|
||||||
|
m_personalLoot[player.GetGUID()] = loot;
|
||||||
|
|
||||||
player.SendLoot(GetGUID(), LootType.Fishinghole);
|
player.SendLoot(GetGUID(), LootType.Fishinghole);
|
||||||
player.UpdateCriteria(CriteriaType.CatchFishInFishingHole, GetGoInfo().entry);
|
player.UpdateCriteria(CriteriaType.CatchFishInFishingHole, GetGoInfo().entry);
|
||||||
return;
|
return;
|
||||||
@@ -2814,7 +2836,16 @@ namespace Game.Entities
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override Loot GetLootForPlayer(Player player)
|
||||||
|
{
|
||||||
|
if (m_personalLoot.Empty())
|
||||||
|
return loot;
|
||||||
|
|
||||||
|
return m_personalLoot.LookupByKey(player.GetGUID());
|
||||||
|
}
|
||||||
|
|
||||||
public void SetLinkedTrap(GameObject linkedTrap) { m_linkedTrap = linkedTrap.GetGUID(); }
|
public void SetLinkedTrap(GameObject linkedTrap) { m_linkedTrap = linkedTrap.GetGUID(); }
|
||||||
|
|
||||||
public GameObject GetLinkedTrap()
|
public GameObject GetLinkedTrap()
|
||||||
{
|
{
|
||||||
return ObjectAccessor.GetGameObject(this, m_linkedTrap);
|
return ObjectAccessor.GetGameObject(this, m_linkedTrap);
|
||||||
@@ -3298,8 +3329,6 @@ namespace Game.Entities
|
|||||||
|
|
||||||
bool HasLootRecipient() { return !m_lootRecipient.IsEmpty() || !m_lootRecipientGroup.IsEmpty(); }
|
bool HasLootRecipient() { return !m_lootRecipient.IsEmpty() || !m_lootRecipientGroup.IsEmpty(); }
|
||||||
|
|
||||||
public override Loot GetLootForPlayer(Player player) { return loot; }
|
|
||||||
|
|
||||||
public override uint GetLevelForTarget(WorldObject target)
|
public override uint GetLevelForTarget(WorldObject target)
|
||||||
{
|
{
|
||||||
Unit owner = GetOwner();
|
Unit owner = GetOwner();
|
||||||
@@ -3425,6 +3454,7 @@ namespace Game.Entities
|
|||||||
List<ObjectGuid> m_SkillupList = new();
|
List<ObjectGuid> m_SkillupList = new();
|
||||||
|
|
||||||
public Loot loot;
|
public Loot loot;
|
||||||
|
Dictionary<ObjectGuid, Loot> m_personalLoot = new();
|
||||||
|
|
||||||
public GameObjectModel m_model;
|
public GameObjectModel m_model;
|
||||||
|
|
||||||
|
|||||||
@@ -6109,95 +6109,13 @@ namespace Game.Entities
|
|||||||
if (guid.IsGameObject())
|
if (guid.IsGameObject())
|
||||||
{
|
{
|
||||||
GameObject go = GetMap().GetGameObject(guid);
|
GameObject go = GetMap().GetGameObject(guid);
|
||||||
bool shouldLootRelease(GameObject go, LootType lootType)
|
if (go == null)
|
||||||
{
|
|
||||||
// not check distance for GO in case owned GO (fishing bobber case, for example)
|
|
||||||
// And permit out of range GO with no owner in case fishing hole
|
|
||||||
if (!go)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
switch (lootType)
|
|
||||||
{
|
|
||||||
case LootType.Fishing:
|
|
||||||
case LootType.FishingJunk:
|
|
||||||
if (go.GetOwnerGUID() != GetGUID())
|
|
||||||
return true;
|
|
||||||
break;
|
|
||||||
case LootType.Fishinghole:
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
if (!go.IsWithinDistInMap(this))
|
|
||||||
return true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shouldLootRelease(go, loot_type))
|
|
||||||
{
|
{
|
||||||
SendLootRelease(guid);
|
SendLootRelease(guid);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
loot = go.GetLootForPlayer(this);
|
loot = go.GetLootForPlayer(this);
|
||||||
|
|
||||||
// loot was generated and respawntime has passed since then, allow to recreate loot
|
|
||||||
// to avoid bugs, this rule covers spawned gameobjects only
|
|
||||||
// Don't allow to regenerate chest loot inside instances and raids, to avoid exploits with duplicate boss loot being given for some encounters
|
|
||||||
if (go.IsSpawnedByDefault() && go.GetLootState() == LootState.Activated && (loot == null || loot.IsLooted()) && !go.GetMap().Instanceable() && go.GetLootGenerationTime() + go.GetRespawnDelay() < GameTime.GetGameTime())
|
|
||||||
go.SetLootState(LootState.Ready);
|
|
||||||
|
|
||||||
if (go.GetLootState() == LootState.Ready)
|
|
||||||
{
|
|
||||||
uint lootid = go.GetGoInfo().GetLootId();
|
|
||||||
Battleground bg = GetBattleground();
|
|
||||||
if (bg)
|
|
||||||
{
|
|
||||||
if (!bg.CanActivateGO((int)go.GetEntry(), (uint)bg.GetPlayerTeam(GetGUID())))
|
|
||||||
{
|
|
||||||
SendLootRelease(guid);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Group group = GetGroup();
|
|
||||||
bool groupRules = (group != null && go.GetGoInfo().type == GameObjectTypes.Chest && go.GetGoInfo().Chest.usegrouplootrules != 0);
|
|
||||||
|
|
||||||
loot = new Loot(GetMap(), guid, loot_type, groupRules ? group : null);
|
|
||||||
if (go.GetMap().Is25ManRaid())
|
|
||||||
loot.maxDuplicates = 3;
|
|
||||||
|
|
||||||
go.loot = loot;
|
|
||||||
|
|
||||||
if (lootid != 0)
|
|
||||||
{
|
|
||||||
// check current RR player and get next if necessary
|
|
||||||
if (groupRules)
|
|
||||||
group.UpdateLooterGuid(go, true);
|
|
||||||
|
|
||||||
loot.FillLoot(lootid, LootStorage.Gameobject, this, !groupRules, false, go.GetLootMode(), GetMap().GetDifficultyLootItemContext());
|
|
||||||
go.SetLootGenerationTime();
|
|
||||||
|
|
||||||
// get next RR player (for next loot)
|
|
||||||
if (groupRules && !loot.Empty())
|
|
||||||
group.UpdateLooterGuid(go);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (go.GetLootMode() > 0)
|
|
||||||
{
|
|
||||||
GameObjectTemplateAddon addon = go.GetTemplateAddon();
|
|
||||||
if (addon != null)
|
|
||||||
loot.GenerateMoneyLoot(addon.Mingold, addon.Maxgold);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (loot_type == LootType.Fishing)
|
|
||||||
go.GetFishLoot(loot, this);
|
|
||||||
else if (loot_type == LootType.FishingJunk)
|
|
||||||
go.GetFishLootJunk(loot, this);
|
|
||||||
|
|
||||||
go.SetLootState(LootState.Activated, this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (guid.IsItem())
|
else if (guid.IsItem())
|
||||||
{
|
{
|
||||||
@@ -6210,44 +6128,11 @@ namespace Game.Entities
|
|||||||
}
|
}
|
||||||
|
|
||||||
loot = item.GetLootForPlayer(this);
|
loot = item.GetLootForPlayer(this);
|
||||||
|
|
||||||
// If item doesn't already have loot, attempt to load it. If that
|
|
||||||
// fails then this is first time opening, generate loot
|
|
||||||
if (!item.m_lootGenerated && !Global.LootItemStorage.LoadStoredLoot(item, this))
|
|
||||||
{
|
|
||||||
item.m_lootGenerated = true;
|
|
||||||
loot = new Loot(GetMap(), guid, loot_type, null);
|
|
||||||
item.loot = loot;
|
|
||||||
|
|
||||||
switch (loot_type)
|
|
||||||
{
|
|
||||||
case LootType.Disenchanting:
|
|
||||||
loot.FillLoot(item.GetDisenchantLoot(this).Id, LootStorage.Disenchant, this, true);
|
|
||||||
break;
|
|
||||||
case LootType.Prospecting:
|
|
||||||
loot.FillLoot(item.GetEntry(), LootStorage.Prospecting, this, true);
|
|
||||||
break;
|
|
||||||
case LootType.Milling:
|
|
||||||
loot.FillLoot(item.GetEntry(), LootStorage.Milling, this, true);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
loot.GenerateMoneyLoot(item.GetTemplate().MinMoneyLoot, item.GetTemplate().MaxMoneyLoot);
|
|
||||||
loot.FillLoot(item.GetEntry(), LootStorage.Items, this, true, loot.gold != 0);
|
|
||||||
|
|
||||||
// Force save the loot and money items that were just rolled
|
|
||||||
// Also saves the container item ID in Loot struct (not to DB)
|
|
||||||
if (loot.gold > 0 || loot.unlootedCount > 0)
|
|
||||||
Global.LootItemStorage.AddNewStoredLoot(item.GetGUID().GetCounter(), loot, this);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (guid.IsCorpse()) // remove insignia
|
else if (guid.IsCorpse()) // remove insignia
|
||||||
{
|
{
|
||||||
Corpse bones = ObjectAccessor.GetCorpse(this, guid);
|
Corpse bones = ObjectAccessor.GetCorpse(this, guid);
|
||||||
|
if (bones == null)
|
||||||
if (bones == null || !(loot_type == LootType.Corpse || loot_type == LootType.Insignia) || bones.GetCorpseType() != CorpseType.Bones)
|
|
||||||
{
|
{
|
||||||
SendLootRelease(guid);
|
SendLootRelease(guid);
|
||||||
return;
|
return;
|
||||||
@@ -6260,77 +6145,13 @@ namespace Game.Entities
|
|||||||
Creature creature = GetMap().GetCreature(guid);
|
Creature creature = GetMap().GetCreature(guid);
|
||||||
|
|
||||||
// must be in range and creature must be alive for pickpocket and must be dead for another loot
|
// must be in range and creature must be alive for pickpocket and must be dead for another loot
|
||||||
if (creature == null || creature.IsAlive() != (loot_type == LootType.Pickpocketing) || (!aeLooting && !creature.IsWithinDistInMap(this, SharedConst.InteractionDistance)))
|
if (creature == null)
|
||||||
{
|
|
||||||
SendLootRelease(guid);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (loot_type == LootType.Pickpocketing && IsFriendlyTo(creature))
|
|
||||||
{
|
{
|
||||||
SendLootRelease(guid);
|
SendLootRelease(guid);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
loot = creature.GetLootForPlayer(this);
|
loot = creature.GetLootForPlayer(this);
|
||||||
|
|
||||||
if (loot_type == LootType.Pickpocketing)
|
|
||||||
{
|
|
||||||
if (loot == null || loot.loot_type != LootType.Pickpocketing)
|
|
||||||
{
|
|
||||||
if (creature.CanGeneratePickPocketLoot())
|
|
||||||
{
|
|
||||||
creature.StartPickPocketRefillTimer();
|
|
||||||
|
|
||||||
loot = new Loot(GetMap(), creature.GetGUID(), LootType.Pickpocketing, null);
|
|
||||||
creature.loot = loot;
|
|
||||||
uint lootid = creature.GetCreatureTemplate().PickPocketId;
|
|
||||||
if (lootid != 0)
|
|
||||||
loot.FillLoot(lootid, LootStorage.Pickpocketing, this, true);
|
|
||||||
|
|
||||||
// Generate extra money for pick pocket loot
|
|
||||||
uint a = RandomHelper.URand(0, creature.GetLevel() / 2);
|
|
||||||
uint b = RandomHelper.URand(0, GetLevel() / 2);
|
|
||||||
loot.gold = (uint)(10 * (a + b) * WorldConfig.GetFloatValue(WorldCfg.RateDropMoney));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
SendLootError(loot.GetGUID(), guid, LootError.AlreadPickPocketed);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// exploit fix
|
|
||||||
if (!creature.HasDynamicFlag(UnitDynFlags.Lootable))
|
|
||||||
{
|
|
||||||
SendLootError(loot.GetGUID(), guid, LootError.DidntKill);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// the player whose group may loot the corpse
|
|
||||||
Player recipient = creature.GetLootRecipient();
|
|
||||||
Group recipientGroup = creature.GetLootRecipientGroup();
|
|
||||||
if (!recipient && !recipientGroup)
|
|
||||||
{
|
|
||||||
SendLootError(loot.GetGUID(), guid, LootError.DidntKill);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (loot.loot_type == LootType.Skinning)
|
|
||||||
{
|
|
||||||
loot_type = LootType.Skinning;
|
|
||||||
}
|
|
||||||
else if (loot_type == LootType.Skinning)
|
|
||||||
{
|
|
||||||
loot.Clear();
|
|
||||||
loot.FillLoot(creature.GetCreatureTemplate().SkinLootId, LootStorage.Skinning, this, true);
|
|
||||||
|
|
||||||
// Set new loot recipient
|
|
||||||
creature.SetLootRecipient(this, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -176,6 +176,11 @@ namespace Game
|
|||||||
}
|
}
|
||||||
|
|
||||||
public bool Invoke(Creature creature)
|
public bool Invoke(Creature creature)
|
||||||
|
{
|
||||||
|
return IsValidAELootTarget(creature);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsValidLootTarget(Creature creature)
|
||||||
{
|
{
|
||||||
if (creature.IsAlive())
|
if (creature.IsAlive())
|
||||||
return false;
|
return false;
|
||||||
@@ -189,6 +194,14 @@ namespace Game
|
|||||||
return _looter.IsAllowedToLoot(creature);
|
return _looter.IsAllowedToLoot(creature);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsValidAELootTarget(Creature creature)
|
||||||
|
{
|
||||||
|
if (creature.GetGUID() == _mainLootTarget)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return IsValidLootTarget(creature);
|
||||||
|
}
|
||||||
|
|
||||||
Player _looter;
|
Player _looter;
|
||||||
ObjectGuid _mainLootTarget;
|
ObjectGuid _mainLootTarget;
|
||||||
}
|
}
|
||||||
@@ -200,6 +213,14 @@ namespace Game
|
|||||||
if (!GetPlayer().IsAlive() || !packet.Unit.IsCreatureOrVehicle())
|
if (!GetPlayer().IsAlive() || !packet.Unit.IsCreatureOrVehicle())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
Creature lootTarget = ObjectAccessor.GetCreature(GetPlayer(), packet.Unit);
|
||||||
|
if (!lootTarget)
|
||||||
|
return;
|
||||||
|
|
||||||
|
AELootCreatureCheck check = new(_player, packet.Unit);
|
||||||
|
if (!check.IsValidLootTarget(lootTarget))
|
||||||
|
return;
|
||||||
|
|
||||||
// interrupt cast
|
// interrupt cast
|
||||||
if (GetPlayer().IsNonMeleeSpellCast(false))
|
if (GetPlayer().IsNonMeleeSpellCast(false))
|
||||||
GetPlayer().InterruptNonMeleeSpells(false);
|
GetPlayer().InterruptNonMeleeSpells(false);
|
||||||
@@ -207,7 +228,6 @@ namespace Game
|
|||||||
GetPlayer().RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags.Looting);
|
GetPlayer().RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags.Looting);
|
||||||
|
|
||||||
List<Creature> corpses = new();
|
List<Creature> corpses = new();
|
||||||
AELootCreatureCheck check = new(_player, packet.Unit);
|
|
||||||
CreatureListSearcher searcher = new(_player, corpses, check);
|
CreatureListSearcher searcher = new(_player, corpses, check);
|
||||||
Cell.VisitGridObjects(_player, searcher, AELootCreatureCheck.LootDistance);
|
Cell.VisitGridObjects(_player, searcher, AELootCreatureCheck.LootDistance);
|
||||||
|
|
||||||
@@ -247,6 +267,8 @@ namespace Game
|
|||||||
if (player.GetLootGUID() == lguid)
|
if (player.GetLootGUID() == lguid)
|
||||||
player.SetLootGUID(ObjectGuid.Empty);
|
player.SetLootGUID(ObjectGuid.Empty);
|
||||||
|
|
||||||
|
//Player is not looking at loot list, he doesn't need to see updates on the loot list
|
||||||
|
loot.RemoveLooter(player.GetGUID());
|
||||||
player.SendLootRelease(lguid);
|
player.SendLootRelease(lguid);
|
||||||
player.GetAELootView().Remove(loot.GetGUID());
|
player.GetAELootView().Remove(loot.GetGUID());
|
||||||
|
|
||||||
@@ -264,12 +286,7 @@ namespace Game
|
|||||||
if (!go || ((go.GetOwnerGUID() != player.GetGUID() && go.GetGoType() != GameObjectTypes.FishingHole) && !go.IsWithinDistInMap(player)))
|
if (!go || ((go.GetOwnerGUID() != player.GetGUID() && go.GetGoType() != GameObjectTypes.FishingHole) && !go.IsWithinDistInMap(player)))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (go.GetGoType() == GameObjectTypes.Door)
|
if (loot.IsLooted() || go.GetGoType() == GameObjectTypes.FishingNode || go.GetGoType() == GameObjectTypes.FishingHole)
|
||||||
{
|
|
||||||
// locked doors are opened with spelleffect openlock, prevent remove its as looted
|
|
||||||
go.UseDoorOrButton();
|
|
||||||
}
|
|
||||||
else if (loot.IsLooted() || go.GetGoType() == GameObjectTypes.FishingNode)
|
|
||||||
{
|
{
|
||||||
if (go.GetGoType() == GameObjectTypes.FishingHole)
|
if (go.GetGoType() == GameObjectTypes.FishingHole)
|
||||||
{ // The fishing hole used once more
|
{ // The fishing hole used once more
|
||||||
@@ -288,10 +305,6 @@ namespace Game
|
|||||||
{
|
{
|
||||||
// not fully looted object
|
// not fully looted object
|
||||||
go.SetLootState(LootState.Activated, player);
|
go.SetLootState(LootState.Activated, player);
|
||||||
|
|
||||||
// if the round robin player release, reset it.
|
|
||||||
if (player.GetGUID() == loot.roundRobinPlayer)
|
|
||||||
loot.roundRobinPlayer.Clear();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (lguid.IsCorpse()) // ONLY remove insignia at BG
|
else if (lguid.IsCorpse()) // ONLY remove insignia at BG
|
||||||
@@ -342,12 +355,6 @@ namespace Game
|
|||||||
if (creature == null)
|
if (creature == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!creature.IsWithinDistInMap(player, AELootCreatureCheck.LootDistance))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (creature.IsAlive() != (loot != null && loot.loot_type == LootType.Pickpocketing))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (loot.IsLooted())
|
if (loot.IsLooted())
|
||||||
{
|
{
|
||||||
creature.RemoveDynamicFlag(UnitDynFlags.Lootable);
|
creature.RemoveDynamicFlag(UnitDynFlags.Lootable);
|
||||||
|
|||||||
@@ -20,10 +20,10 @@ using Framework.Database;
|
|||||||
using Game.DataStorage;
|
using Game.DataStorage;
|
||||||
using Game.Entities;
|
using Game.Entities;
|
||||||
using Game.Guilds;
|
using Game.Guilds;
|
||||||
|
using Game.Loots;
|
||||||
using Game.Networking;
|
using Game.Networking;
|
||||||
using Game.Networking.Packets;
|
using Game.Networking.Packets;
|
||||||
using Game.Spells;
|
using Game.Spells;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
@@ -190,11 +190,30 @@ namespace Game
|
|||||||
{
|
{
|
||||||
PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHARACTER_GIFT_BY_ITEM);
|
PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_CHARACTER_GIFT_BY_ITEM);
|
||||||
stmt.AddValue(0, item.GetGUID().GetCounter());
|
stmt.AddValue(0, item.GetGUID().GetCounter());
|
||||||
|
|
||||||
|
var pos = item.GetPos();
|
||||||
|
var itemGuid = item.GetGUID();
|
||||||
_queryProcessor.AddCallback(DB.Characters.AsyncQuery(stmt)
|
_queryProcessor.AddCallback(DB.Characters.AsyncQuery(stmt)
|
||||||
.WithCallback(result => HandleOpenWrappedItemCallback(item.GetPos(), item.GetGUID(), result)));
|
.WithCallback(result => HandleOpenWrappedItemCallback(pos, itemGuid, result)));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
// If item doesn't already have loot, attempt to load it. If that
|
||||||
|
// fails then this is first time opening, generate loot
|
||||||
|
if (!item.m_lootGenerated && !Global.LootItemStorage.LoadStoredLoot(item, player))
|
||||||
|
{
|
||||||
|
Loot loot = new(player.GetMap(), item.GetGUID(), LootType.Item, null);
|
||||||
|
item.loot = loot;
|
||||||
|
loot.GenerateMoneyLoot(item.GetTemplate().MinMoneyLoot, item.GetTemplate().MaxMoneyLoot);
|
||||||
|
loot.FillLoot(item.GetEntry(), LootStorage.Items, player, true, loot.gold != 0);
|
||||||
|
|
||||||
|
// Force save the loot and money items that were just rolled
|
||||||
|
// Also saves the container item ID in Loot struct (not to DB)
|
||||||
|
if (loot.gold > 0 || loot.unlootedCount > 0)
|
||||||
|
Global.LootItemStorage.AddNewStoredLoot(item.GetGUID().GetCounter(), loot, player);
|
||||||
|
}
|
||||||
player.SendLoot(item.GetGUID(), LootType.Item);
|
player.SendLoot(item.GetGUID(), LootType.Item);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HandleOpenWrappedItemCallback(ushort pos, ObjectGuid itemGuid, SQLResult result)
|
void HandleOpenWrappedItemCallback(ushort pos, ObjectGuid itemGuid, SQLResult result)
|
||||||
|
|||||||
@@ -762,7 +762,8 @@ namespace Game.Loots
|
|||||||
Group group = lootOwner.GetGroup();
|
Group group = lootOwner.GetGroup();
|
||||||
if (!personal && group != null)
|
if (!personal && group != null)
|
||||||
{
|
{
|
||||||
roundRobinPlayer = lootOwner.GetGUID();
|
if (loot_type == LootType.Corpse)
|
||||||
|
roundRobinPlayer = lootOwner.GetGUID();
|
||||||
|
|
||||||
for (GroupReference refe = group.GetFirstMember(); refe != null; refe = refe.Next())
|
for (GroupReference refe = group.GetFirstMember(); refe != null; refe = refe.Next())
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1225,24 +1225,65 @@ namespace Game.Spells
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
case GameObjectTypes.SpellFocus:
|
case GameObjectTypes.SpellFocus:
|
||||||
|
{
|
||||||
// triggering linked GO
|
// triggering linked GO
|
||||||
uint trapEntry = gameObjTarget.GetGoInfo().SpellFocus.linkedTrap;
|
uint trapEntry = gameObjTarget.GetGoInfo().SpellFocus.linkedTrap;
|
||||||
if (trapEntry != 0)
|
if (trapEntry != 0)
|
||||||
gameObjTarget.TriggeringLinkedGameObject(trapEntry, player);
|
gameObjTarget.TriggeringLinkedGameObject(trapEntry, player);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
case GameObjectTypes.Chest:
|
case GameObjectTypes.Chest:
|
||||||
|
{
|
||||||
// @todo possible must be moved to loot release (in different from linked triggering)
|
// @todo possible must be moved to loot release (in different from linked triggering)
|
||||||
if (gameObjTarget.GetGoInfo().Chest.triggeredEvent != 0)
|
var bg = player.GetBattleground();
|
||||||
|
if (bg != null)
|
||||||
{
|
{
|
||||||
Log.outDebug(LogFilter.Spells, "Chest ScriptStart id {0} for GO {1}", gameObjTarget.GetGoInfo().Chest.triggeredEvent, gameObjTarget.GetSpawnId());
|
if (!bg.CanActivateGO((int)gameObjTarget.GetEntry(), (uint)bg.GetPlayerTeam(player.GetGUID())))
|
||||||
player.GetMap().ScriptsStart(ScriptsType.Event, gameObjTarget.GetGoInfo().Chest.triggeredEvent, player, gameObjTarget);
|
{
|
||||||
|
player.SendLootRelease(guid);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// triggering linked GO
|
if (gameObjTarget.GetLootState() == LootState.Ready)
|
||||||
uint _trapEntry = gameObjTarget.GetGoInfo().Chest.linkedTrap;
|
{
|
||||||
if (_trapEntry != 0)
|
uint lootId = gameObjTarget.GetGoInfo().GetLootId();
|
||||||
gameObjTarget.TriggeringLinkedGameObject(_trapEntry, player);
|
if (lootId != 0)
|
||||||
|
{
|
||||||
|
gameObjTarget.SetLootGenerationTime();
|
||||||
|
|
||||||
|
Group group = player.GetGroup();
|
||||||
|
bool groupRules = group != null && gameObjTarget.GetGoInfo().Chest.usegrouplootrules != 0;
|
||||||
|
|
||||||
|
Loot loot = new(gameObjTarget.GetMap(), guid, loottype, groupRules ? group : null);
|
||||||
|
gameObjTarget.loot = loot;
|
||||||
|
|
||||||
|
loot.FillLoot(lootId, LootStorage.Gameobject, player, !groupRules, false, gameObjTarget.GetLootMode(), gameObjTarget.GetMap().GetDifficultyLootItemContext());
|
||||||
|
|
||||||
|
if (gameObjTarget.GetLootMode() > 0)
|
||||||
|
{
|
||||||
|
var addon = gameObjTarget.GetTemplateAddon();
|
||||||
|
if (addon != null)
|
||||||
|
loot.GenerateMoneyLoot(addon.Mingold, addon.Maxgold);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @todo possible must be moved to loot release (in different from linked triggering)
|
||||||
|
if (gameObjTarget.GetGoInfo().Chest.triggeredEvent != 0)
|
||||||
|
{
|
||||||
|
Log.outDebug(LogFilter.Spells, $"Chest ScriptStart id {gameObjTarget.GetGoInfo().Chest.triggeredEvent} for GO {gameObjTarget.GetSpawnId()}");
|
||||||
|
GameEvents.Trigger(gameObjTarget.GetGoInfo().Chest.triggeredEvent, player, gameObjTarget);
|
||||||
|
}
|
||||||
|
|
||||||
|
// triggering linked GO
|
||||||
|
uint trapEntry = gameObjTarget.GetGoInfo().Chest.linkedTrap;
|
||||||
|
if (trapEntry != 0)
|
||||||
|
gameObjTarget.TriggeringLinkedGameObject(trapEntry, player);
|
||||||
|
|
||||||
|
gameObjTarget.SetLootState(LootState.Activated, player);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
// Don't return, let loots been taken
|
// Don't return, let loots been taken
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@@ -1909,10 +1950,37 @@ namespace Game.Spells
|
|||||||
if (effectHandleMode != SpellEffectHandleMode.HitTarget)
|
if (effectHandleMode != SpellEffectHandleMode.HitTarget)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!m_caster.IsTypeId(TypeId.Player))
|
Player player = m_caster.ToPlayer();
|
||||||
|
if (player == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
m_caster.ToPlayer().SendLoot(unitTarget.GetGUID(), LootType.Pickpocketing);
|
Creature creature = unitTarget?.ToCreature();
|
||||||
|
if (creature == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (creature.CanGeneratePickPocketLoot())
|
||||||
|
{
|
||||||
|
creature.StartPickPocketRefillTimer();
|
||||||
|
|
||||||
|
creature.loot = new Loot(creature.GetMap(), creature.GetGUID(), LootType.Pickpocketing, null);
|
||||||
|
uint lootid = creature.GetCreatureTemplate().PickPocketId;
|
||||||
|
if (lootid != 0)
|
||||||
|
creature.loot.FillLoot(lootid, LootStorage.Pickpocketing, player, true);
|
||||||
|
|
||||||
|
// Generate extra money for pick pocket loot
|
||||||
|
uint a = RandomHelper.URand(0, creature.GetLevel() / 2);
|
||||||
|
uint b = RandomHelper.URand(0, player.GetLevel() / 2);
|
||||||
|
creature.loot.gold = (uint)(10 * (a + b) * WorldConfig.GetFloatValue(WorldCfg.RateDropMoney));
|
||||||
|
}
|
||||||
|
else if (creature.loot != null)
|
||||||
|
{
|
||||||
|
if (creature.loot.loot_type == LootType.Pickpocketing && creature.loot.IsLooted())
|
||||||
|
player.SendLootError(creature.loot.GetGUID(), creature.GetGUID(), LootError.AlreadPickPocketed);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
player.SendLoot(unitTarget.GetGUID(), LootType.Pickpocketing);
|
||||||
}
|
}
|
||||||
|
|
||||||
[SpellEffectHandler(SpellEffectName.AddFarsight)]
|
[SpellEffectHandler(SpellEffectName.AddFarsight)]
|
||||||
@@ -3170,6 +3238,8 @@ namespace Game.Spells
|
|||||||
if (caster != null)
|
if (caster != null)
|
||||||
{
|
{
|
||||||
caster.UpdateCraftSkill(m_spellInfo);
|
caster.UpdateCraftSkill(m_spellInfo);
|
||||||
|
itemTarget.loot = new Loot(caster.GetMap(), itemTarget.GetGUID(), LootType.Disenchanting, null);
|
||||||
|
itemTarget.loot.FillLoot(itemTarget.GetDisenchantLoot(caster).Id, LootStorage.Disenchant, caster, true);
|
||||||
caster.SendLoot(itemTarget.GetGUID(), LootType.Disenchanting);
|
caster.SendLoot(itemTarget.GetGUID(), LootType.Disenchanting);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3543,6 +3613,9 @@ namespace Game.Spells
|
|||||||
|
|
||||||
creature.RemoveUnitFlag(UnitFlags.Skinnable);
|
creature.RemoveUnitFlag(UnitFlags.Skinnable);
|
||||||
creature.SetDynamicFlag(UnitDynFlags.Lootable);
|
creature.SetDynamicFlag(UnitDynFlags.Lootable);
|
||||||
|
creature.loot = new Loot(creature.GetMap(), creature.GetGUID(), LootType.Skinning, null);
|
||||||
|
creature.loot.FillLoot(creature.GetCreatureTemplate().SkinLootId, LootStorage.Skinning, player, true);
|
||||||
|
creature.SetLootRecipient(player, false);
|
||||||
player.SendLoot(creature.GetGUID(), LootType.Skinning);
|
player.SendLoot(creature.GetGUID(), LootType.Skinning);
|
||||||
|
|
||||||
if (skill == SkillType.Skinning)
|
if (skill == SkillType.Skinning)
|
||||||
@@ -4254,6 +4327,8 @@ namespace Game.Spells
|
|||||||
player.UpdateGatherSkill(SkillType.Jewelcrafting, SkillValue, reqSkillValue);
|
player.UpdateGatherSkill(SkillType.Jewelcrafting, SkillValue, reqSkillValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
itemTarget.loot = new Loot(player.GetMap(), itemTarget.GetGUID(), LootType.Prospecting, null);
|
||||||
|
itemTarget.loot.FillLoot(itemTarget.GetEntry(), LootStorage.Prospecting, player, true);
|
||||||
player.SendLoot(itemTarget.GetGUID(), LootType.Prospecting);
|
player.SendLoot(itemTarget.GetGUID(), LootType.Prospecting);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4280,6 +4355,8 @@ namespace Game.Spells
|
|||||||
player.UpdateGatherSkill(SkillType.Inscription, SkillValue, reqSkillValue);
|
player.UpdateGatherSkill(SkillType.Inscription, SkillValue, reqSkillValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
itemTarget.loot = new Loot(player.GetMap(), itemTarget.GetGUID(), LootType.Milling, null);
|
||||||
|
itemTarget.loot.FillLoot(itemTarget.GetEntry(), LootStorage.Milling, player, true);
|
||||||
player.SendLoot(itemTarget.GetGUID(), LootType.Milling);
|
player.SendLoot(itemTarget.GetGUID(), LootType.Milling);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user