Skip to content

Commit

Permalink
feat: Adds Stamina System to overhaul overweight. (#1465)
Browse files Browse the repository at this point in the history
## Overhaul to Stamina (Overweight) System

### Added configurations

```json
{
  "settings": {
    "stamina.enableMountStamina": "True",
    "stamina.cannotMoveWhenFatigued": "True",
    "stamina.stonesPerOverweightLoss": "25",
    "stamina.stonesOverweightAllowance": "4",
    "stamina.baseOverweightLoss": "5",
    "stamina.additionalLossWhenBelow": "0.1",
    "stamina.mountLastMoveStepsReset": "01:00:00:00",
    "stamina.enableMountStamina": "True",
    "stamina.useMountStaminaOnlyWhenOverloaded": "False",
  },
}
```
- `stamina.cannotMoveWhenFatigued` - By default, Pre-AOS expansions will outright block a player if they are fatigued. A player is fatigued when they run out of stamina by any mechanism.
- `stamina.stonesPerOverweightLoss` - The amount of stamina lost for every X stones above overweight. Example, if a person is overweight 28 stones overweight, then there there is 1 additional stamina. 28 / 25 = 1 (no decimals, no rounding)
- `stamina.stonesOverweightAllowance` - The number of stones allowed before overweight penalties take affect. This _does not_ substract from the overweight stones calculation.
- `stamina.baseOverweightLoss` - The base amount of stamina loss for being overweight. While running, the final amount is multiplied by 2. If mount stamina is turned off, then the final amount is divided by 3 while mounted. 

### Mount stamina

Mounts have a new property `StepsMax` to determine the maximum steps they can take before being fatigued. To regain steps, the player _must stay on the mount and not move_ (per OSI). The gain rates are configurable. If a mount is dismounted, or the player logs off, the mount is considered inactive and mounts regain all of their steps after _24 hours_.

### Changes to player stamina

Players now have a proper inactivity time reset for their steps. If a player is idle for 16 seconds (including logging off, or the server being offline), then the steps counter is rest.

### Developer Notes

- `StepsTaken` - This field has been removed. It was not serialized and could not be used reliably. If a developer was using it, then the recommendation is to build a mechanism to track total steps another way.
- `IHasStamina` - This new interface was added and currently `IMount` and `PlayerMobile` are valid types.

Important: Entities that are sent to the StaminaSystem for tracking (mounts, players, or something else), must be an `ISerializable` to be serialized properly. If they are not, then the serialization system will record a null, and nothing will be deserialized upon world load. No errors will be given.
  • Loading branch information
kamronbatman authored Sep 17, 2023
1 parent 25eaff0 commit 0f2870e
Show file tree
Hide file tree
Showing 38 changed files with 947 additions and 457 deletions.
12 changes: 12 additions & 0 deletions Projects/Server/Mobiles/IHasSteps.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System;

namespace Server.Mobiles;

public interface IHasSteps
{
int StepsMax { get; }

int StepsGainedPerIdleTime { get; }

TimeSpan IdleTimePerStepsGain { get; }
}
4 changes: 3 additions & 1 deletion Projects/Server/Mobiles/IMount.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
*************************************************************************/

using System;

namespace Server.Mobiles;

public interface IMount
public interface IMount : IHasSteps
{
Mobile Rider { get; set; }
void OnRiderDamaged(int amount, Mobile from, bool willKill);
Expand Down
122 changes: 82 additions & 40 deletions Projects/Server/Mobiles/Mobile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4081,33 +4081,49 @@ private bool CanMove(Direction d, Point3D oldLocation, ref Point3D newLocation)
switch (d & Direction.Mask)
{
case Direction.North:
--y;
break;
{
--y;
break;
}
case Direction.Right:
++x;
--y;
break;
{
++x;
--y;
break;
}
case Direction.East:
++x;
break;
{
++x;
break;
}
case Direction.Down:
++x;
++y;
break;
{
++x;
++y;
break;
}
case Direction.South:
++y;
break;
{
++y;
break;
}
case Direction.Left:
--x;
++y;
break;
{
--x;
++y;
break;
}
case Direction.West:
--x;
break;
{
--x;
break;
}
case Direction.Up:
--x;
--y;
break;
{
--x;
--y;
break;
}
}

newLocation.m_X = x;
Expand Down Expand Up @@ -5428,38 +5444,64 @@ public virtual void DoSpeech(string text, int[] keywords, MessageType type, int
switch (type)
{
case MessageType.Regular:
SpeechHue = hue;
break;
{
SpeechHue = hue;
break;
}
case MessageType.Emote:
EmoteHue = hue;
break;
{
EmoteHue = hue;
break;
}
case MessageType.Whisper:
WhisperHue = hue;
range = 1;
break;
{
WhisperHue = hue;
range = 1;
break;
}
case MessageType.Yell:
YellHue = hue;
range = 18;
break;
{
YellHue = hue;
range = 18;
break;
}
case MessageType.System:
break;
{
break;
}
case MessageType.Label:
break;
{
break;
}
case MessageType.Focus:
break;
{
break;
}
case MessageType.Spell:
break;
{
break;
}
case MessageType.Guild:
break;
{
break;
}
case MessageType.Alliance:
break;
{
break;
}
case MessageType.Command:
break;
{
break;
}
case MessageType.Encoded:
break;
{
break;
}
default:
type = MessageType.Regular;
break;
{
type = MessageType.Regular;
break;
}
}

var regArgs = new SpeechEventArgs(this, text, type, hue, keywords);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ public UnholySteed(Serial serial) : base(serial)
{
}

public override int StepsMax => 6400;
public override string CorpseName => "an unholy corpse";
public override bool IsDispellable => false;
public override bool IsBondable => false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ public HolySteed(Serial serial) : base(serial)
{
}

public override int StepsMax => 6400;
public override string CorpseName => "a holy corpse";
public override bool IsDispellable => false;
public override bool IsBondable => false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ public FactionWarHorse(Serial serial) : base(serial)
{
}

public override int StepsMax => 6400;
public override string CorpseName => "a war horse corpse";

[CommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -468,61 +468,4 @@ public override void Deserialize(IGenericReader reader)
Timer.StartTimer(Register);
}
}

public class VirtualMount : IMount
{
private readonly VirtualMountItem m_Item;

public VirtualMount(VirtualMountItem item) => m_Item = item;

Mobile IMount.Rider
{
get => m_Item.Rider;
set { }
}

public virtual void OnRiderDamaged(int amount, Mobile from, bool willKill)
{
}
}

[SerializationGenerator(0, false)]
public partial class VirtualMountItem : Item, IMountItem
{
private VirtualMount _mount;

[SerializableField(0)]
private Mobile _rider;

public VirtualMountItem(Mobile mob) : base(0x3EA0)
{
Layer = Layer.Mount;

Rider = mob;
_mount = new VirtualMount(this);
}

public IMount Mount => _mount;

[AfterDeserialization]
private void AfterDeserialization()
{
if (_rider?.Deleted != false)
{
Delete();
}
else
{
_mount = new VirtualMount(this);
}
}

public override DeathMoveResult OnParentDeath(Mobile parent)
{
_mount = null;
Delete();

return DeathMoveResult.RemainEquipped;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Server.Items;
using Server.Mobiles;

namespace Server.Factions
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Server.Items;
using Server.Mobiles;

namespace Server.Factions
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@ public static class PlayerMurderSystem
// Only the players that are online
private static readonly HashSet<MurderContext> _contextTerms = new(MurderContext.EqualityComparer.Default);

private static readonly Timer _murdererTimer = new MurdererTimer();

private static TimeSpan _shortTermMurderDuration;

private static TimeSpan _longTermMurderDuration;
Expand All @@ -41,8 +39,6 @@ public static void Initialize()
EventSink.Disconnected += OnDisconnected;
EventSink.Login += OnLogin;
EventSink.PlayerDeleted += OnPlayerDeleted;

_murdererTimer.Start();
}

private static void OnPlayerDeleted(Mobile m)
Expand Down Expand Up @@ -182,6 +178,11 @@ public MurdererTimer() : base(TimeSpan.FromMinutes(5.0), TimeSpan.FromMinutes(5.
{
}

public static void Initialize()
{
new MurdererTimer().Start();
}

protected override void OnTick()
{
if (_contextTerms.Count == 0)
Expand All @@ -208,5 +209,10 @@ protected override void OnTick()
}
}
}

~MurdererTimer()
{
PlayerMurderSystem.logger.Error($"{nameof(MurdererTimer)} is no longer running!");
}
}
}
17 changes: 13 additions & 4 deletions Projects/UOContent/Engines/Virtues/VirtueSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Runtime.InteropServices;
using Server.Collections;
using Server.Logging;
using Server.Mobiles;

namespace Server.Engines.Virtues;
Expand Down Expand Up @@ -29,9 +30,9 @@ public enum VirtueName

public static class VirtueSystem
{
private static readonly Dictionary<PlayerMobile, VirtueContext> _playerVirtues = new();
private static readonly ILogger logger = LogFactory.GetLogger(typeof(VirtueSystem));

private static readonly Timer _virtueTimer = new VirtueTimer();
private static readonly Dictionary<PlayerMobile, VirtueContext> _playerVirtues = new();

private static void FixVirtue(Mobile m, int[] virtueValues)
{
Expand Down Expand Up @@ -66,8 +67,6 @@ public static void Initialize()
FixVirtue(m, values);
}
}

_virtueTimer.Start();
}

private static void Serialize(IGenericWriter writer)
Expand Down Expand Up @@ -346,6 +345,11 @@ public VirtueTimer() : base(TimeSpan.FromMinutes(5.0), TimeSpan.FromMinutes(5.0)
{
}

public static void Initialize()
{
new VirtueTimer().Start();
}

protected override void OnTick()
{
if (_playerVirtues.Count == 0)
Expand All @@ -371,5 +375,10 @@ protected override void OnTick()
_playerVirtues.Remove((PlayerMobile)queue.Dequeue());
}
}

~VirtueTimer()
{
VirtueSystem.logger.Error($"{nameof(VirtueTimer)} is no longer running!");
}
}
}
Loading

0 comments on commit 0f2870e

Please sign in to comment.