### Summary - Adds CanSpawnMobile(x, y, minZ, maxZ, canSwim, cantWalk, out spawnZ) overload for finding spawn surfaces within a Z range - Adds CanSpawnItem(x, y, minZ, maxZ, out spawnZ) for item spawning with Surface+Impassable support (tables, furniture) - Uses bitmask optimization inspired by Item.DropToWorld's m_OpenSlots pattern for O(1) surface/blocker checks - HomeRange spawners now use surface detection to set proper Z bounds ### Key Changes Map.cs: - CanSpawnMobile with Z-range finds lowest valid surface for mobiles - CanSpawnItem with Z-range finds lowest valid surface for items (including tables) - CanFitItem for point-check item placement on Surface+Impassable tiles - Bitmask approach eliminates nested loops and stackalloc arrays Spawners: - Simplified GetSpawnPosition using new Z-range methods - HomeRange setter detects surface below spawner for proper Z bounds - Consistent handling for mobiles and items ### Bug Fixes - Water tiles (Impassable | Wet) no longer block swimming mobs - Items can now spawn on tables/furniture (Surface+Impassable) ### Test Plan - Run dotnet test - 631 tests pass - Manual testing: multi-story spawning, water mobs, item spawning on tables - Verify HomeRange spawner movement shifts bounds correctly
47 lines
1.6 KiB
C#
47 lines
1.6 KiB
C#
using System;
|
|
using Xunit;
|
|
|
|
namespace Server.Tests;
|
|
|
|
/// <summary>
|
|
/// Fixture for all server tests. Attempts to load TileData from client files if available.
|
|
/// Configure client path via MODERNUO_CLIENT_PATH environment variable or place files at C:\Ultima Online Classic.
|
|
/// </summary>
|
|
[CollectionDefinition("Sequential Server Tests", DisableParallelization = true)]
|
|
public class ServerFixture : ICollectionFixture<ServerFixture>, IDisposable
|
|
{
|
|
/// <summary>
|
|
/// True if TileData was successfully loaded from client files.
|
|
/// </summary>
|
|
public static bool TileDataLoaded => TestServerInitializer.TileDataLoaded;
|
|
|
|
/// <summary>
|
|
/// A tile ID known to have the Surface flag. 0 if not found.
|
|
/// </summary>
|
|
public static ushort SurfaceTileId => TestServerInitializer.SurfaceTileId;
|
|
|
|
/// <summary>
|
|
/// A tile ID known to have the Impassable flag. 0 if not found.
|
|
/// </summary>
|
|
public static ushort ImpassableTileId => TestServerInitializer.ImpassableTileId;
|
|
|
|
/// <summary>
|
|
/// A tile ID known to have the Wet flag (water). 0 if not found.
|
|
/// </summary>
|
|
public static ushort WetTileId => TestServerInitializer.WetTileId;
|
|
|
|
/// <summary>
|
|
/// A tile ID known to have both Surface and Impassable flags (tables, furniture). 0 if not found.
|
|
/// </summary>
|
|
public static ushort SurfaceImpassableTileId => TestServerInitializer.SurfaceImpassableTileId;
|
|
|
|
public ServerFixture()
|
|
{
|
|
TestServerInitializer.Initialize(loadTileData: true);
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
Timer.Init(0);
|
|
}
|
|
}
|