Core/Loot: Move loot generation out of Player::SendLoot

Port From (https://github.com/TrinityCore/TrinityCore/commit/8c20f620d7b070b13b40803095e5fa97f9349512)
This commit is contained in:
hondacrx
2022-09-18 22:13:40 -04:00
parent aa88c5bb2f
commit 6c7991c28f
6 changed files with 185 additions and 230 deletions
+49 -19
View File
@@ -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
m_restockTime = 0;
m_lootState = LootState.Ready;
loot = null;
m_personalLoot.Clear();
AddToObjectUpdateIfNeeded();
break;
default:
@@ -699,11 +701,16 @@ namespace Game.Entities
case GameObjectTypes.Chest:
loot?.Update();
foreach (var (_, loot) in m_personalLoot)
loot.Update();
// Non-consumable chest was partially looted and restock time passed, restock all loot now
if (GetGoInfo().Chest.consumable == 0 && GameTime.GetGameTime() >= m_restockTime)
if (GetGoInfo().Chest.consumable == 0 && GetGoInfo().Chest.chestRestockTime != 0 && GameTime.GetGameTime() >= m_restockTime)
{
m_restockTime = 0;
m_lootState = LootState.Ready;
loot = null;
m_personalLoot.Clear();
AddToObjectUpdateIfNeeded();
}
break;
@@ -789,6 +796,7 @@ namespace Game.Entities
}
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
bool isSummonedAndExpired = (GetOwner() != null || GetSpellId() != 0) && m_respawnTime == 0;
@@ -940,44 +948,48 @@ namespace Game.Entities
SendMessageToSet(packet, true);
}
public void GetFishLoot(Loot fishloot, Player loot_owner)
public Loot GetFishLoot(Player lootOwner)
{
fishloot.Clear();
uint zone, subzone;
uint defaultzone = 1;
GetZoneAndAreaId(out zone, out subzone);
Loot fishLoot = new(GetMap(), GetGUID(), LootType.Fishing, null);
// if subzone loot exist use it
fishloot.FillLoot(subzone, LootStorage.Fishing, loot_owner, true, true);
if (fishloot.Empty())
fishLoot.FillLoot(subzone, LootStorage.Fishing, lootOwner, true, true);
if (fishLoot.Empty())
{
//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.
if (fishloot.Empty())
fishloot.FillLoot(defaultzone, LootStorage.Fishing, loot_owner, true, true);
if (fishLoot.Empty())
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 defaultzone = 1;
GetZoneAndAreaId(out zone, out subzone);
Loot fishLoot = new(GetMap(), GetGUID(), LootType.FishingJunk, null);
// if subzone loot exist use it
fishloot.FillLoot(subzone, LootStorage.Fishing, loot_owner, true, true, LootModes.JunkFish);
if (fishloot.Empty()) //use this becase if zone or subzone has normal mask drop, then fishloot.FillLoot return true.
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.
{
//use zone loot
fishloot.FillLoot(zone, LootStorage.Fishing, loot_owner, true, true, LootModes.JunkFish);
if (fishloot.Empty())
fishLoot.FillLoot(zone, LootStorage.Fishing, lootOwner, true, true, LootModes.JunkFish);
if (fishLoot.Empty())
//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()
@@ -1886,10 +1898,16 @@ namespace Game.Entities
SetLootState(LootState.JustDeactivated);
}
else
{
loot = GetFishLoot(player);
player.SendLoot(GetGUID(), LootType.Fishing);
}
}
else// If fishing skill is too low, send junk loot.
{
loot = GetFishLootJunk(player);
player.SendLoot(GetGUID(), LootType.FishingJunk);
}
break;
}
case LootState.JustDeactivated: // nothing to do, will be deleted at next update
@@ -2099,6 +2117,10 @@ namespace Game.Entities
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.UpdateCriteria(CriteriaType.CatchFishInFishingHole, GetGoInfo().entry);
return;
@@ -2814,7 +2836,16 @@ namespace Game.Entities
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 GameObject GetLinkedTrap()
{
return ObjectAccessor.GetGameObject(this, m_linkedTrap);
@@ -3298,8 +3329,6 @@ namespace Game.Entities
bool HasLootRecipient() { return !m_lootRecipient.IsEmpty() || !m_lootRecipientGroup.IsEmpty(); }
public override Loot GetLootForPlayer(Player player) { return loot; }
public override uint GetLevelForTarget(WorldObject target)
{
Unit owner = GetOwner();
@@ -3425,6 +3454,7 @@ namespace Game.Entities
List<ObjectGuid> m_SkillupList = new();
public Loot loot;
Dictionary<ObjectGuid, Loot> m_personalLoot = new();
public GameObjectModel m_model;
+3 -182
View File
@@ -6109,95 +6109,13 @@ namespace Game.Entities
if (guid.IsGameObject())
{
GameObject go = GetMap().GetGameObject(guid);
bool shouldLootRelease(GameObject go, LootType lootType)
{
// 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))
if (go == null)
{
SendLootRelease(guid);
return;
}
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())
{
@@ -6210,44 +6128,11 @@ namespace Game.Entities
}
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
{
Corpse bones = ObjectAccessor.GetCorpse(this, guid);
if (bones == null || !(loot_type == LootType.Corpse || loot_type == LootType.Insignia) || bones.GetCorpseType() != CorpseType.Bones)
if (bones == null)
{
SendLootRelease(guid);
return;
@@ -6260,77 +6145,13 @@ namespace Game.Entities
Creature creature = GetMap().GetCreature(guid);
// 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)))
{
SendLootRelease(guid);
return;
}
if (loot_type == LootType.Pickpocketing && IsFriendlyTo(creature))
if (creature == null)
{
SendLootRelease(guid);
return;
}
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);
}
}
}