refactor: collapse the move-speed properties into serialized fields
ActiveMoveSpeed/PassiveMoveSpeed become plain [SerializableField]s (not virtual): the properties now read the raw override (0 = inheriting) and CurrentMoveSpeed carries the inherit resolution - it was the only production reader of the resolving getters. The <=0 coercion moves to an allowFieldChange hook. Wire format unchanged (schema diff is empty). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
f4327e6a3a
commit
963b9b3b85
4 changed files with 51 additions and 47 deletions
|
|
@ -57,8 +57,9 @@ public class MoveSpeedTests : IDisposable
|
|||
{
|
||||
var bc = NewCreature();
|
||||
|
||||
Assert.Equal(0.3, bc.ActiveMoveSpeed);
|
||||
Assert.Equal(0.6, bc.PassiveMoveSpeed);
|
||||
// 0 = no override; the resolved pace comes from CurrentMoveSpeed.
|
||||
Assert.Equal(0, bc.ActiveMoveSpeed);
|
||||
Assert.Equal(0, bc.PassiveMoveSpeed);
|
||||
Assert.Equal(bc.CurrentSpeed, bc.CurrentMoveSpeed);
|
||||
}
|
||||
|
||||
|
|
@ -96,8 +97,8 @@ public class MoveSpeedTests : IDisposable
|
|||
|
||||
bc.SetSpeed(0.2, 0.4);
|
||||
|
||||
Assert.Equal(0.2, bc.ActiveMoveSpeed);
|
||||
Assert.Equal(0.4, bc.PassiveMoveSpeed);
|
||||
Assert.Equal(0, bc.ActiveMoveSpeed);
|
||||
Assert.Equal(0, bc.PassiveMoveSpeed);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -108,8 +109,10 @@ public class MoveSpeedTests : IDisposable
|
|||
|
||||
bc.ActiveMoveSpeed = 0;
|
||||
|
||||
Assert.Equal(0.3, bc.ActiveMoveSpeed); // inheriting again
|
||||
Assert.Equal(0, bc.ActiveMoveSpeed); // inheriting again
|
||||
Assert.Equal(0.9, bc.PassiveMoveSpeed); // other override untouched
|
||||
bc.SetCurrentSpeedToActive();
|
||||
Assert.Equal(0.3, bc.CurrentMoveSpeed); // resolves to the think clock
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -121,7 +124,7 @@ public class MoveSpeedTests : IDisposable
|
|||
bc.ScaleMoveSpeed(1.0 / 1.2);
|
||||
|
||||
Assert.Equal(0.5, bc.ActiveMoveSpeed);
|
||||
Assert.Equal(bc.PassiveSpeed, bc.PassiveMoveSpeed); // still inheriting, not 0 * scalar
|
||||
Assert.Equal(0, bc.PassiveMoveSpeed); // still inheriting, not 0 * scalar
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -185,8 +188,8 @@ public class MoveSpeedTests : IDisposable
|
|||
|
||||
bc.MigrateMoveSpeeds();
|
||||
|
||||
Assert.Equal(0.35, bc.ActiveMoveSpeed);
|
||||
Assert.Equal(0.6, bc.PassiveMoveSpeed);
|
||||
Assert.Equal(0, bc.ActiveMoveSpeed); // still inheriting the (tuned) think clock
|
||||
Assert.Equal(0, bc.PassiveMoveSpeed);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
|
@ -213,7 +216,7 @@ public class MoveSpeedTests : IDisposable
|
|||
|
||||
// The v22 tail is the last block; exact consumption catches any offset mistake.
|
||||
Assert.Equal(buffer.Length, reader.Position);
|
||||
Assert.Equal(overridden ? 0.45 : 0.3, copy.ActiveMoveSpeed);
|
||||
Assert.Equal(overridden ? 0.9 : 0.6, copy.PassiveMoveSpeed);
|
||||
Assert.Equal(overridden ? 0.45 : 0, copy.ActiveMoveSpeed);
|
||||
Assert.Equal(overridden ? 0.9 : 0, copy.PassiveMoveSpeed);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -269,7 +269,8 @@ public class BaseCreatureSerializationTests : IDisposable
|
|||
Assert.Equal(10, copy.FireResistSeed);
|
||||
Assert.Equal(0.3, copy.ActiveSpeed);
|
||||
// v22 wrote explicit zeros for the move overrides ("inherit"), so the resolved
|
||||
// pace falls back to the think clock through the resolving getters.
|
||||
Assert.Equal(0.3, copy.ActiveMoveSpeed);
|
||||
// pace falls back to the think clock.
|
||||
Assert.Equal(0, copy.ActiveMoveSpeed);
|
||||
Assert.Equal(0.6, copy.CurrentMoveSpeed); // passive mode, inheriting
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -363,11 +363,30 @@ namespace Server.Mobiles
|
|||
|
||||
private void OnCurrentSpeedChange(double oldValue, double newValue) => AIObject?.OnCurrentSpeedChanged();
|
||||
|
||||
// Movement clock (seconds per step); 0 = inherit the matching think value.
|
||||
// Serialized through the hand-written resolving properties (fields 10 and 11).
|
||||
/// <summary>
|
||||
/// Movement clock (seconds per step) while engaged; 0 = inherit
|
||||
/// <see cref="ActiveSpeed"/>. <see cref="CurrentMoveSpeed"/> resolves the pace.
|
||||
/// </summary>
|
||||
[SerializableField(10, allowFieldChange: nameof(CoerceMoveSpeed))]
|
||||
[SaveFlag(nameof(ShouldSerializeActiveMoveSpeed), nameof(ActiveMoveSpeedDefaultValue))]
|
||||
[SerializedCommandProperty(AccessLevel.GameMaster)]
|
||||
private double _activeMoveSpeed;
|
||||
|
||||
/// <summary>
|
||||
/// Movement clock (seconds per step) while idle; 0 = inherit
|
||||
/// <see cref="PassiveSpeed"/>. <see cref="CurrentMoveSpeed"/> resolves the pace.
|
||||
/// </summary>
|
||||
[SerializableField(11, allowFieldChange: nameof(CoerceMoveSpeed))]
|
||||
[SaveFlag(nameof(ShouldSerializePassiveMoveSpeed), nameof(PassiveMoveSpeedDefaultValue))]
|
||||
[SerializedCommandProperty(AccessLevel.GameMaster)]
|
||||
private double _passiveMoveSpeed;
|
||||
|
||||
private bool CoerceMoveSpeed(ref double value)
|
||||
{
|
||||
value = Math.Max(0, value); // anything non-positive means "inherit"
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool ShouldSerializeActiveMoveSpeed()
|
||||
{
|
||||
GetMoveSpeeds(out var activeMoveSpeed, out _);
|
||||
|
|
@ -1075,34 +1094,6 @@ namespace Server.Mobiles
|
|||
[CommandProperty(AccessLevel.GameMaster)]
|
||||
public virtual int ChaseLeashRange => RangePerception * 2;
|
||||
|
||||
/// <summary>Seconds per step while engaged. Inherits <see cref="ActiveSpeed"/>; set 0 to re-inherit.</summary>
|
||||
[SerializableProperty(10, useField: nameof(_activeMoveSpeed))]
|
||||
[SaveFlag(nameof(ShouldSerializeActiveMoveSpeed), nameof(ActiveMoveSpeedDefaultValue))]
|
||||
[CommandProperty(AccessLevel.GameMaster)]
|
||||
public virtual double ActiveMoveSpeed
|
||||
{
|
||||
get => _activeMoveSpeed > 0 ? _activeMoveSpeed : _activeSpeed;
|
||||
set
|
||||
{
|
||||
_activeMoveSpeed = value > 0 ? value : 0;
|
||||
this.MarkDirty();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Seconds per step while idle. Inherits <see cref="PassiveSpeed"/>; set 0 to re-inherit.</summary>
|
||||
[SerializableProperty(11, useField: nameof(_passiveMoveSpeed))]
|
||||
[SaveFlag(nameof(ShouldSerializePassiveMoveSpeed), nameof(PassiveMoveSpeedDefaultValue))]
|
||||
[CommandProperty(AccessLevel.GameMaster)]
|
||||
public virtual double PassiveMoveSpeed
|
||||
{
|
||||
get => _passiveMoveSpeed > 0 ? _passiveMoveSpeed : _passiveSpeed;
|
||||
set
|
||||
{
|
||||
_passiveMoveSpeed = value > 0 ? value : 0;
|
||||
this.MarkDirty();
|
||||
}
|
||||
}
|
||||
|
||||
// Herded creatures walk at a fixed standard pace regardless of their own speed
|
||||
// (RunUO's forced 0.3, without its TransformMoveDelay inflation to 0.6).
|
||||
private const double HerdingMoveSpeed = 0.3;
|
||||
|
|
@ -1129,9 +1120,17 @@ namespace Server.Mobiles
|
|||
return HerdingMoveSpeed;
|
||||
}
|
||||
|
||||
return _currentSpeed == _activeSpeed ? ActiveMoveSpeed
|
||||
: _currentSpeed == _passiveSpeed ? PassiveMoveSpeed
|
||||
: _currentSpeed;
|
||||
if (_currentSpeed == _activeSpeed)
|
||||
{
|
||||
return _activeMoveSpeed > 0 ? _activeMoveSpeed : _activeSpeed;
|
||||
}
|
||||
|
||||
if (_currentSpeed == _passiveSpeed)
|
||||
{
|
||||
return _passiveMoveSpeed > 0 ? _passiveMoveSpeed : _passiveSpeed;
|
||||
}
|
||||
|
||||
return _currentSpeed;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -262,8 +262,9 @@ All "speed" values are **delays in seconds** (smaller = faster). A creature runs
|
|||
(combat decisions, target acquisition, spell timing).
|
||||
- **Move clock** — `ActiveMoveSpeed`/`PassiveMoveSpeed`/`CurrentMoveSpeed`: seconds per
|
||||
step. Inherits the matching think value until overridden, so a creature configured with
|
||||
only think speeds behaves as one clock. Any value is legal — steps are scheduled
|
||||
independently of think ticks, so the two need not divide evenly.
|
||||
only think speeds behaves as one clock. The properties read the raw override (`0` =
|
||||
inheriting); `CurrentMoveSpeed` is the resolved pace. Any value is legal — steps are
|
||||
scheduled independently of think ticks, so the two need not divide evenly.
|
||||
|
||||
Speeds normally come from `Distribution/Data/npc-speeds.json` (via `SpeedClass` or type
|
||||
lists); `activeMove`/`passiveMove` are optional per bucket. Prefer data over code:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue