ace/Source/ACE.Server/WorldObjects/WorldObject_Decay.cs
Coldeve-ACE a31112ea51
Common Logging Changes (#4345)
* Update ShardDatabaseOfflineTools.cs

* Update Corpse.cs

* Update Creature_Death.cs

* Update Player_Death.cs

* Update Player_Inventory.cs

* Update WorldObject_Decay.cs

* Update HouseManager.cs

* Update Player_House.cs

* Update RecipeManager.cs

* Update Player_Allegiance.cs

* Update AuthenticationDatabase.cs

* Update ShardDatabase.cs

* Update WorldDatabase.cs

* Update Player_Magic.cs

* Update Player_Skills.cs

* Update Player_Attributes.cs

* Update Player_Combat.cs

* Update PhysicsObj.cs

* Update Player_Vitals.cs

* Update Player_Missile.cs

* Update Player_Melee.cs

* Update Monster_Melee.cs

* Update Session.cs
2025-08-11 22:38:12 -04:00

169 lines
6.8 KiB
C#

using System;
using System.Linq;
using ACE.Entity;
using ACE.Entity.Enum;
using ACE.Server.Entity.Actions;
using ACE.Server.Managers;
using ACE.Server.Network.GameMessages.Messages;
namespace ACE.Server.WorldObjects
{
partial class WorldObject
{
/// <summary>
/// The default number of seconds for a object on a landblock to disappear<para />
/// Current default is 5 minutes
/// </summary>
protected TimeSpan DefaultTimeToRot { get; set; } = TimeSpan.FromMinutes(5);
/// <summary>
/// A decayable object is one that, when it exists on a landblock, would decay (rot) over time.<para />
/// When it rots, it would be destroyed, and removed from the landblock.<para />
/// In most cases, these should be player dropped items or corpses. It can also be a missile or spell projectile.<para />
/// Items that have a TimeToRot value of -1 will return false.<para />
/// Generators and items still linked to a generator will return false.
/// </summary>
public bool IsDecayable()
{
if (!Guid.IsDynamic())
return false;
// Don't rot generators, and items that were generated by a generator
// If the item was generated by a generator and then picked up by a player, the wo.Generator property would be set to null.
if (IsGenerator || Generator != null)
return false;
// Don't rot items with lifespan, they'll handle their own version of decay.
if (Lifespan.HasValue)
return false;
if (TimeToRot.HasValue)
return TimeToRot != -1; // -1 = Never Rot
return true;
}
private bool decayCompleted;
public void Decay(TimeSpan elapsed)
{
// http://asheron.wikia.com/wiki/Item_Decay
if (decayCompleted)
return;
var previousTTR = TimeToRot;
if (!TimeToRot.HasValue)
{
TimeToRot = DefaultTimeToRot.TotalSeconds;
if (this is Corpse && Level.HasValue)
log.InfoFormat("[CORPSE] {0} (0x{1}).Decay: TimeToRot had no value, set to {2}", Name, Guid, TimeToRot);
return;
}
var corpse = this as Corpse;
if (corpse != null)
{
if (!corpse.InventoryLoaded)
return;
if (corpse.Inventory.Count == 0 && TimeToRot.Value > Corpse.EmptyDecayTime)
{
TimeToRot = Corpse.EmptyDecayTime;
if (Level.HasValue && PropertyManager.GetBool("corpse_decay_tick_logging").Item)
log.InfoFormat("[CORPSE] {0} (0x{1}).Decay({2}): InventoryLoaded = {3} | Inventory.Count = {4} | previous TimeToRot: {5} | current TimeToRot: {6}", corpse.Name, corpse.Guid, elapsed, corpse.InventoryLoaded, corpse.Inventory.Count, previousTTR, TimeToRot);
return;
}
}
if (TimeToRot > 0)
{
TimeToRot -= elapsed.TotalSeconds;
if (this is Corpse && Level.HasValue && PropertyManager.GetBool("corpse_decay_tick_logging").Item)
log.InfoFormat("[CORPSE] {0} (0x{1}).Decay({2}): previous TimeToRot: {3} | current TimeToRot: {4}", corpse.Name, corpse.Guid, elapsed, previousTTR, TimeToRot);
// Is there still time left?
if (TimeToRot > 0)
return;
TimeToRot = -2; // We force it to -2 to make sure it doesn't end up at 0 or -1. 0 indicates instant rot. -1 indicates no rot. 0 and -1 can be found in weenie defaults
if (this is Corpse && Level.HasValue && PropertyManager.GetBool("corpse_decay_tick_logging").Item)
log.InfoFormat("[CORPSE] {0} (0x{1}).Decay({2}): previous TimeToRot: {3} | current TimeToRot: {4}", corpse.Name, corpse.Guid, elapsed, previousTTR, TimeToRot);
}
if (this is Container container && container.IsOpen)
{
// If you wanted to add a grace period to the container to give Player B more time to open it after Player A closes it, it would go here.
return;
}
// Time to rot has elapsed, time to disappear...
decayCompleted = true;
// If this is a player corpse, puke out the corpses contents onto the landblock
if (corpse != null && !corpse.IsMonster)
{
var inventoryGUIDs = corpse.Inventory.Keys.ToList();
var pukedItems = "";
foreach (var guid in inventoryGUIDs)
{
if (corpse.TryRemoveFromInventory(guid, out var item))
{
item.Location = new Position(corpse.Location);
item.Location.PositionZ += 0.05f * (item.ObjScale ?? 1.0f);
item.Placement = ACE.Entity.Enum.Placement.Resting; // This is needed to make items lay flat on the ground.
CurrentLandblock.AddWorldObject(item);
item.SaveBiotaToDatabase();
pukedItems += $"{item.Name} (0x{item.Guid.Full.ToString("X8")}), ";
}
}
if (pukedItems.EndsWith(", "))
pukedItems = pukedItems.Substring(0, pukedItems.Length - 2);
log.Info($"[CORPSE] {corpse.Name} (0x{corpse.Guid}) at {corpse.Location.ToLOCString()} has decayed{((pukedItems == "") ? "" : $" and placed the following items on the landblock: {pukedItems}")}.");
}
if (corpse != null)
{
EnqueueBroadcast(new GameMessageScript(Guid, PlayScript.Destroy));
var actionChain = new ActionChain();
actionChain.AddDelaySeconds(1.0f);
actionChain.AddAction(this, () => Destroy());
actionChain.EnqueueChain();
}
else
Destroy();
}
public void DeleteObject(Container rootOwner = null)
{
if (Wielder != null)
{
if (Wielder is Player player)
player.TryDequipObjectWithNetworking(Guid, out _, Player.DequipObjectAction.ConsumeItem);
else if (Wielder is Creature creature)
creature.TryUnwieldObjectWithBroadcasting(Guid, out _, out _);
}
else if (rootOwner != null)
{
if (rootOwner is Player player)
player.TryRemoveFromInventoryWithNetworking(Guid, out _, Player.RemoveFromInventoryAction.ConsumeItem);
else
rootOwner.TryRemoveFromInventory(Guid);
}
Destroy();
}
}
}