ModernUO/Projects/UOContent/Mobiles/NPCSpeeds.cs
Kamron Batman 3332fabc39
fix: Reverts to RunUO speeds, fixes direction glitching, adds movement speed interpolation (#1695)
> [!IMPORTANT]  
> Please read through these changes, as they changed certain expectations for how the internal AI thinking/movement work.
> Note that some mobs still don't have smooth movement on ClassicUO due to how the client handles animations/movement.

### Summary
- **Important Change**: Mobs will now move at a more regular pace. If the OnThink results in a move, but the cooldown would otherwise prevent the move, then the movement is scheduled on another timer.
- Added a 400ms delay to mobs turning to face a player to attack in order to avoid glitching between moving and turning.
- Reverted AI thinking speeds back to RunUO specific speeds.
- Reverted the AI thinking to moving conversion delays back to RunUO.
- For thinking speeds that are not exactly the predefined speeds from RunUO, there is a new calculation to determine the correct conversion to movement speed. This stops speeds like `0.35` from being faster than `0.3`

> [!NOTE]  
> **Developer Note**
> BaseAI.CheckMove() no longer contains the check for whether or not a mob is on movement cooldown. This function can now be overwritten without causing issues to figuring out that cooldown.
2024-03-18 14:13:41 -07:00

109 lines
3.4 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Text.Json.Serialization;
using Server.Json;
namespace Server.Mobiles;
public enum SpeedLevel
{
None,
VerySlow,
Slow,
Medium,
Fast,
VeryFast
}
public static class NPCSpeeds
{
private const string _tablePath = "Data/npc-speeds.json";
private static readonly Dictionary<Type, SpeedClassEntry> _speedsByType = new();
private static readonly Dictionary<SpeedLevel, SpeedClassEntry> _speedsByLevel = new();
// Enabled for pets on HS+
public static bool ScaleSpeedByDex { get; private set; }
public static double MinDelay { get; private set; }
public static double MaxDelay { get; private set; }
public static int MinDex { get; private set; }
public static int MaxDex { get; private set; }
// Time period to lock NPCs into idling
public static int MinIdleSeconds { get; private set; }
public static int MaxIdleSeconds { get; private set; }
public static void GetSpeeds(BaseCreature bc, out double activeSpeed, out double passiveSpeed)
{
// Used for scaling pet's speed by dex in HS+
if (bc.ScaleSpeedByDex)
{
var maxDex = MaxDex;
double min = MinDelay;
double max = MaxDelay;
var dex = Math.Clamp(bc.Dex, MinDex, maxDex);
activeSpeed = Math.Max(max - (max - min) * ((double)dex / maxDex), min);
passiveSpeed = activeSpeed * 2;
return;
}
if ((bc.SpeedClass == SpeedLevel.None || !_speedsByLevel.TryGetValue(bc.SpeedClass, out var sp)) &&
!_speedsByType.TryGetValue(bc.GetType(), out sp))
{
sp = _speedsByLevel[SpeedLevel.Medium];
}
activeSpeed = sp.ActiveSpeed;
passiveSpeed = sp.PassiveSpeed;
}
public static void RegisterSpeed(SpeedClassEntry entry)
{
_speedsByLevel[entry.Level] = entry;
foreach (var type in entry.Types)
{
_speedsByType[type] = entry;
}
}
public static void Configure()
{
ScaleSpeedByDex = ServerConfiguration.GetSetting("movement.delay.scaleSpeedByDex", Core.HS);
MinDelay = ServerConfiguration.GetSetting("movement.delay.npcMinDelay", 0.1);
MaxDelay = ServerConfiguration.GetSetting("movement.delay.npcMaxDelay", 0.4);
MaxDex = ServerConfiguration.GetSetting("movement.delay.npcMinDex", 50);
MaxDex = ServerConfiguration.GetSetting("movement.delay.npcMaxDex", 200);
MinIdleSeconds = ServerConfiguration.GetSetting("movement.delay.npcMinIdle", 15);
MaxIdleSeconds = ServerConfiguration.GetSetting("movement.delay.npcMaxIdle", 25);
var path = Path.Combine(Core.BaseDirectory, _tablePath);
if (!File.Exists(path))
{
return;
}
var speeds = JsonConfig.Deserialize<SpeedClassEntry[]>(path);
for (var i = 0; i < speeds.Length; i++)
{
RegisterSpeed(speeds[i]);
}
}
public record SpeedClassEntry
{
[JsonPropertyName("level")]
public SpeedLevel Level { get; init; }
[JsonPropertyName("active")]
public double ActiveSpeed { get; init; }
[JsonPropertyName("passive")]
public double PassiveSpeed { get; init; }
[JsonPropertyName("types")]
public HashSet<Type> Types { get; init; }
}
}