fix: Fixes mount stamina, adds Pub46+ Ethereal stamina (#1716)
### Summary * Makes mount stamina significantly faster. * Reduces absolute reset from 24 hours to 4 hours. * Adds Pub 46+ Ethereal stamina where the stamina is global to all mounts for the player * Removes serialization of stamina entirely because it's ok if it gets reset during shard restarts.
This commit is contained in:
parent
c63bf84ad4
commit
90fa2e09de
5 changed files with 160 additions and 176 deletions
|
|
@ -15,14 +15,15 @@ public enum DFAlgorithm
|
||||||
PainSpike
|
PainSpike
|
||||||
}
|
}
|
||||||
|
|
||||||
public class StaminaSystem : GenericPersistence
|
public static class StaminaSystem
|
||||||
{
|
{
|
||||||
private static readonly ILogger logger = LogFactory.GetLogger(typeof(StaminaSystem));
|
private static readonly ILogger logger = LogFactory.GetLogger(typeof(StaminaSystem));
|
||||||
|
|
||||||
private static TimeSpan ResetDuration = TimeSpan.FromHours(24);
|
private static readonly TimeSpan ResetDuration = TimeSpan.FromHours(4);
|
||||||
|
|
||||||
private static readonly Dictionary<IHasSteps, StepsTaken> _stepsTaken = new();
|
private static readonly Dictionary<PlayerMobile, IHasSteps> _etherealMountStepCounters = [];
|
||||||
private static readonly OrderedHashSet<IHasSteps> _resetHash = new();
|
private static readonly Dictionary<IHasSteps, StepsTaken> _stepsTaken = [];
|
||||||
|
private static readonly HashSet<IHasSteps> _resetHash = [];
|
||||||
|
|
||||||
// TODO: This exploits single thread processing and is not thread safe!
|
// TODO: This exploits single thread processing and is not thread safe!
|
||||||
public static DFAlgorithm DFA { get; set; }
|
public static DFAlgorithm DFA { get; set; }
|
||||||
|
|
@ -35,6 +36,9 @@ public class StaminaSystem : GenericPersistence
|
||||||
public static bool EnableMountStamina { get; set; }
|
public static bool EnableMountStamina { get; set; }
|
||||||
public static bool UseMountStaminaOnlyWhenOverloaded { get; set; }
|
public static bool UseMountStaminaOnlyWhenOverloaded { get; set; }
|
||||||
|
|
||||||
|
// Pub 46 (UOML+)
|
||||||
|
public static bool GlobalEtherealMountStamina { get; set; }
|
||||||
|
|
||||||
public static void Configure()
|
public static void Configure()
|
||||||
{
|
{
|
||||||
CannotMoveWhenFatigued = ServerConfiguration.GetOrUpdateSetting("stamina.cannotMoveWhenFatigued", !Core.AOS);
|
CannotMoveWhenFatigued = ServerConfiguration.GetOrUpdateSetting("stamina.cannotMoveWhenFatigued", !Core.AOS);
|
||||||
|
|
@ -44,56 +48,7 @@ public class StaminaSystem : GenericPersistence
|
||||||
AdditionalLossWhenBelow = ServerConfiguration.GetOrUpdateSetting("stamina.additionalLossWhenBelow", 0.10);
|
AdditionalLossWhenBelow = ServerConfiguration.GetOrUpdateSetting("stamina.additionalLossWhenBelow", 0.10);
|
||||||
EnableMountStamina = ServerConfiguration.GetOrUpdateSetting("stamina.enableMountStamina", true);
|
EnableMountStamina = ServerConfiguration.GetOrUpdateSetting("stamina.enableMountStamina", true);
|
||||||
UseMountStaminaOnlyWhenOverloaded = ServerConfiguration.GetSetting("stamina.useMountStaminaOnlyWhenOverloaded", Core.SA);
|
UseMountStaminaOnlyWhenOverloaded = ServerConfiguration.GetSetting("stamina.useMountStaminaOnlyWhenOverloaded", Core.SA);
|
||||||
}
|
GlobalEtherealMountStamina = ServerConfiguration.GetSetting("stamina.globalEtherealMountStamina", Core.ML);
|
||||||
|
|
||||||
public StaminaSystem() : base("StaminaSystem", 10)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Serialize(IGenericWriter writer)
|
|
||||||
{
|
|
||||||
writer.WriteEncodedInt(0); // version
|
|
||||||
|
|
||||||
writer.WriteEncodedInt(_stepsTaken.Count);
|
|
||||||
foreach (var (m, stepsTaken) in _stepsTaken)
|
|
||||||
{
|
|
||||||
writer.Write(m as ISerializable); // To serialize all IHasSteps must be an ISerializable
|
|
||||||
stepsTaken.Serialize(writer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Deserialize(IGenericReader reader)
|
|
||||||
{
|
|
||||||
var version = reader.ReadEncodedInt();
|
|
||||||
|
|
||||||
var count = reader.ReadEncodedInt();
|
|
||||||
_stepsTaken.EnsureCapacity(count);
|
|
||||||
|
|
||||||
var now = Core.Now;
|
|
||||||
|
|
||||||
for (var i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
var m = reader.ReadEntity<ISerializable>() as IHasSteps;
|
|
||||||
var stepsTaken = new StepsTaken();
|
|
||||||
stepsTaken.Deserialize(reader);
|
|
||||||
|
|
||||||
if (m == null)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
RegenSteps(m, ref stepsTaken, false);
|
|
||||||
|
|
||||||
if (stepsTaken.Steps > 0)
|
|
||||||
{
|
|
||||||
_stepsTaken.Add(m, stepsTaken);
|
|
||||||
|
|
||||||
if (m is IMount && now < stepsTaken.IdleStartTime + ResetDuration)
|
|
||||||
{
|
|
||||||
_resetHash.Add(m);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Initialize()
|
public static void Initialize()
|
||||||
|
|
@ -107,14 +62,12 @@ public class StaminaSystem : GenericPersistence
|
||||||
using var queue = PooledRefQueue<IHasSteps>.Create();
|
using var queue = PooledRefQueue<IHasSteps>.Create();
|
||||||
foreach (var m in _stepsTaken.Keys)
|
foreach (var m in _stepsTaken.Keys)
|
||||||
{
|
{
|
||||||
ref var stepsTaken = ref CollectionsMarshal.GetValueRefOrNullRef(_stepsTaken, m);
|
// We cannot remove since we are iterating.
|
||||||
if (!Unsafe.IsNullRef(ref stepsTaken))
|
ref var stepsTaken = ref RegenSteps(m, out var exists, removeOnInvalidation: false);
|
||||||
|
|
||||||
|
if (exists && stepsTaken.Steps <= 0)
|
||||||
{
|
{
|
||||||
RegenSteps(m, ref stepsTaken, false);
|
queue.Enqueue(m);
|
||||||
if (stepsTaken.Steps <= 0)
|
|
||||||
{
|
|
||||||
queue.Enqueue(m);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -134,7 +87,7 @@ public class StaminaSystem : GenericPersistence
|
||||||
if (EnableMountStamina)
|
if (EnableMountStamina)
|
||||||
{
|
{
|
||||||
// Start idle for mount
|
// Start idle for mount
|
||||||
ref var stepsTaken = ref GetMountStepsTaken(m.Mount, out var exists);
|
ref var stepsTaken = ref GetStepsTaken(m.Mount, out var exists);
|
||||||
if (exists)
|
if (exists)
|
||||||
{
|
{
|
||||||
if (stepsTaken.Steps <= 0 || Core.Now >= stepsTaken.IdleStartTime + ResetDuration)
|
if (stepsTaken.Steps <= 0 || Core.Now >= stepsTaken.IdleStartTime + ResetDuration)
|
||||||
|
|
@ -152,8 +105,8 @@ public class StaminaSystem : GenericPersistence
|
||||||
|
|
||||||
if (m is PlayerMobile pm)
|
if (m is PlayerMobile pm)
|
||||||
{
|
{
|
||||||
ref var stepsTaken = ref CollectionsMarshal.GetValueRefOrNullRef(_stepsTaken, pm);
|
ref var stepsTaken = ref RegenSteps(pm, out var exists);
|
||||||
if (!Unsafe.IsNullRef(ref stepsTaken) && RegenSteps(pm, ref stepsTaken))
|
if (exists)
|
||||||
{
|
{
|
||||||
stepsTaken.IdleStartTime = Core.Now;
|
stepsTaken.IdleStartTime = Core.Now;
|
||||||
}
|
}
|
||||||
|
|
@ -170,45 +123,59 @@ public class StaminaSystem : GenericPersistence
|
||||||
if (EnableMountStamina)
|
if (EnableMountStamina)
|
||||||
{
|
{
|
||||||
// Regain mount idle time
|
// Regain mount idle time
|
||||||
ref var stepsTaken = ref GetMountStepsTaken(m.Mount, out var exists);
|
ref var stepsTaken = ref RegenSteps(m.Mount, out var exists);
|
||||||
if (exists)
|
if (exists)
|
||||||
{
|
{
|
||||||
if (RegenSteps(m.Mount, ref stepsTaken))
|
stepsTaken.IdleStartTime = Core.Now;
|
||||||
{
|
_resetHash.Add(m.Mount);
|
||||||
stepsTaken.IdleStartTime = Core.Now;
|
|
||||||
_resetHash.Add(m.Mount);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m is PlayerMobile pm)
|
if (m is PlayerMobile pm)
|
||||||
{
|
{
|
||||||
ref var stepsTaken = ref CollectionsMarshal.GetValueRefOrNullRef(_stepsTaken, pm);
|
ref var stepsTaken = ref RegenSteps(pm, out var exists);
|
||||||
|
if (exists)
|
||||||
if (!Unsafe.IsNullRef(ref stepsTaken) && RegenSteps(pm, ref stepsTaken))
|
|
||||||
{
|
{
|
||||||
stepsTaken.IdleStartTime = Core.Now;
|
stepsTaken.IdleStartTime = Core.Now;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void RemoveEntry(IHasSteps m)
|
private static bool IsEthereal(IHasSteps m, out PlayerMobile pm)
|
||||||
{
|
{
|
||||||
if (m != null)
|
if (m is not EtherealMount and not VirtualMountItem)
|
||||||
{
|
{
|
||||||
_stepsTaken.Remove(m);
|
pm = null;
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pm = ((IMount)m).Rider as PlayerMobile;
|
||||||
|
return pm != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void OnDismount(IHasSteps mount)
|
public static void RemoveEntry(IHasSteps m)
|
||||||
|
{
|
||||||
|
if (m == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GlobalEtherealMountStamina && IsEthereal(m, out var pm))
|
||||||
|
{
|
||||||
|
_etherealMountStepCounters.Remove(pm);
|
||||||
|
}
|
||||||
|
|
||||||
|
_stepsTaken.Remove(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void OnDismount(IMount mount)
|
||||||
{
|
{
|
||||||
if (!EnableMountStamina)
|
if (!EnableMountStamina)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ref var stepsTaken = ref GetMountStepsTaken(mount, out var exists);
|
if (RegenSteps(mount))
|
||||||
if (exists && RegenSteps(mount, ref stepsTaken))
|
|
||||||
{
|
{
|
||||||
_resetHash.Add(mount);
|
_resetHash.Add(mount);
|
||||||
return;
|
return;
|
||||||
|
|
@ -217,7 +184,7 @@ public class StaminaSystem : GenericPersistence
|
||||||
_resetHash.Remove(mount);
|
_resetHash.Remove(mount);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ref StepsTaken GetMountStepsTaken(IHasSteps m, out bool exists)
|
private static ref StepsTaken GetStepsTaken(IHasSteps m, out bool exists)
|
||||||
{
|
{
|
||||||
if (m == null)
|
if (m == null)
|
||||||
{
|
{
|
||||||
|
|
@ -225,6 +192,18 @@ public class StaminaSystem : GenericPersistence
|
||||||
return ref Unsafe.NullRef<StepsTaken>();
|
return ref Unsafe.NullRef<StepsTaken>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (GlobalEtherealMountStamina && IsEthereal(m, out var pm))
|
||||||
|
{
|
||||||
|
ref var stepsCounter = ref CollectionsMarshal.GetValueRefOrNullRef(_etherealMountStepCounters, pm);
|
||||||
|
if (Unsafe.IsNullRef(ref stepsCounter))
|
||||||
|
{
|
||||||
|
exists = false;
|
||||||
|
return ref Unsafe.NullRef<StepsTaken>();
|
||||||
|
}
|
||||||
|
|
||||||
|
m = stepsCounter;
|
||||||
|
}
|
||||||
|
|
||||||
ref var stepsTaken = ref CollectionsMarshal.GetValueRefOrNullRef(_stepsTaken, m);
|
ref var stepsTaken = ref CollectionsMarshal.GetValueRefOrNullRef(_stepsTaken, m);
|
||||||
exists = !Unsafe.IsNullRef(ref stepsTaken);
|
exists = !Unsafe.IsNullRef(ref stepsTaken);
|
||||||
return ref stepsTaken;
|
return ref stepsTaken;
|
||||||
|
|
@ -232,43 +211,65 @@ public class StaminaSystem : GenericPersistence
|
||||||
|
|
||||||
private static ref StepsTaken GetOrCreateStepsTaken(IHasSteps m, out bool created)
|
private static ref StepsTaken GetOrCreateStepsTaken(IHasSteps m, out bool created)
|
||||||
{
|
{
|
||||||
|
if (GlobalEtherealMountStamina && IsEthereal(m, out var pm))
|
||||||
|
{
|
||||||
|
ref var stepsCounter = ref CollectionsMarshal.GetValueRefOrAddDefault(_etherealMountStepCounters, pm, out var stepsCounterExists);
|
||||||
|
if (!stepsCounterExists)
|
||||||
|
{
|
||||||
|
stepsCounter = new EtherealMountStepCounter();
|
||||||
|
}
|
||||||
|
|
||||||
|
m = stepsCounter;
|
||||||
|
}
|
||||||
|
|
||||||
ref var stepsTaken = ref CollectionsMarshal.GetValueRefOrAddDefault(_stepsTaken, m, out var exists);
|
ref var stepsTaken = ref CollectionsMarshal.GetValueRefOrAddDefault(_stepsTaken, m, out var exists);
|
||||||
created = !exists;
|
created = !exists;
|
||||||
|
if (created)
|
||||||
|
{
|
||||||
|
stepsTaken.Entity = m;
|
||||||
|
}
|
||||||
|
|
||||||
return ref stepsTaken;
|
return ref stepsTaken;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void RegenSteps(IHasSteps m, int amount, bool removeOnInvalidation = true)
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static bool RegenSteps(IHasSteps m, int amount = 0, bool removeOnInvalidation = true)
|
||||||
{
|
{
|
||||||
ref var stepsTaken = ref GetMountStepsTaken(m, out var exists);
|
RegenSteps(m, out var exists, amount, removeOnInvalidation);
|
||||||
if (exists)
|
return exists;
|
||||||
{
|
|
||||||
RegenSteps(m, ref stepsTaken, removeOnInvalidation);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Triggered on logout, dismount, and world load
|
// Triggered on logout, dismount, and world load
|
||||||
private static bool RegenSteps(IHasSteps m, ref StepsTaken stepsTaken, bool removeOnInvalidation = true)
|
private static ref StepsTaken RegenSteps(IHasSteps m, out bool exists, int amount = 0, bool removeOnInvalidation = true)
|
||||||
{
|
{
|
||||||
var stepsGained = (int)((Core.Now - stepsTaken.IdleStartTime) / m.IdleTimePerStepsGain * m.StepsGainedPerIdleTime);
|
if (m == null)
|
||||||
return RegenSteps(m, stepsGained, ref stepsTaken, removeOnInvalidation);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool RegenSteps(IHasSteps m, int amount, ref StepsTaken stepsTaken, bool removeOnInvalidation = true)
|
|
||||||
{
|
|
||||||
if (m == null || Unsafe.IsNullRef(ref stepsTaken))
|
|
||||||
{
|
{
|
||||||
return false;
|
exists = false;
|
||||||
|
return ref Unsafe.NullRef<StepsTaken>();
|
||||||
}
|
}
|
||||||
|
|
||||||
stepsTaken.Steps -= amount;
|
ref var stepsTaken = ref GetStepsTaken(m, out exists);
|
||||||
|
if (!exists)
|
||||||
|
{
|
||||||
|
return ref Unsafe.NullRef<StepsTaken>();
|
||||||
|
}
|
||||||
|
|
||||||
|
exists = RegenSteps(ref stepsTaken, amount, removeOnInvalidation);
|
||||||
|
return ref stepsTaken;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool RegenSteps(ref StepsTaken stepsTaken, int amount = 0, bool removeOnInvalidation = true)
|
||||||
|
{
|
||||||
|
var entity = stepsTaken.Entity;
|
||||||
|
var stepsGained = (int)((Core.Now - stepsTaken.IdleStartTime) / entity.IdleTimePerStepsGain * entity.StepsGainedPerIdleTime);
|
||||||
|
|
||||||
|
stepsTaken.Steps -= stepsGained + amount;
|
||||||
|
|
||||||
if (stepsTaken.Steps <= 0)
|
if (stepsTaken.Steps <= 0)
|
||||||
{
|
{
|
||||||
if (removeOnInvalidation)
|
if (removeOnInvalidation)
|
||||||
{
|
{
|
||||||
_stepsTaken.Remove(m);
|
RemoveEntry(entity);
|
||||||
stepsTaken = ref Unsafe.NullRef<StepsTaken>();
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -330,7 +331,7 @@ public class StaminaSystem : GenericPersistence
|
||||||
|
|
||||||
from.Stam -= stamLoss;
|
from.Stam -= stamLoss;
|
||||||
|
|
||||||
if (from.Stam == 0)
|
if (from.Stam <= 0)
|
||||||
{
|
{
|
||||||
// You are too fatigued to move, because you are carrying too much weight!
|
// You are too fatigued to move, because you are carrying too much weight!
|
||||||
from.SendLocalizedMessage(500109);
|
from.SendLocalizedMessage(500109);
|
||||||
|
|
@ -339,24 +340,29 @@ public class StaminaSystem : GenericPersistence
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!running)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (AdditionalLossWhenBelow > 0 && from.Stam / Math.Max(from.StamMax, 1.0) < AdditionalLossWhenBelow)
|
if (AdditionalLossWhenBelow > 0 && from.Stam / Math.Max(from.StamMax, 1.0) < AdditionalLossWhenBelow)
|
||||||
{
|
{
|
||||||
--from.Stam;
|
--from.Stam;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (CannotMoveWhenFatigued && from.Stam == 0)
|
if (CannotMoveWhenFatigued && from.Stam <= 0)
|
||||||
{
|
{
|
||||||
from.SendLocalizedMessage(500110); // You are too fatigued to move.
|
from.SendLocalizedMessage(500110); // You are too fatigued to move.
|
||||||
e.Blocked = true;
|
e.Blocked = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (running && from is PlayerMobile pm)
|
if (from is PlayerMobile pm)
|
||||||
{
|
{
|
||||||
ref StepsTaken stepsTaken = ref GetOrCreateStepsTaken(pm, out var created);
|
ref StepsTaken stepsTaken = ref GetOrCreateStepsTaken(pm, out var created);
|
||||||
if (!created)
|
if (!created)
|
||||||
{
|
{
|
||||||
RegenSteps(pm, ref stepsTaken, false);
|
RegenSteps(ref stepsTaken, removeOnInvalidation: false);
|
||||||
}
|
}
|
||||||
|
|
||||||
var steps = ++stepsTaken.Steps;
|
var steps = ++stepsTaken.Steps;
|
||||||
|
|
@ -378,44 +384,25 @@ public class StaminaSystem : GenericPersistence
|
||||||
{
|
{
|
||||||
var from = e.Mobile;
|
var from = e.Mobile;
|
||||||
|
|
||||||
|
var maxSteps = mount.StepsMax;
|
||||||
var running = (e.Direction & Direction.Running) != 0;
|
var running = (e.Direction & Direction.Running) != 0;
|
||||||
|
|
||||||
var stamLoss = overweight > 0 ? GetStamLoss(from, overweight, running) : 0;
|
var stamLoss = overweight > 0 ? GetStamLoss(from, overweight, running) : 0;
|
||||||
|
|
||||||
|
if (stamLoss <= 0 && (!running || UseMountStaminaOnlyWhenOverloaded))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ref var stepsTaken = ref GetOrCreateStepsTaken(mount, out var created);
|
ref var stepsTaken = ref GetOrCreateStepsTaken(mount, out var created);
|
||||||
|
|
||||||
// Gain any idle steps
|
|
||||||
if (!created)
|
if (!created)
|
||||||
{
|
{
|
||||||
RegenSteps(mount, ref stepsTaken, false); // Don't delete the entry if it's reset
|
RegenSteps(ref stepsTaken, removeOnInvalidation: false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mount is Mobile m && AdditionalLossWhenBelow > 0 && m.Stam / Math.Max(m.StamMax, 1.0) < AdditionalLossWhenBelow)
|
stepsTaken.Steps += stamLoss + 1;
|
||||||
{
|
stepsTaken.IdleStartTime = Core.Now;
|
||||||
stamLoss++;
|
|
||||||
}
|
|
||||||
|
|
||||||
var maxSteps = mount.StepsMax;
|
|
||||||
|
|
||||||
if (stepsTaken.Steps <= maxSteps)
|
|
||||||
{
|
|
||||||
// Pre-SA mounts would lose stamina while running even when they were not overweight
|
|
||||||
if (running && !UseMountStaminaOnlyWhenOverloaded)
|
|
||||||
{
|
|
||||||
stamLoss++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stamLoss > 0)
|
|
||||||
{
|
|
||||||
stepsTaken.Steps += stamLoss;
|
|
||||||
stepsTaken.IdleStartTime = Core.Now;
|
|
||||||
|
|
||||||
// This only executes when mounted, so we have the player say it since the actual mount is internalized
|
|
||||||
if ((mount as BaseCreature)?.Debug == true && stepsTaken.Steps % 20 == 0)
|
|
||||||
{
|
|
||||||
from.PublicOverheadMessage(MessageType.Regular, 41, false, $"Steps {stepsTaken.Steps}/{mount.StepsMax}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stepsTaken.Steps > maxSteps)
|
if (stepsTaken.Steps > maxSteps)
|
||||||
{
|
{
|
||||||
|
|
@ -454,29 +441,14 @@ public class StaminaSystem : GenericPersistence
|
||||||
|
|
||||||
private struct StepsTaken
|
private struct StepsTaken
|
||||||
{
|
{
|
||||||
|
public IHasSteps Entity;
|
||||||
public int Steps;
|
public int Steps;
|
||||||
public DateTime IdleStartTime;
|
public DateTime IdleStartTime;
|
||||||
|
|
||||||
public void Serialize(IGenericWriter writer)
|
|
||||||
{
|
|
||||||
writer.WriteEncodedInt(0); // version
|
|
||||||
|
|
||||||
writer.WriteEncodedInt(Steps);
|
|
||||||
writer.WriteDeltaTime(IdleStartTime);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Deserialize(IGenericReader reader)
|
|
||||||
{
|
|
||||||
reader.ReadEncodedInt(); // version
|
|
||||||
|
|
||||||
Steps = reader.ReadEncodedInt();
|
|
||||||
IdleStartTime = reader.ReadDeltaTime();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ResetTimer : Timer
|
private class ResetTimer : Timer
|
||||||
{
|
{
|
||||||
private static TimeSpan CheckDuration = TimeSpan.FromHours(1);
|
private static readonly TimeSpan _checkDuration = TimeSpan.FromHours(1);
|
||||||
|
|
||||||
private DateTime _nextCheck;
|
private DateTime _nextCheck;
|
||||||
|
|
||||||
|
|
@ -499,31 +471,43 @@ public class StaminaSystem : GenericPersistence
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
using var queue = PooledRefQueue<IHasSteps>.Create();
|
if (_resetHash.Count > 0)
|
||||||
|
|
||||||
ref StepsTaken stepsTaken = ref Unsafe.NullRef<StepsTaken>();
|
|
||||||
foreach (var m in _resetHash)
|
|
||||||
{
|
{
|
||||||
stepsTaken = ref GetMountStepsTaken(m, out var exists);
|
using var queue = PooledRefQueue<IHasSteps>.Create();
|
||||||
if (!exists || Core.Now >= stepsTaken.IdleStartTime + ResetDuration)
|
|
||||||
|
ref StepsTaken stepsTaken = ref Unsafe.NullRef<StepsTaken>();
|
||||||
|
foreach (var m in _resetHash)
|
||||||
{
|
{
|
||||||
queue.Enqueue(m);
|
stepsTaken = ref GetStepsTaken(m, out var exists);
|
||||||
|
if (!exists || Core.Now >= stepsTaken.IdleStartTime + ResetDuration)
|
||||||
|
{
|
||||||
|
queue.Enqueue(m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_resetHash.Count == queue.Count)
|
||||||
|
{
|
||||||
|
_resetHash.Clear();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
while (queue.Count > 0)
|
||||||
|
{
|
||||||
|
_resetHash.Remove(queue.Dequeue());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_resetHash.Count == queue.Count)
|
_nextCheck = Core.Now + _checkDuration;
|
||||||
{
|
|
||||||
_resetHash.Clear();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
while (queue.Count > 0)
|
|
||||||
{
|
|
||||||
_resetHash.Remove(queue.Dequeue());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_nextCheck = Core.Now + CheckDuration;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Placeholder for a special case where Ethereal mount steps are global to the player
|
||||||
|
private class EtherealMountStepCounter : IHasSteps
|
||||||
|
{
|
||||||
|
// The properties are not actually used
|
||||||
|
public int StepsMax => 3840;
|
||||||
|
public int StepsGainedPerIdleTime => 1;
|
||||||
|
public TimeSpan IdleTimePerStepsGain => TimeSpan.FromSeconds(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -37,10 +37,10 @@ public abstract partial class BaseMount : BaseCreature, IMount
|
||||||
public virtual bool AllowMaleRider => true;
|
public virtual bool AllowMaleRider => true;
|
||||||
public virtual bool AllowFemaleRider => true;
|
public virtual bool AllowFemaleRider => true;
|
||||||
|
|
||||||
// Stamina System - 1 step per 10 seconds and 3840 steps max = 9.667 hours
|
// Stamina System - 1 step per 1 second and 3840 steps max = 64 minutes
|
||||||
public virtual int StepsMax => 3840;
|
public virtual int StepsMax => 3840;
|
||||||
public virtual int StepsGainedPerIdleTime => 1;
|
public virtual int StepsGainedPerIdleTime => 1;
|
||||||
public virtual TimeSpan IdleTimePerStepsGain => TimeSpan.FromSeconds(10);
|
public virtual TimeSpan IdleTimePerStepsGain => TimeSpan.FromSeconds(1);
|
||||||
|
|
||||||
[Hue]
|
[Hue]
|
||||||
[CommandProperty(AccessLevel.GameMaster)]
|
[CommandProperty(AccessLevel.GameMaster)]
|
||||||
|
|
|
||||||
|
|
@ -145,7 +145,7 @@ namespace Server.Mobiles
|
||||||
public virtual int StepsMax => 3840; // Should be same as horse
|
public virtual int StepsMax => 3840; // Should be same as horse
|
||||||
|
|
||||||
public virtual int StepsGainedPerIdleTime => 1;
|
public virtual int StepsGainedPerIdleTime => 1;
|
||||||
public virtual TimeSpan IdleTimePerStepsGain => TimeSpan.FromSeconds(10);
|
public virtual TimeSpan IdleTimePerStepsGain => TimeSpan.FromSeconds(1);
|
||||||
|
|
||||||
public void OnRiderDamaged(int amount, Mobile from, bool willKill)
|
public void OnRiderDamaged(int amount, Mobile from, bool willKill)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -4293,7 +4293,8 @@ namespace Server.Mobiles
|
||||||
if (stamGain > 0)
|
if (stamGain > 0)
|
||||||
{
|
{
|
||||||
Stam += stamGain;
|
Stam += stamGain;
|
||||||
// 64 food = 3,640 steps
|
|
||||||
|
// 61 food = 3,840 steps
|
||||||
StaminaSystem.RegenSteps(this as IHasSteps, stamGain * 4);
|
StaminaSystem.RegenSteps(this as IHasSteps, stamGain * 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,11 +30,10 @@ public partial class VirtualMountItem : Item, IMountItem, IMount
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int Steps { get; set; }
|
// If this is ever equipped by a player, steps are treated the same as Ethereal mounts
|
||||||
|
public int StepsMax => 3840;
|
||||||
public int StepsMax => 400;
|
|
||||||
public int StepsGainedPerIdleTime => 1;
|
public int StepsGainedPerIdleTime => 1;
|
||||||
public TimeSpan IdleTimePerStepsGain => TimeSpan.FromSeconds(10);
|
public TimeSpan IdleTimePerStepsGain => TimeSpan.FromSeconds(1);
|
||||||
|
|
||||||
public void OnRiderDamaged(int amount, Mobile from, bool willKill)
|
public void OnRiderDamaged(int amount, Mobile from, bool willKill)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue