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
|
|
@ -16,6 +16,17 @@ public class DatWidgetFactoryTests
|
|||
Assert.IsType<UiMeter>(e);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RetailRadarClass_MakesUiRadar()
|
||||
{
|
||||
var e = DatWidgetFactory.Create(
|
||||
new ElementInfo { Type = UiRadar.RetailClassId, Width = 120, Height = 140 },
|
||||
NoTex,
|
||||
null);
|
||||
|
||||
Assert.IsType<UiRadar>(e);
|
||||
}
|
||||
|
||||
// ── Test 2: Unknown type → UiDatElement fallback ─────────────────────────
|
||||
|
||||
[Fact]
|
||||
|
|
|
|||
|
|
@ -57,6 +57,14 @@ public static class FixtureLoader
|
|||
public static AcDream.App.UI.Layout.ElementInfo LoadChatInfos()
|
||||
=> LoadInfos("chat_21000006.json");
|
||||
|
||||
/// <summary>Builds the committed retail radar LayoutDesc 0x21000074 fixture.</summary>
|
||||
public static ImportedLayout LoadRadar()
|
||||
=> LayoutImporter.Build(LoadRadarInfos(), _ => (0u, 0, 0), null);
|
||||
|
||||
/// <summary>Returns the resolved ElementInfo tree for retail radar LayoutDesc 0x21000074.</summary>
|
||||
public static AcDream.App.UI.Layout.ElementInfo LoadRadarInfos()
|
||||
=> LoadInfos("radar_21000074.json");
|
||||
|
||||
// ── Shared loader ────────────────────────────────────────────────────────
|
||||
|
||||
private static AcDream.App.UI.Layout.ElementInfo LoadInfos(string fileName)
|
||||
|
|
|
|||
159
tests/AcDream.App.Tests/UI/Layout/RadarControllerTests.cs
Normal file
159
tests/AcDream.App.Tests/UI/Layout/RadarControllerTests.cs
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
using System.Numerics;
|
||||
using AcDream.App.UI;
|
||||
using AcDream.App.UI.Layout;
|
||||
using AcDream.Core.Ui;
|
||||
|
||||
namespace AcDream.App.Tests.UI.Layout;
|
||||
|
||||
public sealed class RadarControllerTests
|
||||
{
|
||||
[Fact]
|
||||
public void Bind_RealFixture_ReusesImportedCoordinateTextAndDatBackground()
|
||||
{
|
||||
var layout = FixtureLoader.LoadRadar();
|
||||
_ = RadarController.Bind(
|
||||
layout,
|
||||
() => new UiRadarSnapshot(0f, Array.Empty<UiRadarBlip>(), "42.1N,33.6E"));
|
||||
|
||||
var coordinates = Assert.IsType<UiText>(
|
||||
layout.FindElement(RadarController.CoordinateContainerId));
|
||||
Assert.Equal(0x06004CC0u, coordinates.BackgroundSprite);
|
||||
Assert.Empty(coordinates.Children);
|
||||
Assert.True(coordinates.ClickThrough);
|
||||
Assert.False(coordinates.AcceptsFocus);
|
||||
Assert.Equal("42.1N,33.6E", Assert.Single(coordinates.LinesProvider()).Text);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Bind_UsesImportedChildrenForCompassCoordinatesAndLockChrome()
|
||||
{
|
||||
var layout = BuildRadarLayout();
|
||||
var state = new UiRadarSnapshot(
|
||||
0f,
|
||||
Array.Empty<UiRadarBlip>(),
|
||||
"42.1N,33.6E",
|
||||
UiLocked: false);
|
||||
bool? requestedLock = null;
|
||||
|
||||
_ = RadarController.Bind(
|
||||
layout,
|
||||
() => state,
|
||||
setUiLocked: value => requestedLock = value);
|
||||
|
||||
var radar = Assert.IsType<UiRadar>(layout.Root);
|
||||
Assert.Equal(new Vector2(60f, 60f), radar.Center);
|
||||
Assert.True(radar.Draggable);
|
||||
|
||||
var coordinateContainer = Assert.IsType<UiDatElement>(
|
||||
layout.FindElement(RadarController.CoordinateContainerId));
|
||||
Assert.True(coordinateContainer.Visible);
|
||||
var coordinateText = Assert.IsType<UiText>(Assert.Single(coordinateContainer.Children));
|
||||
Assert.Equal("42.1N,33.6E", Assert.Single(coordinateText.LinesProvider()).Text);
|
||||
|
||||
var north = layout.FindElement(RadarController.NorthTokenId)!;
|
||||
Assert.Equal(55f, north.Left);
|
||||
Assert.Equal(1f, north.Top);
|
||||
|
||||
var lockButton = Assert.IsType<UiButton>(layout.FindElement(RadarController.LockButtonId));
|
||||
Assert.Equal("UnlockedUI", lockButton.ActiveState);
|
||||
var click = new UiEvent(lockButton.EventId, lockButton, UiEventType.Click);
|
||||
Assert.True(lockButton.OnEvent(in click));
|
||||
Assert.True(requestedLock);
|
||||
|
||||
state = state with
|
||||
{
|
||||
PlayerHeadingDegrees = 90f,
|
||||
CoordinatesText = null,
|
||||
UiLocked = true,
|
||||
};
|
||||
radar.Refresh();
|
||||
|
||||
Assert.False(radar.Draggable);
|
||||
Assert.False(coordinateContainer.Visible);
|
||||
Assert.False(coordinateText.Visible);
|
||||
Assert.Equal("LockedUI", lockButton.ActiveState);
|
||||
|
||||
var drag = layout.FindElement(RadarController.DragButtonId)!;
|
||||
Assert.False(drag.Visible);
|
||||
Assert.False(drag.ClickThrough);
|
||||
|
||||
float northMagnitude = Vector2.Distance(new Vector2(60f, 5.5f), radar.Center);
|
||||
var expectedNorth = RetailRadar.GetCompassTokenTopLeft(
|
||||
90f,
|
||||
RadarCompassPoint.North,
|
||||
radar.Center,
|
||||
northMagnitude,
|
||||
new Vector2(north.Width, north.Height));
|
||||
Assert.Equal(expectedNorth.X, north.Left);
|
||||
Assert.Equal(expectedNorth.Y, north.Top);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Bind_LeavesStaticDatChildrenInRetainedTree()
|
||||
{
|
||||
var layout = BuildRadarLayout();
|
||||
_ = RadarController.Bind(layout, () => UiRadarSnapshot.Empty);
|
||||
|
||||
Assert.Equal(8, layout.Root.Children.Count);
|
||||
Assert.IsType<UiDatElement>(layout.FindElement(RadarController.RadarDiscId));
|
||||
Assert.IsType<UiButton>(layout.FindElement(RadarController.LockButtonId));
|
||||
Assert.IsType<UiDatElement>(layout.FindElement(RadarController.DragButtonId));
|
||||
Assert.False(layout.FindElement(RadarController.CoordinateContainerId)!.Visible);
|
||||
}
|
||||
|
||||
private static ImportedLayout BuildRadarLayout()
|
||||
{
|
||||
var root = new ElementInfo
|
||||
{
|
||||
Id = RadarController.RootId,
|
||||
Type = UiRadar.RetailClassId,
|
||||
Width = 120,
|
||||
Height = 140,
|
||||
};
|
||||
|
||||
root.Children.Add(Sprite(RadarController.CoordinateContainerId, 0, 120, 120, 18, 0x06004CC0u));
|
||||
root.Children.Add(Sprite(RadarController.RadarDiscId, 0, 0, 120, 120, 0x06004CC1u));
|
||||
root.Children.Add(Sprite(RadarController.NorthTokenId, 55, 1, 10, 9, 0x060011FBu));
|
||||
root.Children.Add(Sprite(RadarController.EastTokenId, 110, 55, 10, 9, 0x06001938u));
|
||||
root.Children.Add(Sprite(RadarController.SouthTokenId, 55, 110, 10, 9, 0x0600193Au));
|
||||
root.Children.Add(Sprite(RadarController.WestTokenId, 0, 55, 10, 9, 0x0600193Cu));
|
||||
|
||||
var lockButton = new ElementInfo
|
||||
{
|
||||
Id = RadarController.LockButtonId,
|
||||
Type = 1,
|
||||
X = 6,
|
||||
Y = 6,
|
||||
Width = 27,
|
||||
Height = 27,
|
||||
};
|
||||
lockButton.StateMedia["LockedUI"] = (0x060074B7u, 3);
|
||||
lockButton.StateMedia["UnlockedUI"] = (0x060074B8u, 3);
|
||||
root.Children.Add(lockButton);
|
||||
|
||||
root.Children.Add(Sprite(RadarController.DragButtonId, 87, 6, 27, 27, 0x060074C9u, type: 2));
|
||||
return LayoutImporter.Build(root, _ => (0u, 0, 0), null);
|
||||
}
|
||||
|
||||
private static ElementInfo Sprite(
|
||||
uint id,
|
||||
float x,
|
||||
float y,
|
||||
float width,
|
||||
float height,
|
||||
uint sprite,
|
||||
uint type = 3)
|
||||
{
|
||||
var info = new ElementInfo
|
||||
{
|
||||
Id = id,
|
||||
Type = type,
|
||||
X = x,
|
||||
Y = y,
|
||||
Width = width,
|
||||
Height = height,
|
||||
};
|
||||
info.StateMedia[""] = (sprite, 1);
|
||||
return info;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
using AcDream.App.UI;
|
||||
using AcDream.App.UI.Layout;
|
||||
|
||||
namespace AcDream.App.Tests.UI.Layout;
|
||||
|
||||
public sealed class RadarLayoutConformanceTests
|
||||
{
|
||||
[Fact]
|
||||
public void RadarFixture_HasRetailRootAndAllStaticAssets()
|
||||
{
|
||||
var root = FixtureLoader.LoadRadarInfos();
|
||||
|
||||
Assert.Equal(RadarController.RootId, root.Id);
|
||||
Assert.Equal(UiRadar.RetailClassId, root.Type);
|
||||
Assert.Equal(120f, root.Width);
|
||||
Assert.Equal(140f, root.Height);
|
||||
Assert.Equal(8, root.Children.Count);
|
||||
|
||||
AssertElement(root, RadarController.CoordinateContainerId, 0, 120, 120, 18, 0x06004CC0u);
|
||||
AssertElement(root, RadarController.RadarDiscId, 0, 0, 120, 120, 0x06004CC1u);
|
||||
AssertElement(root, RadarController.NorthTokenId, 55, 1, 10, 9, 0x060011FBu);
|
||||
AssertElement(root, RadarController.EastTokenId, 110, 55, 10, 9, 0x06001938u);
|
||||
AssertElement(root, RadarController.SouthTokenId, 55, 110, 10, 9, 0x0600193Au);
|
||||
AssertElement(root, RadarController.WestTokenId, 0, 55, 10, 9, 0x0600193Cu);
|
||||
AssertElement(root, RadarController.DragButtonId, 87, 6, 27, 27, 0x060074C9u);
|
||||
|
||||
var lockButton = Find(root, RadarController.LockButtonId)!;
|
||||
Assert.Equal(1u, lockButton.Type);
|
||||
Assert.Equal(0x060074B7u, lockButton.StateMedia["LockedUI"].File);
|
||||
Assert.Equal(0x060074B8u, lockButton.StateMedia["UnlockedUI"].File);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RadarFixture_BuildsDedicatedWidgetAndPreservesStaticChildren()
|
||||
{
|
||||
var layout = FixtureLoader.LoadRadar();
|
||||
|
||||
Assert.IsType<UiRadar>(layout.Root);
|
||||
Assert.Equal(8, layout.Root.Children.Count);
|
||||
var coordinates = Assert.IsType<UiText>(
|
||||
layout.FindElement(RadarController.CoordinateContainerId));
|
||||
Assert.Equal(0x06004CC0u, coordinates.BackgroundSprite);
|
||||
Assert.IsType<UiDatElement>(layout.FindElement(RadarController.RadarDiscId));
|
||||
Assert.IsType<UiButton>(layout.FindElement(RadarController.LockButtonId));
|
||||
}
|
||||
|
||||
private static void AssertElement(
|
||||
ElementInfo root,
|
||||
uint id,
|
||||
float x,
|
||||
float y,
|
||||
float width,
|
||||
float height,
|
||||
uint sprite)
|
||||
{
|
||||
var element = Find(root, id)!;
|
||||
Assert.Equal(x, element.X);
|
||||
Assert.Equal(y, element.Y);
|
||||
Assert.Equal(width, element.Width);
|
||||
Assert.Equal(height, element.Height);
|
||||
Assert.Equal(sprite, element.StateMedia[""].File);
|
||||
}
|
||||
|
||||
private static ElementInfo? Find(ElementInfo node, uint id)
|
||||
{
|
||||
if (node.Id == id)
|
||||
return node;
|
||||
foreach (var child in node.Children)
|
||||
{
|
||||
var found = Find(child, id);
|
||||
if (found is not null)
|
||||
return found;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text.Json;
|
||||
using AcDream.App.UI.Layout;
|
||||
using DatReaderWriter;
|
||||
using DatReaderWriter.Options;
|
||||
|
||||
namespace AcDream.App.Tests.UI.Layout;
|
||||
|
||||
/// <summary>One-off generator for the committed retail radar LayoutDesc fixture.</summary>
|
||||
public sealed class RadarLayoutFixtureGenerator
|
||||
{
|
||||
[Fact(Skip = "manual: regenerates the committed radar fixture; needs the real dats (ACDREAM_DAT_DIR)")]
|
||||
public void GenerateRadarFixture()
|
||||
{
|
||||
var datDir = Environment.GetEnvironmentVariable("ACDREAM_DAT_DIR")
|
||||
?? Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
|
||||
"Documents",
|
||||
"Asheron's Call");
|
||||
using var dats = new DatCollection(datDir, DatAccessType.Read);
|
||||
var info = LayoutImporter.ImportInfos(dats, RadarController.LayoutId);
|
||||
Assert.NotNull(info);
|
||||
|
||||
var json = JsonSerializer.Serialize(info, new JsonSerializerOptions
|
||||
{
|
||||
IncludeFields = true,
|
||||
WriteIndented = true,
|
||||
});
|
||||
File.WriteAllText(FixturePath(), json);
|
||||
}
|
||||
|
||||
private static string FixturePath([CallerFilePath] string thisFile = "")
|
||||
=> Path.Combine(Path.GetDirectoryName(thisFile)!, "fixtures", "radar_21000074.json");
|
||||
}
|
||||
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);
|
||||
}
|
||||
250
tests/AcDream.App.Tests/UI/Layout/fixtures/radar_21000074.json
Normal file
250
tests/AcDream.App.Tests/UI/Layout/fixtures/radar_21000074.json
Normal file
|
|
@ -0,0 +1,250 @@
|
|||
{
|
||||
"Id": 268437203,
|
||||
"Type": 268435472,
|
||||
"X": 0,
|
||||
"Y": 0,
|
||||
"Width": 120,
|
||||
"Height": 140,
|
||||
"Left": 1,
|
||||
"Top": 1,
|
||||
"Right": 1,
|
||||
"Bottom": 1,
|
||||
"ReadOrder": 0,
|
||||
"ZLevel": 0,
|
||||
"FontDid": 0,
|
||||
"HJustify": 1,
|
||||
"VJustify": 1,
|
||||
"FontColor": null,
|
||||
"StateMedia": {},
|
||||
"StateCursors": {},
|
||||
"DefaultStateName": "",
|
||||
"Children": [
|
||||
{
|
||||
"Id": 268435518,
|
||||
"Type": 12,
|
||||
"X": 0,
|
||||
"Y": 120,
|
||||
"Width": 120,
|
||||
"Height": 18,
|
||||
"Left": 0,
|
||||
"Top": 0,
|
||||
"Right": 0,
|
||||
"Bottom": 0,
|
||||
"ReadOrder": 1,
|
||||
"ZLevel": 0,
|
||||
"FontDid": 1073741825,
|
||||
"HJustify": 1,
|
||||
"VJustify": 1,
|
||||
"FontColor": null,
|
||||
"StateMedia": {
|
||||
"": {
|
||||
"Item1": 100682944,
|
||||
"Item2": 1
|
||||
}
|
||||
},
|
||||
"StateCursors": {},
|
||||
"DefaultStateName": "",
|
||||
"Children": []
|
||||
},
|
||||
{
|
||||
"Id": 268435519,
|
||||
"Type": 3,
|
||||
"X": 0,
|
||||
"Y": 0,
|
||||
"Width": 120,
|
||||
"Height": 120,
|
||||
"Left": 0,
|
||||
"Top": 0,
|
||||
"Right": 0,
|
||||
"Bottom": 0,
|
||||
"ReadOrder": 2,
|
||||
"ZLevel": 0,
|
||||
"FontDid": 0,
|
||||
"HJustify": 1,
|
||||
"VJustify": 1,
|
||||
"FontColor": null,
|
||||
"StateMedia": {
|
||||
"": {
|
||||
"Item1": 100682945,
|
||||
"Item2": 1
|
||||
}
|
||||
},
|
||||
"StateCursors": {},
|
||||
"DefaultStateName": "",
|
||||
"Children": []
|
||||
},
|
||||
{
|
||||
"Id": 268435520,
|
||||
"Type": 3,
|
||||
"X": 55,
|
||||
"Y": 1,
|
||||
"Width": 10,
|
||||
"Height": 9,
|
||||
"Left": 0,
|
||||
"Top": 0,
|
||||
"Right": 0,
|
||||
"Bottom": 0,
|
||||
"ReadOrder": 5,
|
||||
"ZLevel": 0,
|
||||
"FontDid": 0,
|
||||
"HJustify": 1,
|
||||
"VJustify": 1,
|
||||
"FontColor": null,
|
||||
"StateMedia": {
|
||||
"": {
|
||||
"Item1": 100667899,
|
||||
"Item2": 1
|
||||
}
|
||||
},
|
||||
"StateCursors": {},
|
||||
"DefaultStateName": "",
|
||||
"Children": []
|
||||
},
|
||||
{
|
||||
"Id": 268435521,
|
||||
"Type": 3,
|
||||
"X": 110,
|
||||
"Y": 55,
|
||||
"Width": 10,
|
||||
"Height": 9,
|
||||
"Left": 0,
|
||||
"Top": 0,
|
||||
"Right": 0,
|
||||
"Bottom": 0,
|
||||
"ReadOrder": 6,
|
||||
"ZLevel": 0,
|
||||
"FontDid": 0,
|
||||
"HJustify": 1,
|
||||
"VJustify": 1,
|
||||
"FontColor": null,
|
||||
"StateMedia": {
|
||||
"": {
|
||||
"Item1": 100669752,
|
||||
"Item2": 1
|
||||
}
|
||||
},
|
||||
"StateCursors": {},
|
||||
"DefaultStateName": "",
|
||||
"Children": []
|
||||
},
|
||||
{
|
||||
"Id": 268435522,
|
||||
"Type": 3,
|
||||
"X": 55,
|
||||
"Y": 110,
|
||||
"Width": 10,
|
||||
"Height": 9,
|
||||
"Left": 0,
|
||||
"Top": 0,
|
||||
"Right": 0,
|
||||
"Bottom": 0,
|
||||
"ReadOrder": 7,
|
||||
"ZLevel": 0,
|
||||
"FontDid": 0,
|
||||
"HJustify": 1,
|
||||
"VJustify": 1,
|
||||
"FontColor": null,
|
||||
"StateMedia": {
|
||||
"": {
|
||||
"Item1": 100669754,
|
||||
"Item2": 1
|
||||
}
|
||||
},
|
||||
"StateCursors": {},
|
||||
"DefaultStateName": "",
|
||||
"Children": []
|
||||
},
|
||||
{
|
||||
"Id": 268437155,
|
||||
"Type": 2,
|
||||
"X": 87,
|
||||
"Y": 6,
|
||||
"Width": 27,
|
||||
"Height": 27,
|
||||
"Left": 0,
|
||||
"Top": 0,
|
||||
"Right": 0,
|
||||
"Bottom": 0,
|
||||
"ReadOrder": 4,
|
||||
"ZLevel": 0,
|
||||
"FontDid": 0,
|
||||
"HJustify": 1,
|
||||
"VJustify": 1,
|
||||
"FontColor": null,
|
||||
"StateMedia": {
|
||||
"": {
|
||||
"Item1": 100693193,
|
||||
"Item2": 3
|
||||
}
|
||||
},
|
||||
"StateCursors": {
|
||||
"": {
|
||||
"File": 100688153,
|
||||
"HotspotX": 16,
|
||||
"HotspotY": 16,
|
||||
"IsValid": true
|
||||
}
|
||||
},
|
||||
"DefaultStateName": "",
|
||||
"Children": []
|
||||
},
|
||||
{
|
||||
"Id": 268437017,
|
||||
"Type": 1,
|
||||
"X": 6,
|
||||
"Y": 6,
|
||||
"Width": 27,
|
||||
"Height": 27,
|
||||
"Left": 0,
|
||||
"Top": 0,
|
||||
"Right": 0,
|
||||
"Bottom": 0,
|
||||
"ReadOrder": 3,
|
||||
"ZLevel": 0,
|
||||
"FontDid": 0,
|
||||
"HJustify": 1,
|
||||
"VJustify": 1,
|
||||
"FontColor": null,
|
||||
"StateMedia": {
|
||||
"LockedUI": {
|
||||
"Item1": 100693175,
|
||||
"Item2": 3
|
||||
},
|
||||
"UnlockedUI": {
|
||||
"Item1": 100693176,
|
||||
"Item2": 3
|
||||
}
|
||||
},
|
||||
"StateCursors": {},
|
||||
"DefaultStateName": "Normal",
|
||||
"Children": []
|
||||
},
|
||||
{
|
||||
"Id": 268435523,
|
||||
"Type": 3,
|
||||
"X": 0,
|
||||
"Y": 55,
|
||||
"Width": 10,
|
||||
"Height": 9,
|
||||
"Left": 0,
|
||||
"Top": 0,
|
||||
"Right": 0,
|
||||
"Bottom": 0,
|
||||
"ReadOrder": 8,
|
||||
"ZLevel": 0,
|
||||
"FontDid": 0,
|
||||
"HJustify": 1,
|
||||
"VJustify": 1,
|
||||
"FontColor": null,
|
||||
"StateMedia": {
|
||||
"": {
|
||||
"Item1": 100669756,
|
||||
"Item2": 1
|
||||
}
|
||||
},
|
||||
"StateCursors": {},
|
||||
"DefaultStateName": "",
|
||||
"Children": []
|
||||
}
|
||||
]
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue