feat(ui): port retail radar and compass
This commit is contained in:
parent
c4af181b92
commit
3cbe4b00a1
43 changed files with 2882 additions and 262 deletions
146
tests/AcDream.App.Tests/UI/Layout/RadarSnapshotProviderTests.cs
Normal file
146
tests/AcDream.App.Tests/UI/Layout/RadarSnapshotProviderTests.cs
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
using System.Numerics;
|
||||
using AcDream.App.UI.Layout;
|
||||
using AcDream.Core.Items;
|
||||
using AcDream.Core.Net;
|
||||
using AcDream.Core.Net.Messages;
|
||||
using AcDream.Core.Ui;
|
||||
using AcDream.Core.World;
|
||||
|
||||
namespace AcDream.App.Tests.UI.Layout;
|
||||
|
||||
public sealed class RadarSnapshotProviderTests
|
||||
{
|
||||
[Fact]
|
||||
public void BuildSnapshot_JoinsWorldAndWeenieTables_UsingCoreRetailMath()
|
||||
{
|
||||
const uint player = 1u;
|
||||
const uint monster = 2u;
|
||||
var objects = new ClientObjectTable();
|
||||
objects.Ingest(Weenie(player, "Player", ItemType.Creature));
|
||||
objects.Ingest(Weenie(monster, "Drudge", ItemType.Creature) with
|
||||
{
|
||||
RadarBehavior = (byte)RadarBehavior.ShowAlways,
|
||||
});
|
||||
|
||||
var entities = new Dictionary<uint, WorldEntity>
|
||||
{
|
||||
[player] = Entity(player, Vector3.Zero, Quaternion.Identity),
|
||||
[monster] = Entity(monster, new Vector3(0f, 15f, 6f), Quaternion.Identity),
|
||||
};
|
||||
var spawns = new Dictionary<uint, WorldSession.EntitySpawn>
|
||||
{
|
||||
[player] = Spawn(player) with { ObjectDescriptionFlags = 0x00000008u }, // BF_PLAYER
|
||||
[monster] = Spawn(monster) with { ObjectDescriptionFlags = 0x00000010u }, // BF_ATTACKABLE
|
||||
};
|
||||
|
||||
uint? selected = monster;
|
||||
var provider = new RadarSnapshotProvider(
|
||||
objects, entities, spawns,
|
||||
playerGuid: () => player,
|
||||
playerYawRadians: () => MathF.PI / 2f, // retail heading 0 degrees
|
||||
playerCellId: () => 0xA9B40001u,
|
||||
selectedGuid: () => selected,
|
||||
coordinatesOnRadar: () => true,
|
||||
uiLocked: () => false);
|
||||
|
||||
var snapshot = provider.BuildSnapshot();
|
||||
|
||||
Assert.Equal(0f, snapshot.PlayerHeadingDegrees, 3);
|
||||
Assert.Equal(RadarCoordinates.TryFromCell(0xA9B40001u, out var coords)
|
||||
? coords.CombinedText : null, snapshot.CoordinatesText);
|
||||
var blip = Assert.Single(snapshot.Blips);
|
||||
Assert.Equal(monster, blip.ObjectId);
|
||||
Assert.Equal("Drudge", blip.Name);
|
||||
Assert.Equal(60f, blip.PixelX);
|
||||
Assert.Equal(50f, blip.PixelY); // 15 m * 50 px / 75 m
|
||||
Assert.Equal(RadarBlipShape.Plus, blip.Shape);
|
||||
Assert.True(blip.Selected);
|
||||
Assert.Equal(RadarBlipColors.Gold.Red * 0.65f, blip.Color.X, 5);
|
||||
Assert.Equal(RadarBlipColors.Gold.Green * 0.65f, blip.Color.Y, 5);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildSnapshot_RejectsShowNever_AndHidesIndoorCoordinates()
|
||||
{
|
||||
const uint player = 10u;
|
||||
const uint hidden = 11u;
|
||||
var objects = new ClientObjectTable();
|
||||
objects.Ingest(Weenie(player, "Player", ItemType.Creature));
|
||||
objects.Ingest(Weenie(hidden, "Hidden", ItemType.Portal) with
|
||||
{
|
||||
RadarBehavior = (byte)RadarBehavior.ShowNever,
|
||||
});
|
||||
var entities = new Dictionary<uint, WorldEntity>
|
||||
{
|
||||
[player] = Entity(player, Vector3.Zero, Quaternion.Identity),
|
||||
[hidden] = Entity(hidden, new Vector3(2f, 2f, 0f), Quaternion.Identity),
|
||||
};
|
||||
var spawns = new Dictionary<uint, WorldSession.EntitySpawn>
|
||||
{
|
||||
[player] = Spawn(player),
|
||||
[hidden] = Spawn(hidden),
|
||||
};
|
||||
var provider = new RadarSnapshotProvider(
|
||||
objects, entities, spawns,
|
||||
playerGuid: () => player,
|
||||
playerYawRadians: () => 0f,
|
||||
playerCellId: () => 0xA9B40100u, // EnvCell: no landscape coordinate
|
||||
selectedGuid: () => null,
|
||||
coordinatesOnRadar: () => true,
|
||||
uiLocked: () => true);
|
||||
|
||||
var snapshot = provider.BuildSnapshot();
|
||||
|
||||
Assert.Empty(snapshot.Blips);
|
||||
Assert.Null(snapshot.CoordinatesText);
|
||||
Assert.True(snapshot.UiLocked);
|
||||
}
|
||||
|
||||
private static WorldEntity Entity(uint guid, Vector3 position, Quaternion rotation) => new()
|
||||
{
|
||||
Id = guid,
|
||||
ServerGuid = guid,
|
||||
SourceGfxObjOrSetupId = 0u,
|
||||
Position = position,
|
||||
Rotation = rotation,
|
||||
MeshRefs = Array.Empty<MeshRef>(),
|
||||
};
|
||||
|
||||
private static WorldSession.EntitySpawn Spawn(uint guid) => new(
|
||||
Guid: guid,
|
||||
Position: null,
|
||||
SetupTableId: null,
|
||||
AnimPartChanges: Array.Empty<CreateObject.AnimPartChange>(),
|
||||
TextureChanges: Array.Empty<CreateObject.TextureChange>(),
|
||||
SubPalettes: Array.Empty<CreateObject.SubPaletteSwap>(),
|
||||
BasePaletteId: null,
|
||||
ObjScale: null,
|
||||
Name: null,
|
||||
ItemType: null,
|
||||
MotionState: null,
|
||||
MotionTableId: null);
|
||||
|
||||
private static WeenieData Weenie(uint guid, string name, ItemType type) => new(
|
||||
Guid: guid,
|
||||
Name: name,
|
||||
Type: type,
|
||||
WeenieClassId: 1u,
|
||||
IconId: 0u,
|
||||
IconOverlayId: 0u,
|
||||
IconUnderlayId: 0u,
|
||||
Effects: 0u,
|
||||
Value: null,
|
||||
StackSize: null,
|
||||
StackSizeMax: null,
|
||||
Burden: null,
|
||||
ContainerId: null,
|
||||
WielderId: null,
|
||||
ValidLocations: null,
|
||||
CurrentWieldedLocation: null,
|
||||
Priority: null,
|
||||
ItemsCapacity: null,
|
||||
ContainersCapacity: null,
|
||||
Structure: null,
|
||||
MaxStructure: null,
|
||||
Workmanship: null);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue