fix: respect active region MountsAllowed for players (#2630)

A player can mount a horse or validate an ethereal mount inside a BaseRegion that overrides MountsAllowed to false because CheckMountAllowed never consults that property. This adds the missing check in the shared player permission path, using existing localized message 1042317.
This commit is contained in:
Robert Dickey 2026-09-11 19:26:06 -05:00 committed by GitHub
parent c9831b9680
commit 51d2e998fc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 103 additions and 0 deletions

View file

@ -0,0 +1,96 @@
using System;
using System.Collections.Generic;
using Server;
using Server.Mobiles;
using Server.Items;
using Server.Regions;
using Xunit;
namespace UOContent.Tests.Mobiles;
[Collection("Sequential UOContent Tests")]
public class MountRegionTests
{
public static IEnumerable<object[]> MountCases()
{
foreach (var era in Enum.GetValues<Expansion>())
{
foreach (var allowed in new[] { false, true })
{
yield return new object[] { era, allowed, false };
yield return new object[] { era, allowed, true };
}
}
}
[Theory]
[MemberData(nameof(MountCases))]
public void Mounting_RespectsActiveRegion(Expansion era, bool allowed, bool ethereal)
{
var previous = Core.Expansion;
var region = new MountTestRegion(allowed);
PlayerMobile player = null;
TestHorse horse = null;
EtherealHorse statue = null;
try
{
Core.Expansion = era;
region.Register();
player = new PlayerMobile(World.NewMobile);
player.DefaultMobileInit();
player.Race = Race.Human;
player.AddItem(new Backpack());
player.MoveToWorld(new Point3D(1000, 1000, 0), Map.Felucca);
Assert.Same(region, player.Region);
if (ethereal)
{
statue = new EtherealHorse { IsRewardItem = false };
player.Backpack.DropItem(statue);
Assert.Same(player.Backpack, statue.Parent);
Assert.Equal(allowed, statue.Validate(player));
}
else
{
horse = new TestHorse();
horse.MoveToWorld(player.Location, player.Map);
horse.SetControlMaster(player);
horse.OnDoubleClick(player);
Assert.Equal(allowed, horse.Rider == player);
Assert.Equal(allowed, player.Mounted);
}
}
finally
{
horse?.Delete();
statue?.Delete();
player?.Delete();
region.Unregister();
Core.Expansion = previous;
}
}
private class MountTestRegion : BaseRegion
{
private readonly bool _allowed;
public MountTestRegion(bool allowed)
: base("MountRegionTest", Map.Felucca, 100,
new Rectangle3D(990, 990, -128, 20, 20, 256))
{
_allowed = allowed;
}
public override bool MountsAllowed => _allowed;
}
// The test fixture does not configure NPCSpeeds.
private class TestHorse : Horse
{
public override void GetSpeeds(out double activeSpeed, out double passiveSpeed)
{
activeSpeed = 0.2;
passiveSpeed = 0.4;
}
}
}

View file

@ -3,6 +3,7 @@ using System;
using Server.Items;
using Server.Misc;
using Server.Multis;
using Server.Regions;
using Server.Targeting;
namespace Server.Mobiles;
@ -286,6 +287,12 @@ public abstract partial class BaseMount : BaseCreature, IMount
if (mob is PlayerMobile mobile)
{
if (mobile.Region is BaseRegion { MountsAllowed: false })
{
mobile.SendLocalizedMessage(1042317); // You may not ride at this time
result = false;
}
if (mobile.MountBlockReason != BlockMountType.None)
{
mobile.SendLocalizedMessage((int)mobile.MountBlockReason);