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
149
src/AcDream.App/UI/Layout/RadarSnapshotProvider.cs
Normal file
149
src/AcDream.App/UI/Layout/RadarSnapshotProvider.cs
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
using System.Numerics;
|
||||
using AcDream.Core.Items;
|
||||
using AcDream.Core.Net;
|
||||
using AcDream.Core.Physics.Motion;
|
||||
using AcDream.Core.Ui;
|
||||
using AcDream.Core.World;
|
||||
|
||||
namespace AcDream.App.UI.Layout;
|
||||
|
||||
/// <summary>
|
||||
/// Joins acdream's existing two-table world model into the immutable projected frame
|
||||
/// consumed by <see cref="UiRadar"/>. This is the App-layer equivalent of retail's
|
||||
/// <c>gmRadarUI::DrawObjects</c> object-maintenance walk: Core still owns every
|
||||
/// AC-specific classification and math decision, while the retained widget remains a
|
||||
/// backend-only renderer.
|
||||
/// </summary>
|
||||
public sealed class RadarSnapshotProvider
|
||||
{
|
||||
private static readonly Vector2 ProductionCenter = new(60f, 60f);
|
||||
|
||||
private readonly ClientObjectTable _objects;
|
||||
private readonly IReadOnlyDictionary<uint, WorldEntity> _worldEntities;
|
||||
private readonly IReadOnlyDictionary<uint, WorldSession.EntitySpawn> _spawns;
|
||||
private readonly Func<uint> _playerGuid;
|
||||
private readonly Func<float> _playerYawRadians;
|
||||
private readonly Func<uint> _playerCellId;
|
||||
private readonly Func<uint?> _selectedGuid;
|
||||
private readonly Func<bool> _coordinatesOnRadar;
|
||||
private readonly Func<bool> _uiLocked;
|
||||
private readonly Func<uint, RadarRelationshipTraits>? _relationshipFor;
|
||||
|
||||
public RadarSnapshotProvider(
|
||||
ClientObjectTable objects,
|
||||
IReadOnlyDictionary<uint, WorldEntity> worldEntities,
|
||||
IReadOnlyDictionary<uint, WorldSession.EntitySpawn> spawns,
|
||||
Func<uint> playerGuid,
|
||||
Func<float> playerYawRadians,
|
||||
Func<uint> playerCellId,
|
||||
Func<uint?> selectedGuid,
|
||||
Func<bool> coordinatesOnRadar,
|
||||
Func<bool> uiLocked,
|
||||
Func<uint, RadarRelationshipTraits>? relationshipFor = null)
|
||||
{
|
||||
_objects = objects ?? throw new ArgumentNullException(nameof(objects));
|
||||
_worldEntities = worldEntities ?? throw new ArgumentNullException(nameof(worldEntities));
|
||||
_spawns = spawns ?? throw new ArgumentNullException(nameof(spawns));
|
||||
_playerGuid = playerGuid ?? throw new ArgumentNullException(nameof(playerGuid));
|
||||
_playerYawRadians = playerYawRadians ?? throw new ArgumentNullException(nameof(playerYawRadians));
|
||||
_playerCellId = playerCellId ?? throw new ArgumentNullException(nameof(playerCellId));
|
||||
_selectedGuid = selectedGuid ?? throw new ArgumentNullException(nameof(selectedGuid));
|
||||
_coordinatesOnRadar = coordinatesOnRadar ?? throw new ArgumentNullException(nameof(coordinatesOnRadar));
|
||||
_uiLocked = uiLocked ?? throw new ArgumentNullException(nameof(uiLocked));
|
||||
_relationshipFor = relationshipFor;
|
||||
}
|
||||
|
||||
public UiRadarSnapshot BuildSnapshot()
|
||||
{
|
||||
bool uiLocked = _uiLocked();
|
||||
uint playerGuid = _playerGuid();
|
||||
if (playerGuid == 0u || !_worldEntities.TryGetValue(playerGuid, out var playerEntity))
|
||||
return UiRadarSnapshot.Empty with { UiLocked = uiLocked };
|
||||
|
||||
float heading = MoveToMath.HeadingFromYaw(_playerYawRadians());
|
||||
uint playerCellId = _playerCellId();
|
||||
bool isOutside = RadarCoordinates.TryFromCell(playerCellId, out var playerCoordinates);
|
||||
string? coordinates = _coordinatesOnRadar() && isOutside
|
||||
? playerCoordinates.CombinedText
|
||||
: null;
|
||||
|
||||
uint playerPwd = _spawns.TryGetValue(playerGuid, out var playerSpawn)
|
||||
? playerSpawn.ObjectDescriptionFlags ?? 0u
|
||||
: 0u;
|
||||
var playerTraits = RadarObjectTraits.FromPublicWeenieDescription(
|
||||
(uint)(_objects.Get(playerGuid)?.Type ?? ItemType.None), playerPwd);
|
||||
float range = RetailRadar.GetRangeMeters(isOutside);
|
||||
|
||||
var blips = new List<UiRadarBlip>(_worldEntities.Count);
|
||||
foreach (var pair in _worldEntities)
|
||||
{
|
||||
uint guid = pair.Key;
|
||||
if (guid == playerGuid)
|
||||
continue;
|
||||
|
||||
var entity = pair.Value;
|
||||
var clientObject = _objects.Get(guid);
|
||||
_spawns.TryGetValue(guid, out var spawn);
|
||||
|
||||
byte? rawBehavior = clientObject?.RadarBehavior ?? spawn.RadarBehavior;
|
||||
if (rawBehavior is null
|
||||
|| !RetailRadar.IsShowable((RadarBehavior)rawBehavior.Value, hasPhysicsObject: true))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
uint itemType = (uint)(clientObject?.Type ?? ItemType.None);
|
||||
if (itemType == 0u)
|
||||
itemType = spawn.ItemType ?? 0u;
|
||||
uint pwd = spawn.ObjectDescriptionFlags ?? 0u;
|
||||
byte colorOverride = clientObject?.RadarBlipColor ?? spawn.RadarBlipColor ?? 0;
|
||||
var traits = RadarObjectTraits.FromPublicWeenieDescription(
|
||||
itemType, pwd, colorOverride);
|
||||
|
||||
var relationship = _relationshipFor?.Invoke(guid) ?? default;
|
||||
relationship = relationship with
|
||||
{
|
||||
PlayerIsPlayerKiller = playerTraits.IsPlayerKiller,
|
||||
PlayerIsPkLite = playerTraits.IsPkLite,
|
||||
};
|
||||
var shape = RetailRadar.GetBlipShape(traits, relationship);
|
||||
if (shape == RadarBlipShape.Undef)
|
||||
continue;
|
||||
|
||||
Vector3 playerSpace = MoveToMath.GlobalToLocalVec(
|
||||
playerEntity.Rotation,
|
||||
entity.Position - playerEntity.Position);
|
||||
if (!RetailRadar.TryProject(
|
||||
playerSpace,
|
||||
ProductionCenter,
|
||||
RadarController.RadarPixelRadius,
|
||||
range,
|
||||
out var projection))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var color = RadarBlipColors.For(traits, relationship)
|
||||
.DimRgb(projection.RgbMultiplier);
|
||||
string? name = clientObject?.Name;
|
||||
if (string.IsNullOrEmpty(name))
|
||||
name = spawn.Name ?? $"0x{guid:X8}";
|
||||
|
||||
blips.Add(new UiRadarBlip(
|
||||
ObjectId: guid,
|
||||
Name: name,
|
||||
PixelX: projection.Pixel.X,
|
||||
PixelY: projection.Pixel.Y,
|
||||
Color: new Vector4(color.Red, color.Green, color.Blue, color.Alpha),
|
||||
Shape: shape,
|
||||
Selected: _selectedGuid() == guid));
|
||||
}
|
||||
|
||||
return new UiRadarSnapshot(
|
||||
PlayerHeadingDegrees: heading,
|
||||
Blips: blips,
|
||||
CoordinatesText: coordinates,
|
||||
BlankBlips: false,
|
||||
UiLocked: uiLocked);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue