diff --git a/docs/ISSUES.md b/docs/ISSUES.md
index dcb72441..93a426b6 100644
--- a/docs/ISSUES.md
+++ b/docs/ISSUES.md
@@ -61,6 +61,12 @@ back to inert generic DAT elements.
`gmUIElement_EffectsIndicator`. The retained controller consequently had no
live bindings for session packet age, Vitae, or the player load ratio and did
not dispatch Character Information or the shared logout confirmation path.
+The strip and its actions are now ported. Link Status (panel 8), both effect
+pages (4/5), Vitae (15), and the authored Mini Game shell (9) are registered as
+children of the same retained main-panel owner as Inventory/Character/Magic,
+so switching pages preserves one canonical position. Link ping RTT and Vitae
+recovery XP are live; transport packet-loss averaging and Mini Game gameplay
+remain the explicitly narrowed AP-110 residual.
**Files:** `src/AcDream.App/UI/Layout/IndicatorBarController.cs`;
`src/AcDream.App/UI/Layout/DatWidgetFactory.cs`;
@@ -74,6 +80,8 @@ quality follows the 5/20/40-second thresholds and bad-link flash cadence;
helpful, harmful, Vitae, and burden react to live state; the burden icon opens
Character Information; effects open their matching panels; crossed swords use
the shared confirmation dialog and close gracefully only after acceptance.
+Every non-ghosted detail icon opens its authored page in the shared main-panel
+position; closing a restore-previous page returns to the deferred ordinary page.
---
diff --git a/docs/architecture/retail-divergence-register.md b/docs/architecture/retail-divergence-register.md
index e0ff8525..31fb7525 100644
--- a/docs/architecture/retail-divergence-register.md
+++ b/docs/architecture/retail-divergence-register.md
@@ -207,7 +207,7 @@ AP-94..AP-112 for the confirmed retail-UI completion gaps.
| ~~AP-107~~ | **RETIRED 2026-07-11 (Wave 3.3 / #197)** — typed `OfferPrimaryClick` returns `NotActive`, `ConsumedSuccess`, or `ConsumedRejected`; every retained item surface plus radar/world offers active target mode before local selection/open/use fallback. Rejections are consumed and cannot drift selection. | `src/AcDream.App/UI/ItemInteractionController.cs`; inventory/paperdoll/toolbar/radar/world call sites | — | — | `UIElement_ItemList::HandleTargetedUseLeftClick @ 0x004E24D0` |
| AP-108 | Paperdoll/AutoWield still omit full `AutoWieldIsLegal`/dual-wield rules, double-click examine/drag from the doll, body-part selection lighting, and retail's synchronous `" - cannot unwield the %s"` failure suffix (the current send seam reports rejection asynchronously). **Primary replacement retired from this row 2026-07-14:** inventory activation and paperdoll drops share the confirmed blocker transaction, preserve explicit slot intent, relocate an already-worn item to an explicitly selected compatible slot, and emit retail's successful move-to-backpack status. **Aetheria retired 2026-07-13.** | `src/AcDream.App/UI/Layout/PaperdollController.cs`; `src/AcDream.App/UI/AutoWieldController.cs` | Basic equip slots, Aetheria, and live doll work; primary weapon, incompatible shield, and mismatched ammo blockers sequence through server-confirmed dequip→wield in both peace and war | Remaining illegal/off-hand cases, asynchronous dequip rejection wording, doll examine/drag, and selection lighting still differ functionally | `CPlayerSystem::AutoWield @ 0x00560A60`; `ACCWeenieObject::BlocksUseOfShield @ 0x0055D3E0`; `gmPaperDollUI @ 0x004A3590..0x004A5F90` |
| AP-109 | Character Titles page is inert and live displayed-title/luminance state is absent | `src/AcDream.App/UI/Layout/CharacterStatController.cs`; `CharacterSheetProvider.cs` | Attributes/skills core output is user-accepted | Titles cannot be selected/displayed and level-200 luminance fields are missing | `gmCharacterTitleUI @ 0x0049A610`; `gmStatManagementUI::UpdateExperience @ 0x004F0A70` |
-| AP-110 | Remaining retained gameplay panels and world HUD are absent: advanced-combat powerbar, residual social/examine/floating chat, quests/map/vitae/options/smartbox, vendor/trade/salvage/tinkering, and D.6 nameplates/floaters | D.5/D.6 roadmap; retained layout registration set | Basic combat plus M3 spellbook/component/effects/favorite-spell surfaces now cover the active melee/missile/magic loops; later waves land each remaining surface against authoritative state | Large portions of retail gameplay still have no production UI outside the current combat/magic/inventory/character loops | Named `gm*UI::PostInit` methods and LayoutDesc catalog |
+| AP-110 | Remaining retained gameplay panels and world HUD are absent: advanced-combat powerbar, residual social/examine/floating chat, quests/map/options/smartbox, vendor/trade/salvage/tinkering, mini-game gameplay, Link Status NAK/retransmission packet-loss averaging, and D.6 nameplates/floaters | `src/AcDream.App/UI/RetailUiRuntime.cs`; `src/AcDream.Core.Net/LinkStatusSnapshot.cs`; D.5/D.6 roadmap | Basic combat plus M3 spellbook/component/effects/favorite-spell/Link/Vitae surfaces now cover the active melee/missile/magic loops; the Link page has exact ping RTT but reports the current transport's zero default until its missing reliability statistics land | Large portions of retail gameplay still have no production UI; real packet loss is displayed as 0.00% instead of retail's moving average | Named `gm*UI::PostInit` methods; `CLinkStatusAverages::GetAveragePacketLoss @ 0x00546610`; LayoutDesc catalog |
| ~~AP-111~~ | **RETIRED 2026-07-11 (M2 held-object parenting)** — equipped hand items are no longer omitted from the render world. CreateObject now preserves Placement/Parent/position timestamp bootstrap; live `0xF749` ParentEvent is parsed with retail sequence freshness; a focused render controller resolves `Setup.HoldingLocations`, applies the child's placement frame, and recomposes the separate child entity after every parent animation tick. Pickup retains the weenie's visual metadata for a later wield. | `src/AcDream.Core.Net/Messages/{CreateObject,ParentEvent}.cs`; `src/AcDream.Core/Meshing/EquippedChildAttachment.cs`; `src/AcDream.App/Rendering/EquippedChildRenderController.cs` | — | — | `ClientCombatSystem::GetDefaultCombatMode @ 0x0056B310`; `SmartBox::HandleParentEvent @ 0x004535D0`; `CPhysicsObj::set_parent @ 0x00515A90`; `CPhysicsObj::UpdateChild @ 0x00512D50` |
| AP-112 | The basic combat bar ports visibility, height selection, desired-power slider, exact 1.0/0.8-second charge, ready-stance gating, request/release, `MaybeStopCompletely`, server-response queueing, and auto-repeat, but still omits `StartAttackRequest`'s `FinishJump` call and exact trained-Recklessness visibility semantics (IA-20 keeps the dark range as the accepted baseline) | `src/AcDream.App/Combat/CombatAttackController.cs`; `src/AcDream.App/UI/Layout/CombatUiController.cs` | The shared player movement owner now performs retail's server-control-gated full stop and movement report before an attack build; the remaining seams require the jump owner and a distinct Recklessness treatment | Starting an attack while charging a jump may not finish that jump exactly when retail does; trained/untrained Recklessness presentation is identical | `ClientCombatSystem::StartAttackRequest @ 0x0056C040`; `CommandInterpreter::MaybeStopCompletely @ 0x006B3B90`; `gmCombatUI::ListenToElementMessage @ 0x004CC430` |
| AP-113 | Invalid lifestone-command arguments display the local text `Usage: /lifestone`; retail definitely emits a local usage/error line but Binary Ninja misidentifies the referenced wide-string address, so its exact wording is not yet recovered | `src/AcDream.UI.Abstractions/Panels/Chat/ChatCommandRouter.cs`; `RetailClientCommandCatalog.cs` | The behavior boundary is exact (handled locally, no chat and no game action); only a low-impact diagnostic sentence differs | `/ls now` can show different wording/color from retail while still refusing the invalid request correctly | `ClientCommunicationSystem::DoLifestone @ 0x0056FC70` |
diff --git a/docs/plans/2026-04-11-roadmap.md b/docs/plans/2026-04-11-roadmap.md
index 1fbb9b07..ca94d438 100644
--- a/docs/plans/2026-04-11-roadmap.md
+++ b/docs/plans/2026-04-11-roadmap.md
@@ -1,6 +1,6 @@
# acdream — strategic roadmap
-**Status:** Living document. Updated 2026-07-17. **M3 active; automated implementation complete, visual gates pending.** M2's connected demo loop is complete. M3 now has the retail cast-intent/component/target lifecycle, exact live enchantment wire state, scoped Magic input, retained spell bar, spellbook, component book, positive/negative effects panels, the complete authored seven-control gameplay indicator strip, and shared `gmPanelUI` switching over the Step 9 projectile/PhysicsScript/particle/portal foundation. The remaining M3 work is the connected single-client magic-UI/casting gate and final two-client portal-out/materialization observer gate.
+**Status:** Living document. Updated 2026-07-17. **M3 active; automated implementation complete, visual gates pending.** M2's connected demo loop is complete. M3 now has the retail cast-intent/component/target lifecycle, exact live enchantment wire state, scoped Magic input, retained spell bar, spellbook, component book, positive/negative effects panels, the complete authored seven-control gameplay indicator strip and its Link/Vitae detail pages, and shared `gmPanelUI` switching over the Step 9 projectile/PhysicsScript/particle/portal foundation. The remaining M3 work is the connected single-client magic-UI/casting gate and final two-client portal-out/materialization observer gate.
**Purpose:** One source of truth for where the project is and where it's going. Every observed defect or missing feature has a named phase that owns it; when something looks wrong in-game, look here to find the phase that'll address it. Implementation details live in per-phase specs under `docs/superpowers/specs/`, not in this file.
---
@@ -22,7 +22,9 @@ active enchantment, then recall through the DAT-driven portal presentation.
equipped caster endowment, scoped retail keys, spellbook/component book,
positive/negative active-enchantment panels, the seven-control link/effect/
Vitae/burden/mini-game/session indicator strip, and the retail
- one-active-main-panel lifecycle. Connected-gate
+ one-active-main-panel lifecycle. Link Status, Helpful/Harmful, Vitae, and
+ the inactive Mini Game shell now occupy that same canonical main-panel
+ position; Link RTT and Vitae recovery XP are live. Connected-gate
correction now also pins type-12 spellbook tabs, retail zero text margins,
and `PassToChildren` Closed/Open chrome through real-DAT fixtures.
3. **Pending user visual gate:** connected projectile/self-buff casts plus the
diff --git a/docs/plans/2026-05-12-milestones.md b/docs/plans/2026-05-12-milestones.md
index e949cc0c..c89e2b3c 100644
--- a/docs/plans/2026-05-12-milestones.md
+++ b/docs/plans/2026-05-12-milestones.md
@@ -714,6 +714,11 @@ the burden/effect/logout actions use the shared panel/dialog owners. Main-panel
visibility now flows through retail's one-active `gmPanelUI` owner. M3 remains
active only for the connected single-client magic
UI/casting visual gate and the final two-client portal observer gate.
+The 2026-07-17 indicator follow-up also mounts the authored Link Status, Vitae,
+and Mini Game detail roots as children of that same main-panel owner. Helpful,
+Harmful, Link, and Vitae therefore replace Inventory/Character/Magic in one
+canonical window position. Link uses retail's payload-free ping/RTT cadence;
+Vitae uses PropertyInt 129/139 and the retail XP-pool threshold formula.
The first connected gate exposed and corrected two retained-import defects:
spellbook tabs are authored stateful text controls (not buttons), and retail
text starts with zero margins. Real-DAT fixtures now pin spellbook binding,
diff --git a/docs/research/2026-07-17-retail-indicator-bar-pseudocode.md b/docs/research/2026-07-17-retail-indicator-bar-pseudocode.md
index b4dc6dc1..72ac82e2 100644
--- a/docs/research/2026-07-17-retail-indicator-bar-pseudocode.md
+++ b/docs/research/2026-07-17-retail-indicator-bar-pseudocode.md
@@ -25,6 +25,17 @@ Named retail functions:
- `gmUIElement_BurdenIndicator::Update @ 0x004E6CE0`
- `LinkStatusHolder::GetConnectionStatus @ 0x00411380`
- `LinkStatusHolder::OnHeartbeat @ 0x004113D0`
+- `gmPanelUI::SetupChildren @ 0x004BC9E0`
+- `gmPanelUI::RecvNotice_SetPanelVisibility @ 0x004BC6F0`
+- `gmLinkStatusUI::PostInit @ 0x004AADB0`
+- `gmLinkStatusUI::Update @ 0x004AAEA0`
+- `gmLinkStatusUI::ListenToElementMessage @ 0x004AB0B0`
+- `gmLinkStatusUI::ListenToGlobalMessage @ 0x004AB110`
+- `gmLinkStatusUI::RecvNotice_Ping @ 0x004AB150`
+- `gmVitaeUI::PostInit @ 0x004A7240`
+- `gmVitaeUI::Update @ 0x004A7400`
+- `VitaeSystem::VitaeCPPoolThreshold @ 0x005C8FD0`
+- `CM_Character::Event_RequestPing @ 0x006A19A0`
- `gmEffectsUI::SpellEffectMatchesUIType @ 0x004B76C0`
- `gmGamePlayUI::RecvNotice_EndCharacterSession @ 0x004EBEA0`
@@ -40,6 +51,11 @@ The installed retail LayoutDesc establishes the fixed child order and art:
| 105 | `0x100000F3` | `0x10000004` | mini-game |
| 125 | `0x100000FA` | ordinary button | end character session |
+`gmPanelUI::SetupChildren` reads enum property `0x10000029` from each
+registered child. The installed main-panel LayoutDesc resolves the indicator
+detail pages to Link Status `8`, Mini Game `9`, and Vitae `15` (Helpful and
+Harmful remain `4` and `5`). They are not independent floating-window ids.
+
## Pseudocode
### Link indicator
@@ -82,6 +98,46 @@ acdream records the last successfully decoded server datagram in
keeps socket/session facts out of the UI while preserving retail's thresholds
and cadence.
+### Link Status detail page
+
+```text
+On shown:
+ register for time notices
+ pleaseRequestPing = true
+ Update()
+
+On hidden:
+ unregister time notices
+ pleaseRequestPing = false
+
+On time notice while visible:
+ if now >= nextUpdate:
+ nextUpdate = now + 5 seconds
+ Update()
+
+Update:
+ replace text with localized connection explanation + 5/20-second legend
+ append 10-second packet-loss percentage with two decimal places
+ append ping in milliseconds, or "????" before a response
+ if pleaseRequestPing or now - lastPingRequest >= 120 seconds:
+ lastPingRequest = now
+ Event_RequestPing()
+ pleaseRequestPing = false
+
+Event_RequestPing:
+ send F7B1, game-action sequence, opcode 0x01E9
+ // no payload
+
+On empty 0x01EA PingResponse:
+ pingRoundTrip = monotonicNow - lastPingRequest
+```
+
+The network session owns the send timestamp and response sample. The retained
+page owns only retail's display and refresh cadence. acdream's transport does
+not yet expose the retail NAK/retransmission moving averages, so packet-loss
+averaging remains the explicit AP-110 residual rather than being guessed from
+ordinary packet sequence gaps.
+
### Helpful and harmful effects
```text
@@ -109,6 +165,25 @@ On PlayerDesc or VitaeChanged:
SetState(Ghosted = 13)
```
+The detail page uses the same modifier:
+
+```text
+lostPercent = 100 - int(vitae * 100)
+if lostPercent <= 0:
+ show localized full-strength sentence
+else:
+ vitaePool = player PropertyInt 129
+ deathLevel = player PropertyInt 139, falling back to Level (25)
+ threshold = int(((deathLevel ^ 2.5 * 2.5) + 20)
+ * vitae ^ 5 + 0.5)
+ remaining = threshold - vitaePool
+ show localized lost-percent sentence and remaining-XP sentence
+```
+
+The x87 powers omitted from the named pseudo-C output for
+`VitaeCPPoolThreshold` were cross-checked against the official ACE
+`Player_Xp.cs` port. The property ids and rounding expression agree.
+
### Burden / character information
```text
@@ -136,6 +211,28 @@ The authored ghosted state is rendered now. The notices remain owned by the
still-unported mini-game subsystem (existing divergence AP-110), so this task
does not manufacture a game-active state from unrelated packets.
+The authored `gmMiniGameUI` root `0x1000016A` is mounted and registered as
+main panel `9`, including its close behavior. It remains unreachable while the
+indicator is Ghosted; board state and game actions remain with the unported
+mini-game subsystem.
+
+### Shared main-panel ownership
+
+```text
+For every toolbar or indicator detail page:
+ register page with gmPanelUI panel id
+ when shown:
+ hide the current child
+ move the requested child to the shared main-panel placement
+ show only the requested child
+ when a page with bool property 0x10000049 closes:
+ restore the deferred ordinary child
+```
+
+Helpful/Harmful, Link Status, Vitae, and the Mini Game shell therefore share
+the same retained host position as Inventory, Character, and Magic. They do
+not remember independent positions.
+
### End character session
```text
@@ -159,8 +256,8 @@ CharacterLogOff and transport Disconnect packets.
`EncumbranceSystem::Load @ 0x004FCC40`.
- The existing enchantment parser preserves the separate Mult, Add, Vitae, and
Cooldown registry buckets used by the retail indicator queries.
-- ACE/reference source is not present in this worktree. No server behavior is
- inferred for the strip: state is derived only from packets already parsed by
- the client, and the named retail client remains the behavior oracle.
-- Full Link Status, Vitae, and mini-game panels remain tracked by AP-110. This
- port does not replace them with an ad-hoc panel.
+- Official ACE was cross-checked for the empty PingResponse and the vitae-pool
+ threshold expression; the named retail client remains the behavior oracle.
+- The Link Status and Vitae pages now use their authored DAT roots and localized
+ strings. AP-110 retains only packet-loss averaging and mini-game gameplay for
+ this slice; neither is replaced with an ad-hoc approximation.
diff --git a/src/AcDream.App/Rendering/GameWindow.cs b/src/AcDream.App/Rendering/GameWindow.cs
index 3589acc9..8ac731d4 100644
--- a/src/AcDream.App/Rendering/GameWindow.cs
+++ b/src/AcDream.App/Rendering/GameWindow.cs
@@ -2320,6 +2320,7 @@ public sealed class GameWindow : IDisposable
() => _liveSession?.LinkStatus
?? AcDream.Core.Net.LinkStatusSnapshot.Disconnected,
ClientTimerNow,
+ () => _liveSession?.RequestLinkStatusPing(),
() => _window?.Close()),
Toolbar: new AcDream.App.UI.ToolbarRuntimeBindings(
Objects,
diff --git a/src/AcDream.App/UI/Layout/IndicatorBarController.cs b/src/AcDream.App/UI/Layout/IndicatorBarController.cs
index ff5ab440..713e87f5 100644
--- a/src/AcDream.App/UI/Layout/IndicatorBarController.cs
+++ b/src/AcDream.App/UI/Layout/IndicatorBarController.cs
@@ -85,7 +85,10 @@ public sealed class IndicatorBarController : IRetainedPanelController
_helpful.OnClick = () => bindings.TogglePanel(RetailPanelCatalog.PositiveEffects);
_harmful.OnClick = () => bindings.TogglePanel(RetailPanelCatalog.NegativeEffects);
+ _link.OnClick = () => bindings.TogglePanel(RetailPanelCatalog.LinkStatus);
+ _vitae.OnClick = () => bindings.TogglePanel(RetailPanelCatalog.Vitae);
_burden.OnClick = () => bindings.TogglePanel(RetailPanelCatalog.Character);
+ _miniGame.OnClick = () => bindings.TogglePanel(RetailPanelCatalog.MiniGame);
_endCharacterSession.OnClick = bindings.RequestEndCharacterSession;
bindings.Spellbook.EnchantmentsChanged += UpdateEnchantments;
diff --git a/src/AcDream.App/UI/Layout/IndicatorDetailText.cs b/src/AcDream.App/UI/Layout/IndicatorDetailText.cs
new file mode 100644
index 00000000..fc613209
--- /dev/null
+++ b/src/AcDream.App/UI/Layout/IndicatorDetailText.cs
@@ -0,0 +1,40 @@
+using System;
+using System.Collections.Generic;
+
+namespace AcDream.App.UI.Layout;
+
+///
+/// Shared text shaping for retail indicator detail pages. Retail
+/// UIElement_Text::SetText reflows newline-delimited strings into the
+/// authored field width; acdream's retained text seam carries already-shaped
+/// lines, so controllers perform the equivalent shaping before publication.
+///
+internal static class IndicatorDetailText
+{
+ public static IReadOnlyList Shape(UiText target, string text)
+ {
+ ArgumentNullException.ThrowIfNull(target);
+ text ??= string.Empty;
+
+ float maxWidth = Math.Max(1f, target.Width - (2f * target.Padding));
+ float Measure(string value)
+ => target.DatFont?.MeasureWidth(value)
+ ?? target.Font?.MeasureWidth(value)
+ ?? value.Length * 8f;
+
+ var lines = new List();
+ string normalized = text.Replace("\\n", "\n", StringComparison.Ordinal);
+ foreach (string paragraph in normalized.Split('\n'))
+ {
+ if (paragraph.Length == 0)
+ {
+ lines.Add(new UiText.Line(string.Empty, target.DefaultColor));
+ continue;
+ }
+
+ foreach (string line in ChatWindowController.WrapText(paragraph, maxWidth, Measure))
+ lines.Add(new UiText.Line(line, target.DefaultColor));
+ }
+ return lines;
+ }
+}
diff --git a/src/AcDream.App/UI/Layout/LinkStatusUiController.cs b/src/AcDream.App/UI/Layout/LinkStatusUiController.cs
new file mode 100644
index 00000000..4d461fc1
--- /dev/null
+++ b/src/AcDream.App/UI/Layout/LinkStatusUiController.cs
@@ -0,0 +1,142 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using AcDream.Core.Net;
+
+namespace AcDream.App.UI.Layout;
+
+///
+/// Retained port of retail gmLinkStatusUI. LayoutDesc owns the page and
+/// chrome; this controller owns its five-second text refresh and 120-second
+/// ping cadence.
+///
+public sealed class LinkStatusUiController : IRetainedPanelController
+{
+ public const uint LayoutId = 0x2100001Du;
+ public const uint RootId = 0x10000167u;
+ public const uint MainTextId = 0x10000169u;
+ public const uint CloseId = 0x100000FCu;
+
+ private const double UpdateIntervalSeconds = 5d;
+ private const double PingIntervalSeconds = 120d;
+
+ private readonly UiText _mainText;
+ private readonly UiButton? _close;
+ private readonly Func _snapshot;
+ private readonly Func _currentTime;
+ private readonly Action _requestPing;
+ private readonly LinkStatusStrings _strings;
+ private IReadOnlyList _lines = Array.Empty();
+ private double _nextUpdateTime = -1d;
+ private double _lastPingRequestTime = -1d;
+ private bool _pleaseRequestPing;
+ private bool _visible;
+
+ private LinkStatusUiController(
+ ImportedLayout layout,
+ Func snapshot,
+ Func currentTime,
+ Action requestPing,
+ LinkStatusStrings strings,
+ Action? close)
+ {
+ _mainText = (UiText)layout.FindElement(MainTextId)!;
+ _close = layout.FindElement(CloseId) as UiButton;
+ _snapshot = snapshot;
+ _currentTime = currentTime;
+ _requestPing = requestPing;
+ _strings = strings;
+ _mainText.LinesProvider = () => _lines;
+ if (_close is not null) _close.OnClick = close;
+ }
+
+ public static LinkStatusUiController? Bind(
+ ImportedLayout layout,
+ Func snapshot,
+ Func currentTime,
+ Action requestPing,
+ LinkStatusStrings strings,
+ Action? close = null)
+ {
+ ArgumentNullException.ThrowIfNull(layout);
+ ArgumentNullException.ThrowIfNull(snapshot);
+ ArgumentNullException.ThrowIfNull(currentTime);
+ ArgumentNullException.ThrowIfNull(requestPing);
+ ArgumentNullException.ThrowIfNull(strings);
+ return layout.FindElement(MainTextId) is UiText
+ ? new LinkStatusUiController(
+ layout, snapshot, currentTime, requestPing, strings, close)
+ : null;
+ }
+
+ ///
+ /// Port of gmLinkStatusUI::ListenToGlobalMessage @ 0x004AB110.
+ ///
+ public void Tick()
+ {
+ if (!_visible) return;
+ double now = _currentTime();
+ if (!double.IsFinite(now) || now < _nextUpdateTime) return;
+ _nextUpdateTime = now + UpdateIntervalSeconds;
+ Update(now);
+ }
+
+ public void OnShown()
+ {
+ _visible = true;
+ _pleaseRequestPing = true;
+ Update(_currentTime());
+ }
+
+ public void OnHidden()
+ {
+ _visible = false;
+ _pleaseRequestPing = false;
+ }
+
+ private void Update(double now)
+ {
+ LinkStatusSnapshot value = _snapshot();
+ string ping = value.RoundTripSeconds is double seconds
+ && double.IsFinite(seconds)
+ && seconds >= 0d
+ ? (seconds * 1000d).ToString("F0", CultureInfo.CurrentCulture)
+ : "????";
+ string body = _strings.Description
+ + _strings.Legend
+ + _strings.DisconnectWarning
+ + _strings.PacketLossPrefix
+ + value.PacketLossPercentage.ToString("F2", CultureInfo.CurrentCulture)
+ + _strings.PingPrefix
+ + ping;
+ _lines = IndicatorDetailText.Shape(_mainText, body);
+
+ if (!double.IsFinite(now)) return;
+ bool periodicPing = _lastPingRequestTime >= 0d
+ && now - _lastPingRequestTime >= PingIntervalSeconds;
+ if (!_pleaseRequestPing && !periodicPing) return;
+ _lastPingRequestTime = now;
+ _pleaseRequestPing = false;
+ _requestPing();
+ }
+
+ public void Dispose()
+ {
+ if (_close is not null) _close.OnClick = null;
+ }
+}
+
+public sealed record LinkStatusStrings(
+ string Description,
+ string Legend,
+ string DisconnectWarning,
+ string PacketLossPrefix,
+ string PingPrefix)
+{
+ public static LinkStatusStrings English { get; } = new(
+ "The Link Indicator shows the current status of your connection to the game servers.",
+ "\n\nGREEN = your link is good.\nYELLOW = no packets for at least 5 sec.\nRED = no packets for at least 20 sec.",
+ "\n\nIf approximately forty seconds pass without receiving a packet, you will be disconnected from the server.",
+ "\n\n\nPacket loss for the last 10 sec: ",
+ "\n\nRoundtrip Ping time to Server: ");
+}
diff --git a/src/AcDream.App/UI/Layout/MiniGameUiController.cs b/src/AcDream.App/UI/Layout/MiniGameUiController.cs
new file mode 100644
index 00000000..6fcda3f3
--- /dev/null
+++ b/src/AcDream.App/UI/Layout/MiniGameUiController.cs
@@ -0,0 +1,35 @@
+using System;
+
+namespace AcDream.App.UI.Layout;
+
+///
+/// Authored shell for retail gmMiniGameUI. The indicator remains
+/// ghosted until a future game-state owner calls SetMiniGameActive;
+/// mounting the page now keeps its window lifecycle on the shared retail host.
+///
+public sealed class MiniGameUiController : IRetainedPanelController
+{
+ public const uint LayoutId = 0x2100001Eu;
+ public const uint RootId = 0x1000016Au;
+ public const uint CloseId = 0x1000016Bu;
+
+ private readonly UiButton _close;
+
+ private MiniGameUiController(UiButton close, Action? closeWindow)
+ {
+ _close = close;
+ _close.OnClick = closeWindow;
+ }
+
+ public static MiniGameUiController? Bind(
+ ImportedLayout layout,
+ Action? close = null)
+ {
+ ArgumentNullException.ThrowIfNull(layout);
+ return layout.FindElement(CloseId) is UiButton button
+ ? new MiniGameUiController(button, close)
+ : null;
+ }
+
+ public void Dispose() => _close.OnClick = null;
+}
diff --git a/src/AcDream.App/UI/Layout/VitaeUiController.cs b/src/AcDream.App/UI/Layout/VitaeUiController.cs
new file mode 100644
index 00000000..e647848b
--- /dev/null
+++ b/src/AcDream.App/UI/Layout/VitaeUiController.cs
@@ -0,0 +1,158 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using AcDream.Core.Items;
+using AcDream.Core.Spells;
+
+namespace AcDream.App.UI.Layout;
+
+///
+/// Retained port of retail gmVitaeUI. It presents the active vitae
+/// modifier and the authoritative experience pool needed to regain one percent.
+///
+public sealed class VitaeUiController : IRetainedPanelController
+{
+ public const uint LayoutId = 0x21000020u;
+ public const uint RootId = 0x100001C1u;
+ public const uint MainTextId = 0x100001C3u;
+ public const uint CloseId = 0x100000FCu;
+
+ internal const uint VitaeCpPoolProperty = 0x81u;
+ internal const uint DeathLevelProperty = 0x8Bu;
+ internal const uint LevelProperty = 0x19u;
+
+ private readonly Spellbook _spellbook;
+ private readonly ClientObjectTable _objects;
+ private readonly Func _playerGuid;
+ private readonly VitaeStrings _strings;
+ private readonly UiText _mainText;
+ private readonly UiButton? _close;
+ private IReadOnlyList _lines = Array.Empty();
+ private bool _disposed;
+
+ private VitaeUiController(
+ ImportedLayout layout,
+ Spellbook spellbook,
+ ClientObjectTable objects,
+ Func playerGuid,
+ VitaeStrings strings,
+ Action? close)
+ {
+ _spellbook = spellbook;
+ _objects = objects;
+ _playerGuid = playerGuid;
+ _strings = strings;
+ _mainText = (UiText)layout.FindElement(MainTextId)!;
+ _close = layout.FindElement(CloseId) as UiButton;
+ _mainText.LinesProvider = () => _lines;
+ if (_close is not null) _close.OnClick = close;
+
+ _spellbook.EnchantmentsChanged += Update;
+ _objects.ObjectAdded += OnObjectChanged;
+ _objects.ObjectUpdated += OnObjectChanged;
+ _objects.Cleared += Update;
+ Update();
+ }
+
+ public static VitaeUiController? Bind(
+ ImportedLayout layout,
+ Spellbook spellbook,
+ ClientObjectTable objects,
+ Func playerGuid,
+ VitaeStrings strings,
+ Action? close = null)
+ {
+ ArgumentNullException.ThrowIfNull(layout);
+ ArgumentNullException.ThrowIfNull(spellbook);
+ ArgumentNullException.ThrowIfNull(objects);
+ ArgumentNullException.ThrowIfNull(playerGuid);
+ ArgumentNullException.ThrowIfNull(strings);
+ return layout.FindElement(MainTextId) is UiText
+ ? new VitaeUiController(
+ layout, spellbook, objects, playerGuid, strings, close)
+ : null;
+ }
+
+ public void OnShown() => Update();
+
+ private void OnObjectChanged(ClientObject item)
+ {
+ if (item.ObjectId == _playerGuid()) Update();
+ }
+
+ private void Update()
+ {
+ float vitae = CurrentVitae();
+ int lostPercent = 100 - (int)(vitae * 100f);
+ string body;
+ if (lostPercent <= 0)
+ {
+ body = _strings.FullStrength;
+ }
+ else
+ {
+ ClientObject? player = _objects.Get(_playerGuid());
+ int pool = player?.Properties.GetInt(VitaeCpPoolProperty) ?? 0;
+ int level = 0;
+ if (player is not null)
+ {
+ level = player.Properties.Ints.TryGetValue(
+ DeathLevelProperty, out int deathLevel)
+ ? deathLevel
+ : player.Properties.GetInt(LevelProperty);
+ }
+ int remaining = VitaeCpPoolThreshold(vitae, level) - pool;
+ body = _strings.LostPrefix
+ + lostPercent
+ + _strings.LostSuffix
+ + _strings.RecoveryPrefix
+ + remaining
+ + _strings.RecoverySuffix;
+ }
+ _lines = IndicatorDetailText.Shape(_mainText, body);
+ }
+
+ private float CurrentVitae()
+ => _spellbook.ActiveEnchantmentSnapshot
+ .Where(record => record.Bucket == 4u)
+ .Select(record => record.StatModValue)
+ .OfType()
+ .Where(float.IsFinite)
+ .DefaultIfEmpty(1f)
+ .Last();
+
+ ///
+ /// Retail VitaeSystem::VitaeCPPoolThreshold @ 0x005C8FD0, with
+ /// the x87 expression cross-checked against ACE's Player_Xp port.
+ ///
+ internal static int VitaeCpPoolThreshold(float vitae, int level)
+ => (int)(((Math.Pow(level, 2.5d) * 2.5d) + 20d)
+ * Math.Pow(vitae, 5d)
+ + 0.5d);
+
+ public void Dispose()
+ {
+ if (_disposed) return;
+ _disposed = true;
+ _spellbook.EnchantmentsChanged -= Update;
+ _objects.ObjectAdded -= OnObjectChanged;
+ _objects.ObjectUpdated -= OnObjectChanged;
+ _objects.Cleared -= Update;
+ if (_close is not null) _close.OnClick = null;
+ }
+}
+
+public sealed record VitaeStrings(
+ string FullStrength,
+ string LostPrefix,
+ string LostSuffix,
+ string RecoveryPrefix,
+ string RecoverySuffix)
+{
+ public static VitaeStrings English { get; } = new(
+ "Your Vitae, or life force, is at full strength.",
+ "Due to your recent death, you have temporarily lost ",
+ " % of your Vitae, or life force.",
+ "\n\nYou will regain 1% of your Vitae once you earn ",
+ " more experience.");
+}
diff --git a/src/AcDream.App/UI/RetailPanelCatalog.cs b/src/AcDream.App/UI/RetailPanelCatalog.cs
index 4df67e17..b2577b15 100644
--- a/src/AcDream.App/UI/RetailPanelCatalog.cs
+++ b/src/AcDream.App/UI/RetailPanelCatalog.cs
@@ -10,16 +10,22 @@ public static class RetailPanelCatalog
public const uint PositiveEffects = 4u;
public const uint NegativeEffects = 5u;
public const uint Inventory = 7u;
+ public const uint LinkStatus = 8u;
+ public const uint MiniGame = 9u;
public const uint Character = 11u;
public const uint Magic = 13u;
+ public const uint Vitae = 15u;
private static readonly (uint PanelId, string WindowName)[] Mounted =
{
(PositiveEffects, WindowNames.PositiveEffects),
(NegativeEffects, WindowNames.NegativeEffects),
(Inventory, WindowNames.Inventory),
+ (LinkStatus, WindowNames.LinkStatus),
+ (MiniGame, WindowNames.MiniGame),
(Character, WindowNames.Character),
(Magic, WindowNames.Spellbook),
+ (Vitae, WindowNames.Vitae),
};
private static readonly (uint PanelId, string WindowName)[] Toolbar =
diff --git a/src/AcDream.App/UI/RetailUiRuntime.cs b/src/AcDream.App/UI/RetailUiRuntime.cs
index 2ab6d167..fcf8e198 100644
--- a/src/AcDream.App/UI/RetailUiRuntime.cs
+++ b/src/AcDream.App/UI/RetailUiRuntime.cs
@@ -81,6 +81,7 @@ public sealed record IndicatorRuntimeBindings(
Func Strength,
Func LinkStatus,
Func CurrentTime,
+ Action RequestLinkStatusPing,
Action EndCharacterSession);
public sealed record ToolbarRuntimeBindings(
@@ -196,6 +197,7 @@ public sealed class RetailUiRuntime : IDisposable
MountCombat();
MountSpellbook();
MountEffects();
+ MountIndicatorDetailPanels();
MountIndicators();
MountJumpPowerbar();
MountDialogFactory();
@@ -245,6 +247,9 @@ public sealed class RetailUiRuntime : IDisposable
public SpellbookWindowController? SpellbookWindowController { get; private set; }
public EffectsUiController? PositiveEffectsController { get; private set; }
public EffectsUiController? NegativeEffectsController { get; private set; }
+ public LinkStatusUiController? LinkStatusUiController { get; private set; }
+ public VitaeUiController? VitaeUiController { get; private set; }
+ public MiniGameUiController? MiniGameUiController { get; private set; }
public IndicatorBarController? IndicatorBarController { get; private set; }
public JumpPowerbarController? JumpPowerbarController { get; private set; }
public RetailFpsController? FpsController { get; private set; }
@@ -274,6 +279,7 @@ public sealed class RetailUiRuntime : IDisposable
SpellcastingUiController?.Tick();
PositiveEffectsController?.Tick();
NegativeEffectsController?.Tick();
+ LinkStatusUiController?.Tick();
IndicatorBarController?.Tick();
JumpPowerbarController?.Tick();
SelectedObjectController?.Tick(deltaSeconds);
@@ -880,7 +886,7 @@ public sealed class RetailUiRuntime : IDisposable
if (positive) PositiveEffectsController = controller;
else NegativeEffectsController = controller;
UiElement root = layout.Root;
- RetailWindowFrame.Mount(
+ RetailWindowHandle handle = RetailWindowFrame.Mount(
Host.Root,
root,
_bindings.Assets.ResolveSprite,
@@ -898,10 +904,184 @@ public sealed class RetailUiRuntime : IDisposable
ContentClickThrough = false,
Controller = controller,
});
- _panelUi.Register(
+ _panelUi.RegisterMainPanel(
positive ? RetailPanelCatalog.PositiveEffects : RetailPanelCatalog.NegativeEffects,
positive ? WindowNames.PositiveEffects : WindowNames.NegativeEffects,
- rootInfo);
+ handle,
+ rootInfo.TryGetEffectiveBool(
+ RetailPanelUiController.RestorePreviousPropertyId,
+ out bool restorePrevious)
+ && restorePrevious);
+ }
+
+ private void MountIndicatorDetailPanels()
+ {
+ MountLinkStatusPanel();
+ MountVitaePanel();
+ MountMiniGamePanel();
+ }
+
+ private void MountLinkStatusPanel()
+ {
+ ElementInfo? rootInfo;
+ ImportedLayout? layout;
+ LinkStatusStrings strings;
+ lock (_bindings.Assets.DatLock)
+ {
+ rootInfo = LayoutImporter.ImportInfos(
+ _bindings.Assets.Dats,
+ LinkStatusUiController.LayoutId,
+ LinkStatusUiController.RootId);
+ var resolver = new DatStringResolver(_bindings.Assets.Dats);
+ layout = rootInfo is null
+ ? null
+ : LayoutImporter.Build(
+ rootInfo,
+ _bindings.Assets.ResolveSprite,
+ _bindings.Assets.DefaultFont,
+ _bindings.Assets.ResolveFont,
+ resolver.Resolve);
+ LinkStatusStrings fallback = LinkStatusStrings.English;
+ strings = new LinkStatusStrings(
+ resolver.Resolve(0x23000001u, 0x0D632F7Fu) ?? fallback.Description,
+ resolver.Resolve(0x23000001u, 0x038FABF3u) ?? fallback.Legend,
+ resolver.Resolve(0x23000001u, 0x05481084u) ?? fallback.DisconnectWarning,
+ resolver.Resolve(0x23000001u, 0x0CADBA93u) ?? fallback.PacketLossPrefix,
+ resolver.Resolve(0x23000001u, 0x0D6485F7u) ?? fallback.PingPrefix);
+ }
+ if (rootInfo is null || layout is null) return;
+
+ LinkStatusUiController? controller = Layout.LinkStatusUiController.Bind(
+ layout,
+ _bindings.Indicators.LinkStatus,
+ _bindings.Indicators.CurrentTime,
+ _bindings.Indicators.RequestLinkStatusPing,
+ strings,
+ () => CloseWindow(WindowNames.LinkStatus));
+ if (controller is null) return;
+ LinkStatusUiController = controller;
+ RegisterIndicatorDetailPanel(
+ RetailPanelCatalog.LinkStatus,
+ WindowNames.LinkStatus,
+ rootInfo,
+ layout.Root,
+ controller);
+ }
+
+ private void MountVitaePanel()
+ {
+ ElementInfo? rootInfo;
+ ImportedLayout? layout;
+ VitaeStrings strings;
+ lock (_bindings.Assets.DatLock)
+ {
+ rootInfo = LayoutImporter.ImportInfos(
+ _bindings.Assets.Dats,
+ VitaeUiController.LayoutId,
+ VitaeUiController.RootId);
+ var resolver = new DatStringResolver(_bindings.Assets.Dats);
+ layout = rootInfo is null
+ ? null
+ : LayoutImporter.Build(
+ rootInfo,
+ _bindings.Assets.ResolveSprite,
+ _bindings.Assets.DefaultFont,
+ _bindings.Assets.ResolveFont,
+ resolver.Resolve);
+ VitaeStrings fallback = VitaeStrings.English;
+ strings = new VitaeStrings(
+ resolver.Resolve(0x23000001u, 0x0142DDECu) ?? fallback.FullStrength,
+ resolver.Resolve(0x23000001u, 0x045D7665u, 0) ?? fallback.LostPrefix,
+ resolver.Resolve(0x23000001u, 0x045D7665u, 1) ?? fallback.LostSuffix,
+ resolver.Resolve(0x23000001u, 0x0DCD8C35u, 0) ?? fallback.RecoveryPrefix,
+ resolver.Resolve(0x23000001u, 0x0DCD8C35u, 1) ?? fallback.RecoverySuffix);
+ }
+ if (rootInfo is null || layout is null) return;
+
+ VitaeUiController? controller = Layout.VitaeUiController.Bind(
+ layout,
+ _bindings.Indicators.Spellbook,
+ _bindings.Indicators.Objects,
+ _bindings.Indicators.PlayerGuid,
+ strings,
+ () => CloseWindow(WindowNames.Vitae));
+ if (controller is null) return;
+ VitaeUiController = controller;
+ RegisterIndicatorDetailPanel(
+ RetailPanelCatalog.Vitae,
+ WindowNames.Vitae,
+ rootInfo,
+ layout.Root,
+ controller);
+ }
+
+ private void MountMiniGamePanel()
+ {
+ ElementInfo? rootInfo;
+ ImportedLayout? layout;
+ lock (_bindings.Assets.DatLock)
+ {
+ rootInfo = LayoutImporter.ImportInfos(
+ _bindings.Assets.Dats,
+ MiniGameUiController.LayoutId,
+ MiniGameUiController.RootId);
+ layout = rootInfo is null
+ ? null
+ : LayoutImporter.Build(
+ rootInfo,
+ _bindings.Assets.ResolveSprite,
+ _bindings.Assets.DefaultFont,
+ _bindings.Assets.ResolveFont,
+ new DatStringResolver(_bindings.Assets.Dats).Resolve);
+ }
+ if (rootInfo is null || layout is null) return;
+
+ MiniGameUiController? controller = Layout.MiniGameUiController.Bind(
+ layout,
+ () => CloseWindow(WindowNames.MiniGame));
+ if (controller is null) return;
+ MiniGameUiController = controller;
+ RegisterIndicatorDetailPanel(
+ RetailPanelCatalog.MiniGame,
+ WindowNames.MiniGame,
+ rootInfo,
+ layout.Root,
+ controller);
+ }
+
+ private void RegisterIndicatorDetailPanel(
+ uint panelId,
+ string windowName,
+ ElementInfo rootInfo,
+ UiElement root,
+ IRetainedPanelController controller)
+ {
+ RetailWindowHandle handle = RetailWindowFrame.Mount(
+ Host.Root,
+ root,
+ _bindings.Assets.ResolveSprite,
+ new RetailWindowFrame.Options
+ {
+ WindowName = windowName,
+ Chrome = RetailWindowChrome.Imported,
+ Left = 18f,
+ Top = 18f,
+ Visible = false,
+ Resizable = false,
+ ResizeX = false,
+ ResizeY = false,
+ ConstrainDragToParent = true,
+ ContentClickThrough = false,
+ Controller = controller,
+ });
+ _panelUi.RegisterMainPanel(
+ panelId,
+ windowName,
+ handle,
+ rootInfo.TryGetEffectiveBool(
+ RetailPanelUiController.RestorePreviousPropertyId,
+ out bool restorePrevious)
+ && restorePrevious);
}
private void MountIndicators()
diff --git a/src/AcDream.App/UI/WindowNames.cs b/src/AcDream.App/UI/WindowNames.cs
index 792cc4ad..21069b3e 100644
--- a/src/AcDream.App/UI/WindowNames.cs
+++ b/src/AcDream.App/UI/WindowNames.cs
@@ -16,4 +16,7 @@ public static class WindowNames
public const string Indicators = "indicators";
public const string PositiveEffects = "effects-positive";
public const string NegativeEffects = "effects-negative";
+ public const string LinkStatus = "link-status";
+ public const string MiniGame = "mini-game";
+ public const string Vitae = "vitae";
}
diff --git a/src/AcDream.Core.Net/LinkStatusSnapshot.cs b/src/AcDream.Core.Net/LinkStatusSnapshot.cs
index 9322c5c5..1d46cde3 100644
--- a/src/AcDream.Core.Net/LinkStatusSnapshot.cs
+++ b/src/AcDream.Core.Net/LinkStatusSnapshot.cs
@@ -7,7 +7,9 @@ namespace AcDream.Core.Net;
///
public readonly record struct LinkStatusSnapshot(
bool Connected,
- double SecondsSinceLastPacket)
+ double SecondsSinceLastPacket,
+ double PacketLossPercentage = 0d,
+ double? RoundTripSeconds = null)
{
public static LinkStatusSnapshot Disconnected => new(false, 0d);
}
diff --git a/src/AcDream.Core.Net/Messages/GameEvents.cs b/src/AcDream.Core.Net/Messages/GameEvents.cs
index 223a5d22..ac05cf2f 100644
--- a/src/AcDream.Core.Net/Messages/GameEvents.cs
+++ b/src/AcDream.Core.Net/Messages/GameEvents.cs
@@ -149,12 +149,9 @@ public static class GameEvents
// ── Pings / misc ────────────────────────────────────────────────────────
- /// 0x01EA PingResponse: echoes the client ping id.
- public static uint? ParsePingResponse(ReadOnlySpan payload)
- {
- if (payload.Length < 4) return null;
- return BinaryPrimitives.ReadUInt32LittleEndian(payload);
- }
+ /// 0x01EA PingResponse has no payload; receipt is the acknowledgement.
+ public static bool ParsePingResponse(ReadOnlySpan payload)
+ => payload.IsEmpty;
// ── Spells / magic ──────────────────────────────────────────────────────
diff --git a/src/AcDream.Core.Net/Messages/SocialActions.cs b/src/AcDream.Core.Net/Messages/SocialActions.cs
index 01c0a854..09ce74cf 100644
--- a/src/AcDream.Core.Net/Messages/SocialActions.cs
+++ b/src/AcDream.Core.Net/Messages/SocialActions.cs
@@ -30,7 +30,7 @@ public static class SocialActions
// Queries
public const uint QueryHealthOpcode = 0x01BFu; // u32 targetGuid
public const uint QueryItemManaOpcode = 0x0263u; // u32 itemGuid; zero cancels
- public const uint PingRequestOpcode = 0x01E9u; // u32 clientId
+ public const uint PingRequestOpcode = 0x01E9u; // no payload
// Fellowship
public const uint FellowshipCreateOpcode = 0x00A2u; // string16L name, bool openness, bool shareXP
@@ -71,14 +71,16 @@ public static class SocialActions
return body;
}
- /// Keepalive ping — server echoes with PingResponse (0x01EA).
- public static byte[] BuildPingRequest(uint seq, uint clientId)
+ ///
+ /// Request the empty PingResponse (0x01EA). Retail
+ /// CM_Character::Event_RequestPing @ 0x006A19A0 sends no payload.
+ ///
+ public static byte[] BuildPingRequest(uint seq)
{
- byte[] body = new byte[16];
+ byte[] body = new byte[12];
BinaryPrimitives.WriteUInt32LittleEndian(body, GameActionEnvelope);
BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(4), seq);
BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(8), PingRequestOpcode);
- BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(12), clientId);
return body;
}
diff --git a/src/AcDream.Core.Net/WorldSession.cs b/src/AcDream.Core.Net/WorldSession.cs
index e8fd9bae..b5e27ad8 100644
--- a/src/AcDream.Core.Net/WorldSession.cs
+++ b/src/AcDream.Core.Net/WorldSession.cs
@@ -529,20 +529,49 @@ public sealed class WorldSession : IDisposable
CurrentState,
Volatile.Read(ref _lastInboundPacketTicks),
Stopwatch.GetTimestamp(),
- Stopwatch.Frequency);
+ Stopwatch.Frequency,
+ PingRoundTripSeconds);
+
+ internal double? PingRoundTripSeconds
+ {
+ get
+ {
+ double value = BitConverter.Int64BitsToDouble(
+ Volatile.Read(ref _lastPingRoundTripBits));
+ return double.IsFinite(value) && value >= 0d ? value : null;
+ }
+ }
internal static LinkStatusSnapshot BuildLinkStatus(
State state,
long lastInboundPacketTicks,
long nowTicks,
- long frequency)
+ long frequency,
+ double? roundTripSeconds = null,
+ double packetLossPercentage = 0d)
{
bool connected = state is not State.Disconnected and not State.Failed;
if (!connected || frequency <= 0)
return LinkStatusSnapshot.Disconnected;
long elapsed = Math.Max(0, nowTicks - lastInboundPacketTicks);
- return new LinkStatusSnapshot(true, elapsed / (double)frequency);
+ return new LinkStatusSnapshot(
+ true,
+ elapsed / (double)frequency,
+ packetLossPercentage,
+ roundTripSeconds);
+ }
+
+ private void RecordPingResponse(long nowTicks)
+ {
+ long requestTicks = Interlocked.Exchange(ref _lastPingRequestTicks, 0L);
+ if (requestTicks <= 0L || nowTicks < requestTicks || Stopwatch.Frequency <= 0)
+ return;
+
+ double elapsed = (nowTicks - requestTicks) / (double)Stopwatch.Frequency;
+ Volatile.Write(
+ ref _lastPingRoundTripBits,
+ BitConverter.DoubleToInt64Bits(elapsed));
}
/// Movement sequence counters for outbound MoveToState/AutonomousPosition.
@@ -574,6 +603,8 @@ public sealed class WorldSession : IDisposable
private readonly NetClient _net;
private long _lastInboundPacketTicks = Stopwatch.GetTimestamp();
+ private long _lastPingRequestTicks;
+ private long _lastPingRoundTripBits = BitConverter.DoubleToInt64Bits(double.NaN);
private readonly IPEndPoint _loginEndpoint;
private readonly IPEndPoint _connectEndpoint;
private readonly FragmentAssembler _assembler = new();
@@ -656,6 +687,11 @@ public sealed class WorldSession : IDisposable
var parsed = SetTurbineChatChannels.TryParse(e.Payload.Span);
if (parsed is not null) TurbineChannelsReceived?.Invoke(parsed.Value);
});
+ GameEvents.Register(GameEventType.PingResponse, e =>
+ {
+ if (Messages.GameEvents.ParsePingResponse(e.Payload.Span))
+ RecordPingResponse(Stopwatch.GetTimestamp());
+ });
}
///
@@ -1701,6 +1737,18 @@ public sealed class WorldSession : IDisposable
SendGameAction(body);
}
+ ///
+ /// Request the round-trip sample displayed by retail gmLinkStatusUI.
+ /// The response is an empty 0x01EA GameEvent, so the session retains the
+ /// monotonic send timestamp rather than putting an invented id on the wire.
+ ///
+ public void RequestLinkStatusPing()
+ {
+ Volatile.Write(ref _lastPingRequestTicks, Stopwatch.GetTimestamp());
+ uint seq = NextGameActionSequence();
+ SendGameAction(SocialActions.BuildPingRequest(seq));
+ }
+
/// Send retail TargetedMeleeAttack (0x0008).
public void SendMeleeAttack(uint targetGuid, AttackHeight attackHeight, float powerLevel)
{
diff --git a/tests/AcDream.App.Tests/UI/Layout/FixtureLoader.cs b/tests/AcDream.App.Tests/UI/Layout/FixtureLoader.cs
index 3f5087bb..5a18205b 100644
--- a/tests/AcDream.App.Tests/UI/Layout/FixtureLoader.cs
+++ b/tests/AcDream.App.Tests/UI/Layout/FixtureLoader.cs
@@ -143,6 +143,24 @@ public static class FixtureLoader
public static ElementInfo LoadIndicatorsInfos()
=> LoadInfos("indicators_21000071.json");
+ public static ImportedLayout LoadLinkStatus()
+ => LayoutImporter.Build(LoadLinkStatusInfos(), _ => (0u, 0, 0), null);
+
+ public static ElementInfo LoadLinkStatusInfos()
+ => LoadInfos("link_status_2100001D.json");
+
+ public static ImportedLayout LoadVitae()
+ => LayoutImporter.Build(LoadVitaeInfos(), _ => (0u, 0, 0), null);
+
+ public static ElementInfo LoadVitaeInfos()
+ => LoadInfos("vitae_21000020.json");
+
+ public static ImportedLayout LoadMiniGame()
+ => LayoutImporter.Build(LoadMiniGameInfos(), _ => (0u, 0, 0), null);
+
+ public static ElementInfo LoadMiniGameInfos()
+ => LoadInfos("mini_game_2100001E.json");
+
// ── Shared loader ────────────────────────────────────────────────────────
private static AcDream.App.UI.Layout.ElementInfo LoadInfos(string fileName)
diff --git a/tests/AcDream.App.Tests/UI/Layout/IndicatorBarControllerTests.cs b/tests/AcDream.App.Tests/UI/Layout/IndicatorBarControllerTests.cs
index e83d8919..caf419a7 100644
--- a/tests/AcDream.App.Tests/UI/Layout/IndicatorBarControllerTests.cs
+++ b/tests/AcDream.App.Tests/UI/Layout/IndicatorBarControllerTests.cs
@@ -67,6 +67,8 @@ public sealed class IndicatorBarControllerTests
50u, 3u, 60f, 1u, StatModValue: 0.99f, Bucket: 4u));
Assert.True(vitae.Enabled);
Assert.Equal(UiButtonStateMachine.Normal, vitae.ActiveRetailStateId);
+ vitae.OnEvent(new UiEvent(0, vitae, UiEventType.Click));
+ Assert.Equal(RetailPanelCatalog.Vitae, h.ToggledPanels[^1]);
h.Spellbook.OnEnchantmentRemoved(3u, 50u);
Assert.False(vitae.Enabled);
@@ -164,6 +166,8 @@ public sealed class IndicatorBarControllerTests
Assert.False(miniGame.Enabled);
controller.SetMiniGameActive(true);
Assert.True(miniGame.Enabled);
+ miniGame.OnEvent(new UiEvent(0, miniGame, UiEventType.Click));
+ Assert.Equal(RetailPanelCatalog.MiniGame, h.ToggledPanels[^1]);
controller.SetMiniGameActive(false);
Assert.False(miniGame.Enabled);
@@ -171,6 +175,18 @@ public sealed class IndicatorBarControllerTests
Assert.Equal(1, h.EndSessionRequests);
}
+ [Fact]
+ public void LinkButton_DispatchesSharedLinkStatusPanel()
+ {
+ var h = CreateHarness();
+ using IndicatorBarController controller = h.Controller;
+ UiButton link = h.Button(IndicatorBarController.LinkButtonId);
+
+ link.OnEvent(new UiEvent(0, link, UiEventType.Click));
+
+ Assert.Equal([RetailPanelCatalog.LinkStatus], h.ToggledPanels);
+ }
+
private static Harness CreateHarness(int strength = 10)
{
var table = SpellTable.LoadFromReader(new StringReader(
diff --git a/tests/AcDream.App.Tests/UI/Layout/IndicatorDetailPanelControllerTests.cs b/tests/AcDream.App.Tests/UI/Layout/IndicatorDetailPanelControllerTests.cs
new file mode 100644
index 00000000..424bc5f7
--- /dev/null
+++ b/tests/AcDream.App.Tests/UI/Layout/IndicatorDetailPanelControllerTests.cs
@@ -0,0 +1,166 @@
+using AcDream.App.UI;
+using AcDream.App.UI.Layout;
+using AcDream.Core.Items;
+using AcDream.Core.Net;
+using AcDream.Core.Spells;
+
+namespace AcDream.App.Tests.UI.Layout;
+
+public sealed class IndicatorDetailPanelControllerTests
+{
+ private const uint Player = 0x50000001u;
+
+ [Fact]
+ public void AuthoredFixtures_AreRetailMainPanelPages()
+ {
+ AssertPage(
+ FixtureLoader.LoadLinkStatusInfos(),
+ LinkStatusUiController.RootId,
+ 0x1000001Du,
+ LinkStatusUiController.MainTextId,
+ LinkStatusUiController.CloseId);
+ AssertPage(
+ FixtureLoader.LoadVitaeInfos(),
+ VitaeUiController.RootId,
+ 0x10000020u,
+ VitaeUiController.MainTextId,
+ VitaeUiController.CloseId);
+ AssertPage(
+ FixtureLoader.LoadMiniGameInfos(),
+ MiniGameUiController.RootId,
+ 0x1000001Eu,
+ 0x1000016Eu,
+ MiniGameUiController.CloseId);
+
+ Assert.Equal(8u, RetailPanelCatalog.LinkStatus);
+ Assert.Equal(9u, RetailPanelCatalog.MiniGame);
+ Assert.Equal(15u, RetailPanelCatalog.Vitae);
+ }
+
+ [Fact]
+ public void LinkStatus_RequestsPingOnShow_RefreshesText_AndRepeatsAfter120Seconds()
+ {
+ ImportedLayout layout = FixtureLoader.LoadLinkStatus();
+ double now = 0d;
+ int pings = 0;
+ int closes = 0;
+ LinkStatusSnapshot snapshot = new(
+ Connected: true,
+ SecondsSinceLastPacket: 0.25d,
+ PacketLossPercentage: 1.25d,
+ RoundTripSeconds: 0.123d);
+ using LinkStatusUiController controller = LinkStatusUiController.Bind(
+ layout,
+ () => snapshot,
+ () => now,
+ () => pings++,
+ LinkStatusStrings.English,
+ () => closes++)!;
+
+ controller.OnShown();
+
+ Assert.Equal(1, pings);
+ string text = VisibleText(layout, LinkStatusUiController.MainTextId);
+ Assert.Contains("Link Indicator", text, StringComparison.Ordinal);
+ Assert.Contains("1.25", text, StringComparison.Ordinal);
+ Assert.Contains("123", text, StringComparison.Ordinal);
+
+ controller.Tick();
+ now = 119d;
+ controller.Tick();
+ Assert.Equal(1, pings);
+ now = 124d;
+ controller.Tick();
+ Assert.Equal(2, pings);
+
+ UiButton close = Assert.IsType(layout.FindElement(LinkStatusUiController.CloseId));
+ close.OnEvent(new UiEvent(0, close, UiEventType.Click));
+ Assert.Equal(1, closes);
+
+ controller.OnHidden();
+ now = 300d;
+ controller.Tick();
+ Assert.Equal(2, pings);
+ }
+
+ [Fact]
+ public void Vitae_UsesDeathLevelPoolAndRetailThresholdFormula()
+ {
+ ImportedLayout layout = FixtureLoader.LoadVitae();
+ var spellbook = new Spellbook();
+ var objects = new ClientObjectTable();
+ objects.AddOrUpdate(new ClientObject { ObjectId = Player });
+ objects.UpdateIntProperty(Player, VitaeUiController.DeathLevelProperty, 10);
+ objects.UpdateIntProperty(Player, VitaeUiController.VitaeCpPoolProperty, 100);
+ using VitaeUiController controller = VitaeUiController.Bind(
+ layout,
+ spellbook,
+ objects,
+ () => Player,
+ VitaeStrings.English)!;
+
+ Assert.Contains(
+ "full strength",
+ VisibleText(layout, VitaeUiController.MainTextId),
+ StringComparison.Ordinal);
+
+ spellbook.OnEnchantmentAdded(new ActiveEnchantmentRecord(
+ SpellId: 666u,
+ LayerId: 1u,
+ Duration: -1f,
+ CasterGuid: Player,
+ StatModValue: 0.9f,
+ Bucket: 4u));
+
+ string text = VisibleText(layout, VitaeUiController.MainTextId);
+ Assert.Contains("lost 10 %", text, StringComparison.Ordinal);
+ Assert.Contains("earn 379 more experience", text, StringComparison.Ordinal);
+ Assert.Equal(479, VitaeUiController.VitaeCpPoolThreshold(0.9f, 10));
+ }
+
+ [Fact]
+ public void MiniGameFixture_BindsItsAuthoredCloseButton()
+ {
+ ImportedLayout layout = FixtureLoader.LoadMiniGame();
+ int closes = 0;
+ using MiniGameUiController controller = MiniGameUiController.Bind(
+ layout, () => closes++)!;
+
+ UiButton close = Assert.IsType(layout.FindElement(MiniGameUiController.CloseId));
+ close.OnEvent(new UiEvent(0, close, UiEventType.Click));
+
+ Assert.Equal(1, closes);
+ }
+
+ private static void AssertPage(
+ ElementInfo info,
+ uint rootId,
+ uint type,
+ uint contentId,
+ uint closeId)
+ {
+ Assert.Equal(rootId, info.Id);
+ Assert.Equal(type, info.Type);
+ Assert.Equal((300f, 362f), (info.Width, info.Height));
+ Assert.NotNull(Find(info, contentId));
+ Assert.NotNull(Find(info, closeId));
+ Assert.True(info.TryGetEffectiveBool(
+ RetailPanelUiController.RestorePreviousPropertyId,
+ out bool restorePrevious));
+ Assert.True(restorePrevious);
+ }
+
+ private static string VisibleText(ImportedLayout layout, uint id)
+ {
+ UiText text = Assert.IsType(layout.FindElement(id));
+ return string.Join(' ', text.LinesProvider().Select(line => line.Text));
+ }
+
+ private static ElementInfo? Find(ElementInfo root, uint id)
+ {
+ if (root.Id == id) return root;
+ foreach (ElementInfo child in root.Children)
+ if (Find(child, id) is { } found) return found;
+ return null;
+ }
+}
diff --git a/tests/AcDream.App.Tests/UI/Layout/RetailLayoutFixtureGenerator.cs b/tests/AcDream.App.Tests/UI/Layout/RetailLayoutFixtureGenerator.cs
index 4ed18528..aa33afa5 100644
--- a/tests/AcDream.App.Tests/UI/Layout/RetailLayoutFixtureGenerator.cs
+++ b/tests/AcDream.App.Tests/UI/Layout/RetailLayoutFixtureGenerator.cs
@@ -18,6 +18,8 @@ public sealed class RetailLayoutFixtureGenerator
{
(0x21000006u, "chat_21000006.json"),
(0x21000016u, "toolbar_21000016.json"),
+ (0x2100001Du, "link_status_2100001D.json"),
+ (0x21000020u, "vitae_21000020.json"),
(0x21000023u, "inventory_21000023.json"),
(0x21000024u, "paperdoll_21000024.json"),
(0x2100002Eu, "character_2100002E.json"),
@@ -118,6 +120,17 @@ public sealed class RetailLayoutFixtureGenerator
File.WriteAllText(Path.Combine(FixtureDirectory(), fileName), effectsJson);
}
+ ElementInfo? miniGame = LayoutImporter.ImportInfos(
+ dats, MiniGameUiController.LayoutId, MiniGameUiController.RootId);
+ Assert.NotNull(miniGame);
+ File.WriteAllText(
+ Path.Combine(FixtureDirectory(), "mini_game_2100001E.json"),
+ JsonSerializer.Serialize(miniGame, new JsonSerializerOptions
+ {
+ IncludeFields = true,
+ WriteIndented = true,
+ }));
+
foreach ((uint rootId, string fileName) in new[]
{
(0x10000466u,
diff --git a/tests/AcDream.App.Tests/UI/Layout/RetailPanelUiControllerTests.cs b/tests/AcDream.App.Tests/UI/Layout/RetailPanelUiControllerTests.cs
index 62114556..23bd81c9 100644
--- a/tests/AcDream.App.Tests/UI/Layout/RetailPanelUiControllerTests.cs
+++ b/tests/AcDream.App.Tests/UI/Layout/RetailPanelUiControllerTests.cs
@@ -113,7 +113,7 @@ public sealed class RetailPanelUiControllerTests
}
[Fact]
- public void RestorePreviousEffect_DoesNotReplaceMainPanelPlacement()
+ public void IndicatorDetailPanel_SharesPlacementAndRestoresPreviousPanel()
{
var root = new UiRoot { Width = 1280f, Height = 720f };
RetailWindowHandle inventory = Mount(root, WindowNames.Inventory, 40f, 60f);
@@ -122,13 +122,17 @@ public sealed class RetailPanelUiControllerTests
using var controller = Create(root);
controller.RegisterMainPanel(7u, WindowNames.Inventory, inventory);
controller.RegisterMainPanel(11u, WindowNames.Character, character);
- controller.Register(4u, WindowNames.PositiveEffects, restorePrevious: true);
+ controller.RegisterMainPanel(
+ 4u,
+ WindowNames.PositiveEffects,
+ effect,
+ restorePrevious: true);
controller.SetPanelVisibility(7u, visible: true);
inventory.MoveTo(280f, 120f);
controller.SetPanelVisibility(4u, visible: true);
- Assert.Equal((900f, 30f), (effect.Left, effect.Top));
+ Assert.Equal((280f, 120f), (effect.Left, effect.Top));
Assert.False(inventory.IsVisible);
Assert.True(effect.IsVisible);
diff --git a/tests/AcDream.App.Tests/UI/Layout/fixtures/link_status_2100001D.json b/tests/AcDream.App.Tests/UI/Layout/fixtures/link_status_2100001D.json
new file mode 100644
index 00000000..4fcf41c2
--- /dev/null
+++ b/tests/AcDream.App.Tests/UI/Layout/fixtures/link_status_2100001D.json
@@ -0,0 +1,1059 @@
+{
+ "Id": 268435815,
+ "Type": 268435485,
+ "X": 0,
+ "Y": 0,
+ "Width": 300,
+ "Height": 362,
+ "OriginalParentWidth": 0,
+ "OriginalParentHeight": 0,
+ "HasOriginalParentSize": false,
+ "Left": 2,
+ "Top": 1,
+ "Right": 1,
+ "Bottom": 1,
+ "ReadOrder": 0,
+ "ZLevel": 0,
+ "States": {
+ "4294967295": {
+ "Id": 4294967295,
+ "Name": "",
+ "PassToChildren": false,
+ "IncorporationFlags": 31,
+ "Image": null,
+ "Cursor": null,
+ "Properties": {
+ "Values": {
+ "88": {
+ "Kind": 0,
+ "MasterPropertyId": 88,
+ "UnsignedValue": 1,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ },
+ "87": {
+ "Kind": 0,
+ "MasterPropertyId": 87,
+ "UnsignedValue": 268435465,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ },
+ "268435529": {
+ "Kind": 1,
+ "MasterPropertyId": 268435529,
+ "UnsignedValue": 0,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": true,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ }
+ }
+ }
+ }
+ },
+ "DefaultStateId": 0,
+ "FontDid": 0,
+ "HJustify": 1,
+ "VJustify": 1,
+ "FontColor": null,
+ "StateMedia": {},
+ "StateCursors": {},
+ "DefaultStateName": "",
+ "Children": [
+ {
+ "Id": 268435816,
+ "Type": 3,
+ "X": 0,
+ "Y": 25,
+ "Width": 300,
+ "Height": 337,
+ "OriginalParentWidth": 300,
+ "OriginalParentHeight": 362,
+ "HasOriginalParentSize": true,
+ "Left": 1,
+ "Top": 1,
+ "Right": 1,
+ "Bottom": 1,
+ "ReadOrder": 3,
+ "ZLevel": 0,
+ "States": {
+ "4294967295": {
+ "Id": 4294967295,
+ "Name": "",
+ "PassToChildren": false,
+ "IncorporationFlags": 31,
+ "Image": {
+ "File": 100682946,
+ "DrawMode": 1
+ },
+ "Cursor": null,
+ "Properties": {
+ "Values": {}
+ }
+ }
+ },
+ "DefaultStateId": 0,
+ "FontDid": 0,
+ "HJustify": 1,
+ "VJustify": 1,
+ "FontColor": null,
+ "StateMedia": {
+ "": {
+ "Item1": 100682946,
+ "Item2": 1
+ }
+ },
+ "StateCursors": {},
+ "DefaultStateName": "",
+ "Children": [
+ {
+ "Id": 268435817,
+ "Type": 12,
+ "X": 8,
+ "Y": 0,
+ "Width": 284,
+ "Height": 337,
+ "OriginalParentWidth": 300,
+ "OriginalParentHeight": 337,
+ "HasOriginalParentSize": true,
+ "Left": 0,
+ "Top": 0,
+ "Right": 0,
+ "Bottom": 0,
+ "ReadOrder": 1,
+ "ZLevel": 0,
+ "States": {
+ "4294967295": {
+ "Id": 4294967295,
+ "Name": "",
+ "PassToChildren": false,
+ "IncorporationFlags": 30,
+ "Image": null,
+ "Cursor": null,
+ "Properties": {
+ "Values": {
+ "35": {
+ "Kind": 4,
+ "MasterPropertyId": 35,
+ "UnsignedValue": 0,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ },
+ "26": {
+ "Kind": 7,
+ "MasterPropertyId": 26,
+ "UnsignedValue": 0,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [
+ {
+ "Kind": 2,
+ "MasterPropertyId": 24,
+ "UnsignedValue": 1073741825,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ }
+ ],
+ "StructValue": {}
+ },
+ "37": {
+ "Kind": 4,
+ "MasterPropertyId": 37,
+ "UnsignedValue": 0,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ },
+ "27": {
+ "Kind": 7,
+ "MasterPropertyId": 27,
+ "UnsignedValue": 0,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [
+ {
+ "Kind": 6,
+ "MasterPropertyId": 25,
+ "UnsignedValue": 0,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 255,
+ "Green": 255,
+ "Red": 255,
+ "Alpha": 255
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ }
+ ],
+ "StructValue": {}
+ },
+ "20": {
+ "Kind": 0,
+ "MasterPropertyId": 20,
+ "UnsignedValue": 1,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ },
+ "21": {
+ "Kind": 0,
+ "MasterPropertyId": 21,
+ "UnsignedValue": 1,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ }
+ }
+ }
+ },
+ "13": {
+ "Id": 13,
+ "Name": "Ghosted",
+ "PassToChildren": false,
+ "IncorporationFlags": 0,
+ "Image": null,
+ "Cursor": null,
+ "Properties": {
+ "Values": {
+ "27": {
+ "Kind": 7,
+ "MasterPropertyId": 27,
+ "UnsignedValue": 0,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [
+ {
+ "Kind": 6,
+ "MasterPropertyId": 25,
+ "UnsignedValue": 0,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 76,
+ "Green": 76,
+ "Red": 76,
+ "Alpha": 255
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ }
+ ],
+ "StructValue": {}
+ }
+ }
+ }
+ }
+ },
+ "DefaultStateId": 0,
+ "FontDid": 1073741825,
+ "HJustify": 1,
+ "VJustify": 1,
+ "FontColor": {
+ "X": 1,
+ "Y": 1,
+ "Z": 1,
+ "W": 1
+ },
+ "StateMedia": {},
+ "StateCursors": {},
+ "DefaultStateName": "",
+ "Children": []
+ }
+ ]
+ },
+ {
+ "Id": 268435708,
+ "Type": 1,
+ "X": 276,
+ "Y": 0,
+ "Width": 24,
+ "Height": 25,
+ "OriginalParentWidth": 300,
+ "OriginalParentHeight": 362,
+ "HasOriginalParentSize": true,
+ "Left": 1,
+ "Top": 1,
+ "Right": 1,
+ "Bottom": 2,
+ "ReadOrder": 1,
+ "ZLevel": 0,
+ "States": {
+ "4294967295": {
+ "Id": 4294967295,
+ "Name": "",
+ "PassToChildren": false,
+ "IncorporationFlags": 30,
+ "Image": null,
+ "Cursor": null,
+ "Properties": {
+ "Values": {
+ "18": {
+ "Kind": 0,
+ "MasterPropertyId": 18,
+ "UnsignedValue": 268435465,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ }
+ }
+ }
+ },
+ "3": {
+ "Id": 3,
+ "Name": "Normal_pressed",
+ "PassToChildren": false,
+ "IncorporationFlags": 0,
+ "Image": {
+ "File": 100668308,
+ "DrawMode": 1
+ },
+ "Cursor": null,
+ "Properties": {
+ "Values": {}
+ }
+ },
+ "1": {
+ "Id": 1,
+ "Name": "Normal",
+ "PassToChildren": false,
+ "IncorporationFlags": 0,
+ "Image": {
+ "File": 100668307,
+ "DrawMode": 1
+ },
+ "Cursor": null,
+ "Properties": {
+ "Values": {}
+ }
+ }
+ },
+ "DefaultStateId": 1,
+ "FontDid": 0,
+ "HJustify": 1,
+ "VJustify": 1,
+ "FontColor": null,
+ "StateMedia": {
+ "Normal": {
+ "Item1": 100668307,
+ "Item2": 1
+ },
+ "Normal_pressed": {
+ "Item1": 100668308,
+ "Item2": 1
+ }
+ },
+ "StateCursors": {},
+ "DefaultStateName": "Normal",
+ "Children": []
+ },
+ {
+ "Id": 268435709,
+ "Type": 3,
+ "X": 0,
+ "Y": 0,
+ "Width": 276,
+ "Height": 25,
+ "OriginalParentWidth": 300,
+ "OriginalParentHeight": 362,
+ "HasOriginalParentSize": true,
+ "Left": 1,
+ "Top": 1,
+ "Right": 1,
+ "Bottom": 2,
+ "ReadOrder": 2,
+ "ZLevel": 0,
+ "States": {
+ "4294967295": {
+ "Id": 4294967295,
+ "Name": "",
+ "PassToChildren": false,
+ "IncorporationFlags": 31,
+ "Image": {
+ "File": 100683002,
+ "DrawMode": 3
+ },
+ "Cursor": null,
+ "Properties": {
+ "Values": {}
+ }
+ }
+ },
+ "DefaultStateId": 0,
+ "FontDid": 0,
+ "HJustify": 1,
+ "VJustify": 1,
+ "FontColor": null,
+ "StateMedia": {
+ "": {
+ "Item1": 100683002,
+ "Item2": 3
+ }
+ },
+ "StateCursors": {},
+ "DefaultStateName": "",
+ "Children": [
+ {
+ "Id": 268435710,
+ "Type": 12,
+ "X": 0,
+ "Y": 0,
+ "Width": 276,
+ "Height": 25,
+ "OriginalParentWidth": 276,
+ "OriginalParentHeight": 25,
+ "HasOriginalParentSize": true,
+ "Left": 0,
+ "Top": 0,
+ "Right": 0,
+ "Bottom": 0,
+ "ReadOrder": 1,
+ "ZLevel": 0,
+ "States": {
+ "4294967295": {
+ "Id": 4294967295,
+ "Name": "",
+ "PassToChildren": false,
+ "IncorporationFlags": 30,
+ "Image": null,
+ "Cursor": null,
+ "Properties": {
+ "Values": {
+ "35": {
+ "Kind": 4,
+ "MasterPropertyId": 35,
+ "UnsignedValue": 0,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ },
+ "26": {
+ "Kind": 7,
+ "MasterPropertyId": 26,
+ "UnsignedValue": 0,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [
+ {
+ "Kind": 2,
+ "MasterPropertyId": 24,
+ "UnsignedValue": 1073741825,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ }
+ ],
+ "StructValue": {}
+ },
+ "37": {
+ "Kind": 4,
+ "MasterPropertyId": 37,
+ "UnsignedValue": 0,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ },
+ "27": {
+ "Kind": 7,
+ "MasterPropertyId": 27,
+ "UnsignedValue": 0,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [
+ {
+ "Kind": 6,
+ "MasterPropertyId": 25,
+ "UnsignedValue": 0,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 255,
+ "Green": 255,
+ "Red": 255,
+ "Alpha": 255
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ }
+ ],
+ "StructValue": {}
+ },
+ "20": {
+ "Kind": 0,
+ "MasterPropertyId": 20,
+ "UnsignedValue": 1,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ },
+ "21": {
+ "Kind": 0,
+ "MasterPropertyId": 21,
+ "UnsignedValue": 1,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ },
+ "23": {
+ "Kind": 5,
+ "MasterPropertyId": 23,
+ "UnsignedValue": 0,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 187694290,
+ "TableId": 587202561,
+ "Override": 0,
+ "English": 1,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ }
+ }
+ }
+ },
+ "13": {
+ "Id": 13,
+ "Name": "Ghosted",
+ "PassToChildren": false,
+ "IncorporationFlags": 0,
+ "Image": null,
+ "Cursor": null,
+ "Properties": {
+ "Values": {
+ "27": {
+ "Kind": 7,
+ "MasterPropertyId": 27,
+ "UnsignedValue": 0,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [
+ {
+ "Kind": 6,
+ "MasterPropertyId": 25,
+ "UnsignedValue": 0,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 76,
+ "Green": 76,
+ "Red": 76,
+ "Alpha": 255
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ }
+ ],
+ "StructValue": {}
+ }
+ }
+ }
+ }
+ },
+ "DefaultStateId": 0,
+ "FontDid": 1073741825,
+ "HJustify": 1,
+ "VJustify": 1,
+ "FontColor": {
+ "X": 1,
+ "Y": 1,
+ "Z": 1,
+ "W": 1
+ },
+ "StateMedia": {},
+ "StateCursors": {},
+ "DefaultStateName": "",
+ "Children": []
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/tests/AcDream.App.Tests/UI/Layout/fixtures/mini_game_2100001E.json b/tests/AcDream.App.Tests/UI/Layout/fixtures/mini_game_2100001E.json
new file mode 100644
index 00000000..5c896a61
--- /dev/null
+++ b/tests/AcDream.App.Tests/UI/Layout/fixtures/mini_game_2100001E.json
@@ -0,0 +1,3402 @@
+{
+ "Id": 268435818,
+ "Type": 268435486,
+ "X": 0,
+ "Y": 0,
+ "Width": 300,
+ "Height": 362,
+ "OriginalParentWidth": 0,
+ "OriginalParentHeight": 0,
+ "HasOriginalParentSize": false,
+ "Left": 2,
+ "Top": 1,
+ "Right": 1,
+ "Bottom": 1,
+ "ReadOrder": 0,
+ "ZLevel": 1000,
+ "States": {
+ "4294967295": {
+ "Id": 4294967295,
+ "Name": "",
+ "PassToChildren": false,
+ "IncorporationFlags": 62,
+ "Image": null,
+ "Cursor": null,
+ "Properties": {
+ "Values": {
+ "88": {
+ "Kind": 0,
+ "MasterPropertyId": 88,
+ "UnsignedValue": 1,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ },
+ "87": {
+ "Kind": 0,
+ "MasterPropertyId": 87,
+ "UnsignedValue": 268435466,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ },
+ "268435529": {
+ "Kind": 1,
+ "MasterPropertyId": 268435529,
+ "UnsignedValue": 0,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": true,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ }
+ }
+ }
+ }
+ },
+ "DefaultStateId": 0,
+ "FontDid": 0,
+ "HJustify": 1,
+ "VJustify": 1,
+ "FontColor": null,
+ "StateMedia": {},
+ "StateCursors": {},
+ "DefaultStateName": "",
+ "Children": [
+ {
+ "Id": 268435819,
+ "Type": 1,
+ "X": 276,
+ "Y": 0,
+ "Width": 24,
+ "Height": 25,
+ "OriginalParentWidth": 300,
+ "OriginalParentHeight": 362,
+ "HasOriginalParentSize": true,
+ "Left": 1,
+ "Top": 1,
+ "Right": 1,
+ "Bottom": 2,
+ "ReadOrder": 1,
+ "ZLevel": 0,
+ "States": {
+ "4294967295": {
+ "Id": 4294967295,
+ "Name": "",
+ "PassToChildren": false,
+ "IncorporationFlags": 30,
+ "Image": null,
+ "Cursor": null,
+ "Properties": {
+ "Values": {
+ "18": {
+ "Kind": 0,
+ "MasterPropertyId": 18,
+ "UnsignedValue": 268435466,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ }
+ }
+ }
+ },
+ "3": {
+ "Id": 3,
+ "Name": "Normal_pressed",
+ "PassToChildren": false,
+ "IncorporationFlags": 0,
+ "Image": {
+ "File": 100668308,
+ "DrawMode": 1
+ },
+ "Cursor": null,
+ "Properties": {
+ "Values": {}
+ }
+ },
+ "1": {
+ "Id": 1,
+ "Name": "Normal",
+ "PassToChildren": false,
+ "IncorporationFlags": 0,
+ "Image": {
+ "File": 100668307,
+ "DrawMode": 1
+ },
+ "Cursor": null,
+ "Properties": {
+ "Values": {}
+ }
+ }
+ },
+ "DefaultStateId": 1,
+ "FontDid": 0,
+ "HJustify": 1,
+ "VJustify": 1,
+ "FontColor": null,
+ "StateMedia": {
+ "Normal": {
+ "Item1": 100668307,
+ "Item2": 1
+ },
+ "Normal_pressed": {
+ "Item1": 100668308,
+ "Item2": 1
+ }
+ },
+ "StateCursors": {},
+ "DefaultStateName": "Normal",
+ "Children": []
+ },
+ {
+ "Id": 268435820,
+ "Type": 3,
+ "X": 0,
+ "Y": 0,
+ "Width": 276,
+ "Height": 25,
+ "OriginalParentWidth": 300,
+ "OriginalParentHeight": 362,
+ "HasOriginalParentSize": true,
+ "Left": 1,
+ "Top": 1,
+ "Right": 1,
+ "Bottom": 2,
+ "ReadOrder": 2,
+ "ZLevel": 0,
+ "States": {
+ "4294967295": {
+ "Id": 4294967295,
+ "Name": "",
+ "PassToChildren": false,
+ "IncorporationFlags": 30,
+ "Image": {
+ "File": 100683002,
+ "DrawMode": 3
+ },
+ "Cursor": null,
+ "Properties": {
+ "Values": {}
+ }
+ }
+ },
+ "DefaultStateId": 0,
+ "FontDid": 0,
+ "HJustify": 1,
+ "VJustify": 1,
+ "FontColor": null,
+ "StateMedia": {
+ "": {
+ "Item1": 100683002,
+ "Item2": 3
+ }
+ },
+ "StateCursors": {},
+ "DefaultStateName": "",
+ "Children": [
+ {
+ "Id": 268435821,
+ "Type": 12,
+ "X": 0,
+ "Y": 0,
+ "Width": 276,
+ "Height": 25,
+ "OriginalParentWidth": 276,
+ "OriginalParentHeight": 25,
+ "HasOriginalParentSize": true,
+ "Left": 0,
+ "Top": 0,
+ "Right": 0,
+ "Bottom": 0,
+ "ReadOrder": 1,
+ "ZLevel": 0,
+ "States": {
+ "4294967295": {
+ "Id": 4294967295,
+ "Name": "",
+ "PassToChildren": false,
+ "IncorporationFlags": 30,
+ "Image": null,
+ "Cursor": null,
+ "Properties": {
+ "Values": {
+ "35": {
+ "Kind": 4,
+ "MasterPropertyId": 35,
+ "UnsignedValue": 0,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ },
+ "26": {
+ "Kind": 7,
+ "MasterPropertyId": 26,
+ "UnsignedValue": 0,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [
+ {
+ "Kind": 2,
+ "MasterPropertyId": 24,
+ "UnsignedValue": 1073741825,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ }
+ ],
+ "StructValue": {}
+ },
+ "37": {
+ "Kind": 4,
+ "MasterPropertyId": 37,
+ "UnsignedValue": 0,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ },
+ "27": {
+ "Kind": 7,
+ "MasterPropertyId": 27,
+ "UnsignedValue": 0,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [
+ {
+ "Kind": 6,
+ "MasterPropertyId": 25,
+ "UnsignedValue": 0,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 255,
+ "Green": 255,
+ "Red": 255,
+ "Alpha": 255
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ }
+ ],
+ "StructValue": {}
+ },
+ "20": {
+ "Kind": 0,
+ "MasterPropertyId": 20,
+ "UnsignedValue": 1,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ },
+ "21": {
+ "Kind": 0,
+ "MasterPropertyId": 21,
+ "UnsignedValue": 1,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ },
+ "23": {
+ "Kind": 5,
+ "MasterPropertyId": 23,
+ "UnsignedValue": 0,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 133508146,
+ "TableId": 587202561,
+ "Override": 0,
+ "English": 1,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ }
+ }
+ }
+ },
+ "13": {
+ "Id": 13,
+ "Name": "Ghosted",
+ "PassToChildren": false,
+ "IncorporationFlags": 0,
+ "Image": null,
+ "Cursor": null,
+ "Properties": {
+ "Values": {
+ "27": {
+ "Kind": 7,
+ "MasterPropertyId": 27,
+ "UnsignedValue": 0,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [
+ {
+ "Kind": 6,
+ "MasterPropertyId": 25,
+ "UnsignedValue": 0,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 76,
+ "Green": 76,
+ "Red": 76,
+ "Alpha": 255
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ }
+ ],
+ "StructValue": {}
+ }
+ }
+ }
+ }
+ },
+ "DefaultStateId": 0,
+ "FontDid": 1073741825,
+ "HJustify": 1,
+ "VJustify": 1,
+ "FontColor": {
+ "X": 1,
+ "Y": 1,
+ "Z": 1,
+ "W": 1
+ },
+ "StateMedia": {},
+ "StateCursors": {},
+ "DefaultStateName": "",
+ "Children": []
+ }
+ ]
+ },
+ {
+ "Id": 268435822,
+ "Type": 3,
+ "X": 0,
+ "Y": 25,
+ "Width": 300,
+ "Height": 337,
+ "OriginalParentWidth": 300,
+ "OriginalParentHeight": 362,
+ "HasOriginalParentSize": true,
+ "Left": 1,
+ "Top": 1,
+ "Right": 1,
+ "Bottom": 1,
+ "ReadOrder": 3,
+ "ZLevel": 0,
+ "States": {
+ "4294967295": {
+ "Id": 4294967295,
+ "Name": "",
+ "PassToChildren": false,
+ "IncorporationFlags": 30,
+ "Image": {
+ "File": 100668312,
+ "DrawMode": 1
+ },
+ "Cursor": null,
+ "Properties": {
+ "Values": {}
+ }
+ }
+ },
+ "DefaultStateId": 0,
+ "FontDid": 0,
+ "HJustify": 1,
+ "VJustify": 1,
+ "FontColor": null,
+ "StateMedia": {
+ "": {
+ "Item1": 100668312,
+ "Item2": 1
+ }
+ },
+ "StateCursors": {},
+ "DefaultStateName": "",
+ "Children": [
+ {
+ "Id": 268435829,
+ "Type": 1,
+ "X": 5,
+ "Y": 300,
+ "Width": 94,
+ "Height": 22,
+ "OriginalParentWidth": 300,
+ "OriginalParentHeight": 337,
+ "HasOriginalParentSize": true,
+ "Left": 1,
+ "Top": 1,
+ "Right": 1,
+ "Bottom": 2,
+ "ReadOrder": 2,
+ "ZLevel": 0,
+ "States": {
+ "4294967295": {
+ "Id": 4294967295,
+ "Name": "",
+ "PassToChildren": false,
+ "IncorporationFlags": 30,
+ "Image": null,
+ "Cursor": null,
+ "Properties": {
+ "Values": {
+ "35": {
+ "Kind": 4,
+ "MasterPropertyId": 35,
+ "UnsignedValue": 0,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ },
+ "26": {
+ "Kind": 7,
+ "MasterPropertyId": 26,
+ "UnsignedValue": 0,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [
+ {
+ "Kind": 2,
+ "MasterPropertyId": 24,
+ "UnsignedValue": 1073741824,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ }
+ ],
+ "StructValue": {}
+ },
+ "37": {
+ "Kind": 4,
+ "MasterPropertyId": 37,
+ "UnsignedValue": 0,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ },
+ "27": {
+ "Kind": 7,
+ "MasterPropertyId": 27,
+ "UnsignedValue": 0,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [
+ {
+ "Kind": 6,
+ "MasterPropertyId": 25,
+ "UnsignedValue": 0,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 255,
+ "Green": 255,
+ "Red": 255,
+ "Alpha": 255
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ }
+ ],
+ "StructValue": {}
+ },
+ "20": {
+ "Kind": 0,
+ "MasterPropertyId": 20,
+ "UnsignedValue": 1,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ },
+ "21": {
+ "Kind": 0,
+ "MasterPropertyId": 21,
+ "UnsignedValue": 1,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ },
+ "23": {
+ "Kind": 5,
+ "MasterPropertyId": 23,
+ "UnsignedValue": 0,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 76273774,
+ "TableId": 587202561,
+ "Override": 0,
+ "English": 1,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ }
+ }
+ }
+ },
+ "3": {
+ "Id": 3,
+ "Name": "Normal_pressed",
+ "PassToChildren": false,
+ "IncorporationFlags": 0,
+ "Image": {
+ "File": 100672325,
+ "DrawMode": 1
+ },
+ "Cursor": null,
+ "Properties": {
+ "Values": {
+ "35": {
+ "Kind": 4,
+ "MasterPropertyId": 35,
+ "UnsignedValue": 0,
+ "IntegerValue": 1,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ },
+ "37": {
+ "Kind": 4,
+ "MasterPropertyId": 37,
+ "UnsignedValue": 0,
+ "IntegerValue": 1,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ }
+ }
+ }
+ },
+ "1": {
+ "Id": 1,
+ "Name": "Normal",
+ "PassToChildren": false,
+ "IncorporationFlags": 0,
+ "Image": {
+ "File": 100672324,
+ "DrawMode": 1
+ },
+ "Cursor": null,
+ "Properties": {
+ "Values": {
+ "35": {
+ "Kind": 4,
+ "MasterPropertyId": 35,
+ "UnsignedValue": 0,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ },
+ "37": {
+ "Kind": 4,
+ "MasterPropertyId": 37,
+ "UnsignedValue": 0,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ }
+ }
+ }
+ },
+ "6": {
+ "Id": 6,
+ "Name": "Highlight",
+ "PassToChildren": false,
+ "IncorporationFlags": 0,
+ "Image": null,
+ "Cursor": null,
+ "Properties": {
+ "Values": {
+ "35": {
+ "Kind": 4,
+ "MasterPropertyId": 35,
+ "UnsignedValue": 0,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ },
+ "37": {
+ "Kind": 4,
+ "MasterPropertyId": 37,
+ "UnsignedValue": 0,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ }
+ }
+ }
+ },
+ "8": {
+ "Id": 8,
+ "Name": "Highlight_pressed",
+ "PassToChildren": false,
+ "IncorporationFlags": 0,
+ "Image": null,
+ "Cursor": null,
+ "Properties": {
+ "Values": {
+ "35": {
+ "Kind": 4,
+ "MasterPropertyId": 35,
+ "UnsignedValue": 0,
+ "IntegerValue": 1,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ },
+ "37": {
+ "Kind": 4,
+ "MasterPropertyId": 37,
+ "UnsignedValue": 0,
+ "IntegerValue": 1,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ }
+ }
+ }
+ },
+ "13": {
+ "Id": 13,
+ "Name": "Ghosted",
+ "PassToChildren": false,
+ "IncorporationFlags": 0,
+ "Image": {
+ "File": 100672326,
+ "DrawMode": 1
+ },
+ "Cursor": null,
+ "Properties": {
+ "Values": {}
+ }
+ }
+ },
+ "DefaultStateId": 1,
+ "FontDid": 1073741824,
+ "HJustify": 1,
+ "VJustify": 1,
+ "FontColor": {
+ "X": 1,
+ "Y": 1,
+ "Z": 1,
+ "W": 1
+ },
+ "StateMedia": {
+ "Normal": {
+ "Item1": 100672324,
+ "Item2": 1
+ },
+ "Normal_pressed": {
+ "Item1": 100672325,
+ "Item2": 1
+ },
+ "Ghosted": {
+ "Item1": 100672326,
+ "Item2": 1
+ }
+ },
+ "StateCursors": {},
+ "DefaultStateName": "Normal",
+ "Children": []
+ },
+ {
+ "Id": 268435830,
+ "Type": 1,
+ "X": 103,
+ "Y": 300,
+ "Width": 94,
+ "Height": 22,
+ "OriginalParentWidth": 300,
+ "OriginalParentHeight": 337,
+ "HasOriginalParentSize": true,
+ "Left": 1,
+ "Top": 1,
+ "Right": 1,
+ "Bottom": 2,
+ "ReadOrder": 3,
+ "ZLevel": 0,
+ "States": {
+ "4294967295": {
+ "Id": 4294967295,
+ "Name": "",
+ "PassToChildren": false,
+ "IncorporationFlags": 30,
+ "Image": null,
+ "Cursor": null,
+ "Properties": {
+ "Values": {
+ "35": {
+ "Kind": 4,
+ "MasterPropertyId": 35,
+ "UnsignedValue": 0,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ },
+ "26": {
+ "Kind": 7,
+ "MasterPropertyId": 26,
+ "UnsignedValue": 0,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [
+ {
+ "Kind": 2,
+ "MasterPropertyId": 24,
+ "UnsignedValue": 1073741824,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ }
+ ],
+ "StructValue": {}
+ },
+ "37": {
+ "Kind": 4,
+ "MasterPropertyId": 37,
+ "UnsignedValue": 0,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ },
+ "27": {
+ "Kind": 7,
+ "MasterPropertyId": 27,
+ "UnsignedValue": 0,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [
+ {
+ "Kind": 6,
+ "MasterPropertyId": 25,
+ "UnsignedValue": 0,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 255,
+ "Green": 255,
+ "Red": 255,
+ "Alpha": 255
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ }
+ ],
+ "StructValue": {}
+ },
+ "20": {
+ "Kind": 0,
+ "MasterPropertyId": 20,
+ "UnsignedValue": 1,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ },
+ "21": {
+ "Kind": 0,
+ "MasterPropertyId": 21,
+ "UnsignedValue": 1,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ },
+ "23": {
+ "Kind": 5,
+ "MasterPropertyId": 23,
+ "UnsignedValue": 0,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 129265635,
+ "TableId": 587202561,
+ "Override": 0,
+ "English": 1,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ }
+ }
+ }
+ },
+ "3": {
+ "Id": 3,
+ "Name": "Normal_pressed",
+ "PassToChildren": false,
+ "IncorporationFlags": 0,
+ "Image": {
+ "File": 100672325,
+ "DrawMode": 1
+ },
+ "Cursor": null,
+ "Properties": {
+ "Values": {
+ "35": {
+ "Kind": 4,
+ "MasterPropertyId": 35,
+ "UnsignedValue": 0,
+ "IntegerValue": 1,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ },
+ "37": {
+ "Kind": 4,
+ "MasterPropertyId": 37,
+ "UnsignedValue": 0,
+ "IntegerValue": 1,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ }
+ }
+ }
+ },
+ "1": {
+ "Id": 1,
+ "Name": "Normal",
+ "PassToChildren": false,
+ "IncorporationFlags": 0,
+ "Image": {
+ "File": 100672324,
+ "DrawMode": 1
+ },
+ "Cursor": null,
+ "Properties": {
+ "Values": {
+ "35": {
+ "Kind": 4,
+ "MasterPropertyId": 35,
+ "UnsignedValue": 0,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ },
+ "37": {
+ "Kind": 4,
+ "MasterPropertyId": 37,
+ "UnsignedValue": 0,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ }
+ }
+ }
+ },
+ "6": {
+ "Id": 6,
+ "Name": "Highlight",
+ "PassToChildren": false,
+ "IncorporationFlags": 0,
+ "Image": null,
+ "Cursor": null,
+ "Properties": {
+ "Values": {
+ "35": {
+ "Kind": 4,
+ "MasterPropertyId": 35,
+ "UnsignedValue": 0,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ },
+ "37": {
+ "Kind": 4,
+ "MasterPropertyId": 37,
+ "UnsignedValue": 0,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ }
+ }
+ }
+ },
+ "8": {
+ "Id": 8,
+ "Name": "Highlight_pressed",
+ "PassToChildren": false,
+ "IncorporationFlags": 0,
+ "Image": null,
+ "Cursor": null,
+ "Properties": {
+ "Values": {
+ "35": {
+ "Kind": 4,
+ "MasterPropertyId": 35,
+ "UnsignedValue": 0,
+ "IntegerValue": 1,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ },
+ "37": {
+ "Kind": 4,
+ "MasterPropertyId": 37,
+ "UnsignedValue": 0,
+ "IntegerValue": 1,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ }
+ }
+ }
+ },
+ "13": {
+ "Id": 13,
+ "Name": "Ghosted",
+ "PassToChildren": false,
+ "IncorporationFlags": 0,
+ "Image": {
+ "File": 100672326,
+ "DrawMode": 1
+ },
+ "Cursor": null,
+ "Properties": {
+ "Values": {}
+ }
+ }
+ },
+ "DefaultStateId": 1,
+ "FontDid": 1073741824,
+ "HJustify": 1,
+ "VJustify": 1,
+ "FontColor": {
+ "X": 1,
+ "Y": 1,
+ "Z": 1,
+ "W": 1
+ },
+ "StateMedia": {
+ "Normal": {
+ "Item1": 100672324,
+ "Item2": 1
+ },
+ "Normal_pressed": {
+ "Item1": 100672325,
+ "Item2": 1
+ },
+ "Ghosted": {
+ "Item1": 100672326,
+ "Item2": 1
+ }
+ },
+ "StateCursors": {},
+ "DefaultStateName": "Normal",
+ "Children": []
+ },
+ {
+ "Id": 268435831,
+ "Type": 1,
+ "X": 201,
+ "Y": 300,
+ "Width": 94,
+ "Height": 22,
+ "OriginalParentWidth": 300,
+ "OriginalParentHeight": 337,
+ "HasOriginalParentSize": true,
+ "Left": 1,
+ "Top": 1,
+ "Right": 1,
+ "Bottom": 2,
+ "ReadOrder": 4,
+ "ZLevel": 0,
+ "States": {
+ "4294967295": {
+ "Id": 4294967295,
+ "Name": "",
+ "PassToChildren": false,
+ "IncorporationFlags": 30,
+ "Image": null,
+ "Cursor": null,
+ "Properties": {
+ "Values": {
+ "35": {
+ "Kind": 4,
+ "MasterPropertyId": 35,
+ "UnsignedValue": 0,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ },
+ "26": {
+ "Kind": 7,
+ "MasterPropertyId": 26,
+ "UnsignedValue": 0,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [
+ {
+ "Kind": 2,
+ "MasterPropertyId": 24,
+ "UnsignedValue": 1073741824,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ }
+ ],
+ "StructValue": {}
+ },
+ "37": {
+ "Kind": 4,
+ "MasterPropertyId": 37,
+ "UnsignedValue": 0,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ },
+ "27": {
+ "Kind": 7,
+ "MasterPropertyId": 27,
+ "UnsignedValue": 0,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [
+ {
+ "Kind": 6,
+ "MasterPropertyId": 25,
+ "UnsignedValue": 0,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 255,
+ "Green": 255,
+ "Red": 255,
+ "Alpha": 255
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ }
+ ],
+ "StructValue": {}
+ },
+ "20": {
+ "Kind": 0,
+ "MasterPropertyId": 20,
+ "UnsignedValue": 1,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ },
+ "21": {
+ "Kind": 0,
+ "MasterPropertyId": 21,
+ "UnsignedValue": 1,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ },
+ "23": {
+ "Kind": 5,
+ "MasterPropertyId": 23,
+ "UnsignedValue": 0,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 72446581,
+ "TableId": 587202561,
+ "Override": 0,
+ "English": 1,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ }
+ }
+ }
+ },
+ "3": {
+ "Id": 3,
+ "Name": "Normal_pressed",
+ "PassToChildren": false,
+ "IncorporationFlags": 0,
+ "Image": {
+ "File": 100672325,
+ "DrawMode": 1
+ },
+ "Cursor": null,
+ "Properties": {
+ "Values": {
+ "35": {
+ "Kind": 4,
+ "MasterPropertyId": 35,
+ "UnsignedValue": 0,
+ "IntegerValue": 1,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ },
+ "37": {
+ "Kind": 4,
+ "MasterPropertyId": 37,
+ "UnsignedValue": 0,
+ "IntegerValue": 1,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ }
+ }
+ }
+ },
+ "1": {
+ "Id": 1,
+ "Name": "Normal",
+ "PassToChildren": false,
+ "IncorporationFlags": 0,
+ "Image": {
+ "File": 100672324,
+ "DrawMode": 1
+ },
+ "Cursor": null,
+ "Properties": {
+ "Values": {
+ "35": {
+ "Kind": 4,
+ "MasterPropertyId": 35,
+ "UnsignedValue": 0,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ },
+ "37": {
+ "Kind": 4,
+ "MasterPropertyId": 37,
+ "UnsignedValue": 0,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ }
+ }
+ }
+ },
+ "6": {
+ "Id": 6,
+ "Name": "Highlight",
+ "PassToChildren": false,
+ "IncorporationFlags": 0,
+ "Image": null,
+ "Cursor": null,
+ "Properties": {
+ "Values": {
+ "35": {
+ "Kind": 4,
+ "MasterPropertyId": 35,
+ "UnsignedValue": 0,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ },
+ "37": {
+ "Kind": 4,
+ "MasterPropertyId": 37,
+ "UnsignedValue": 0,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ }
+ }
+ }
+ },
+ "8": {
+ "Id": 8,
+ "Name": "Highlight_pressed",
+ "PassToChildren": false,
+ "IncorporationFlags": 0,
+ "Image": null,
+ "Cursor": null,
+ "Properties": {
+ "Values": {
+ "35": {
+ "Kind": 4,
+ "MasterPropertyId": 35,
+ "UnsignedValue": 0,
+ "IntegerValue": 1,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ },
+ "37": {
+ "Kind": 4,
+ "MasterPropertyId": 37,
+ "UnsignedValue": 0,
+ "IntegerValue": 1,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ }
+ }
+ }
+ },
+ "13": {
+ "Id": 13,
+ "Name": "Ghosted",
+ "PassToChildren": false,
+ "IncorporationFlags": 0,
+ "Image": {
+ "File": 100672326,
+ "DrawMode": 1
+ },
+ "Cursor": null,
+ "Properties": {
+ "Values": {}
+ }
+ }
+ },
+ "DefaultStateId": 1,
+ "FontDid": 1073741824,
+ "HJustify": 1,
+ "VJustify": 1,
+ "FontColor": {
+ "X": 1,
+ "Y": 1,
+ "Z": 1,
+ "W": 1
+ },
+ "StateMedia": {
+ "Normal": {
+ "Item1": 100672324,
+ "Item2": 1
+ },
+ "Normal_pressed": {
+ "Item1": 100672325,
+ "Item2": 1
+ },
+ "Ghosted": {
+ "Item1": 100672326,
+ "Item2": 1
+ }
+ },
+ "StateCursors": {},
+ "DefaultStateName": "Normal",
+ "Children": []
+ },
+ {
+ "Id": 268435823,
+ "Type": 3,
+ "X": 10,
+ "Y": 10,
+ "Width": 280,
+ "Height": 280,
+ "OriginalParentWidth": 300,
+ "OriginalParentHeight": 337,
+ "HasOriginalParentSize": true,
+ "Left": 1,
+ "Top": 1,
+ "Right": 2,
+ "Bottom": 2,
+ "ReadOrder": 1,
+ "ZLevel": 0,
+ "States": {
+ "4294967295": {
+ "Id": 4294967295,
+ "Name": "",
+ "PassToChildren": false,
+ "IncorporationFlags": 30,
+ "Image": null,
+ "Cursor": null,
+ "Properties": {
+ "Values": {}
+ }
+ }
+ },
+ "DefaultStateId": 0,
+ "FontDid": 0,
+ "HJustify": 1,
+ "VJustify": 1,
+ "FontColor": null,
+ "StateMedia": {},
+ "StateCursors": {},
+ "DefaultStateName": "",
+ "Children": [
+ {
+ "Id": 268435824,
+ "Type": 3,
+ "X": 0,
+ "Y": 0,
+ "Width": 280,
+ "Height": 12,
+ "OriginalParentWidth": 280,
+ "OriginalParentHeight": 280,
+ "HasOriginalParentSize": true,
+ "Left": 1,
+ "Top": 1,
+ "Right": 1,
+ "Bottom": 1,
+ "ReadOrder": 1,
+ "ZLevel": 0,
+ "States": {
+ "4294967295": {
+ "Id": 4294967295,
+ "Name": "",
+ "PassToChildren": false,
+ "IncorporationFlags": 30,
+ "Image": {
+ "File": 100671446,
+ "DrawMode": 1
+ },
+ "Cursor": null,
+ "Properties": {
+ "Values": {}
+ }
+ }
+ },
+ "DefaultStateId": 0,
+ "FontDid": 0,
+ "HJustify": 1,
+ "VJustify": 1,
+ "FontColor": null,
+ "StateMedia": {
+ "": {
+ "Item1": 100671446,
+ "Item2": 1
+ }
+ },
+ "StateCursors": {},
+ "DefaultStateName": "",
+ "Children": []
+ },
+ {
+ "Id": 268435825,
+ "Type": 3,
+ "X": 0,
+ "Y": 268,
+ "Width": 280,
+ "Height": 12,
+ "OriginalParentWidth": 280,
+ "OriginalParentHeight": 280,
+ "HasOriginalParentSize": true,
+ "Left": 1,
+ "Top": 1,
+ "Right": 1,
+ "Bottom": 1,
+ "ReadOrder": 2,
+ "ZLevel": 0,
+ "States": {
+ "4294967295": {
+ "Id": 4294967295,
+ "Name": "",
+ "PassToChildren": false,
+ "IncorporationFlags": 30,
+ "Image": {
+ "File": 100671447,
+ "DrawMode": 1
+ },
+ "Cursor": null,
+ "Properties": {
+ "Values": {}
+ }
+ }
+ },
+ "DefaultStateId": 0,
+ "FontDid": 0,
+ "HJustify": 1,
+ "VJustify": 1,
+ "FontColor": null,
+ "StateMedia": {
+ "": {
+ "Item1": 100671447,
+ "Item2": 1
+ }
+ },
+ "StateCursors": {},
+ "DefaultStateName": "",
+ "Children": []
+ },
+ {
+ "Id": 268435826,
+ "Type": 3,
+ "X": 0,
+ "Y": 12,
+ "Width": 12,
+ "Height": 256,
+ "OriginalParentWidth": 280,
+ "OriginalParentHeight": 280,
+ "HasOriginalParentSize": true,
+ "Left": 1,
+ "Top": 1,
+ "Right": 1,
+ "Bottom": 1,
+ "ReadOrder": 3,
+ "ZLevel": 0,
+ "States": {
+ "4294967295": {
+ "Id": 4294967295,
+ "Name": "",
+ "PassToChildren": false,
+ "IncorporationFlags": 30,
+ "Image": {
+ "File": 100671448,
+ "DrawMode": 1
+ },
+ "Cursor": null,
+ "Properties": {
+ "Values": {}
+ }
+ }
+ },
+ "DefaultStateId": 0,
+ "FontDid": 0,
+ "HJustify": 1,
+ "VJustify": 1,
+ "FontColor": null,
+ "StateMedia": {
+ "": {
+ "Item1": 100671448,
+ "Item2": 1
+ }
+ },
+ "StateCursors": {},
+ "DefaultStateName": "",
+ "Children": []
+ },
+ {
+ "Id": 268435827,
+ "Type": 3,
+ "X": 268,
+ "Y": 12,
+ "Width": 12,
+ "Height": 256,
+ "OriginalParentWidth": 280,
+ "OriginalParentHeight": 280,
+ "HasOriginalParentSize": true,
+ "Left": 1,
+ "Top": 1,
+ "Right": 1,
+ "Bottom": 1,
+ "ReadOrder": 4,
+ "ZLevel": 0,
+ "States": {
+ "4294967295": {
+ "Id": 4294967295,
+ "Name": "",
+ "PassToChildren": false,
+ "IncorporationFlags": 30,
+ "Image": {
+ "File": 100671449,
+ "DrawMode": 1
+ },
+ "Cursor": null,
+ "Properties": {
+ "Values": {}
+ }
+ }
+ },
+ "DefaultStateId": 0,
+ "FontDid": 0,
+ "HJustify": 1,
+ "VJustify": 1,
+ "FontColor": null,
+ "StateMedia": {
+ "": {
+ "Item1": 100671449,
+ "Item2": 1
+ }
+ },
+ "StateCursors": {},
+ "DefaultStateName": "",
+ "Children": []
+ },
+ {
+ "Id": 268435828,
+ "Type": 5,
+ "X": 12,
+ "Y": 12,
+ "Width": 256,
+ "Height": 256,
+ "OriginalParentWidth": 280,
+ "OriginalParentHeight": 280,
+ "HasOriginalParentSize": true,
+ "Left": 1,
+ "Top": 1,
+ "Right": 1,
+ "Bottom": 1,
+ "ReadOrder": 5,
+ "ZLevel": 0,
+ "States": {
+ "4294967295": {
+ "Id": 4294967295,
+ "Name": "",
+ "PassToChildren": false,
+ "IncorporationFlags": 30,
+ "Image": {
+ "File": 100671445,
+ "DrawMode": 1
+ },
+ "Cursor": null,
+ "Properties": {
+ "Values": {
+ "100": {
+ "Kind": 7,
+ "MasterPropertyId": 100,
+ "UnsignedValue": 0,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [
+ {
+ "Kind": 8,
+ "MasterPropertyId": 101,
+ "UnsignedValue": 0,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {
+ "99": {
+ "Kind": 2,
+ "MasterPropertyId": 99,
+ "UnsignedValue": 553648158,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ },
+ "98": {
+ "Kind": 0,
+ "MasterPropertyId": 98,
+ "UnsignedValue": 268435832,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ }
+ }
+ }
+ ],
+ "StructValue": {}
+ },
+ "268435489": {
+ "Kind": 7,
+ "MasterPropertyId": 268435489,
+ "UnsignedValue": 0,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [
+ {
+ "Kind": 2,
+ "MasterPropertyId": 268435490,
+ "UnsignedValue": 100683005,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ },
+ {
+ "Kind": 2,
+ "MasterPropertyId": 268435490,
+ "UnsignedValue": 100683006,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ },
+ {
+ "Kind": 2,
+ "MasterPropertyId": 268435490,
+ "UnsignedValue": 100683007,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ },
+ {
+ "Kind": 2,
+ "MasterPropertyId": 268435490,
+ "UnsignedValue": 100683008,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ },
+ {
+ "Kind": 2,
+ "MasterPropertyId": 268435490,
+ "UnsignedValue": 100683009,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ },
+ {
+ "Kind": 2,
+ "MasterPropertyId": 268435490,
+ "UnsignedValue": 100683010,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ },
+ {
+ "Kind": 2,
+ "MasterPropertyId": 268435490,
+ "UnsignedValue": 100683011,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ },
+ {
+ "Kind": 2,
+ "MasterPropertyId": 268435490,
+ "UnsignedValue": 100683012,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ },
+ {
+ "Kind": 2,
+ "MasterPropertyId": 268435490,
+ "UnsignedValue": 100683013,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ },
+ {
+ "Kind": 2,
+ "MasterPropertyId": 268435490,
+ "UnsignedValue": 100683014,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ },
+ {
+ "Kind": 2,
+ "MasterPropertyId": 268435490,
+ "UnsignedValue": 100683015,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ },
+ {
+ "Kind": 2,
+ "MasterPropertyId": 268435490,
+ "UnsignedValue": 100683016,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ }
+ ],
+ "StructValue": {}
+ },
+ "92": {
+ "Kind": 1,
+ "MasterPropertyId": 92,
+ "UnsignedValue": 0,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": true,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ },
+ "95": {
+ "Kind": 4,
+ "MasterPropertyId": 95,
+ "UnsignedValue": 0,
+ "IntegerValue": 8,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ }
+ }
+ }
+ }
+ },
+ "DefaultStateId": 0,
+ "FontDid": 0,
+ "HJustify": 1,
+ "VJustify": 1,
+ "FontColor": null,
+ "StateMedia": {
+ "": {
+ "Item1": 100671445,
+ "Item2": 1
+ }
+ },
+ "StateCursors": {},
+ "DefaultStateName": "",
+ "Children": []
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/tests/AcDream.App.Tests/UI/Layout/fixtures/vitae_21000020.json b/tests/AcDream.App.Tests/UI/Layout/fixtures/vitae_21000020.json
new file mode 100644
index 00000000..6443b76d
--- /dev/null
+++ b/tests/AcDream.App.Tests/UI/Layout/fixtures/vitae_21000020.json
@@ -0,0 +1,1059 @@
+{
+ "Id": 268435905,
+ "Type": 268435488,
+ "X": 0,
+ "Y": 0,
+ "Width": 300,
+ "Height": 362,
+ "OriginalParentWidth": 0,
+ "OriginalParentHeight": 0,
+ "HasOriginalParentSize": false,
+ "Left": 2,
+ "Top": 1,
+ "Right": 1,
+ "Bottom": 1,
+ "ReadOrder": 0,
+ "ZLevel": 0,
+ "States": {
+ "4294967295": {
+ "Id": 4294967295,
+ "Name": "",
+ "PassToChildren": false,
+ "IncorporationFlags": 30,
+ "Image": null,
+ "Cursor": null,
+ "Properties": {
+ "Values": {
+ "88": {
+ "Kind": 0,
+ "MasterPropertyId": 88,
+ "UnsignedValue": 1,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ },
+ "87": {
+ "Kind": 0,
+ "MasterPropertyId": 87,
+ "UnsignedValue": 268435468,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ },
+ "268435529": {
+ "Kind": 1,
+ "MasterPropertyId": 268435529,
+ "UnsignedValue": 0,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": true,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ }
+ }
+ }
+ }
+ },
+ "DefaultStateId": 0,
+ "FontDid": 0,
+ "HJustify": 1,
+ "VJustify": 1,
+ "FontColor": null,
+ "StateMedia": {},
+ "StateCursors": {},
+ "DefaultStateName": "",
+ "Children": [
+ {
+ "Id": 268435906,
+ "Type": 3,
+ "X": 0,
+ "Y": 25,
+ "Width": 300,
+ "Height": 337,
+ "OriginalParentWidth": 300,
+ "OriginalParentHeight": 362,
+ "HasOriginalParentSize": true,
+ "Left": 1,
+ "Top": 1,
+ "Right": 1,
+ "Bottom": 1,
+ "ReadOrder": 3,
+ "ZLevel": 0,
+ "States": {
+ "4294967295": {
+ "Id": 4294967295,
+ "Name": "",
+ "PassToChildren": false,
+ "IncorporationFlags": 30,
+ "Image": {
+ "File": 100682946,
+ "DrawMode": 1
+ },
+ "Cursor": null,
+ "Properties": {
+ "Values": {}
+ }
+ }
+ },
+ "DefaultStateId": 0,
+ "FontDid": 0,
+ "HJustify": 1,
+ "VJustify": 1,
+ "FontColor": null,
+ "StateMedia": {
+ "": {
+ "Item1": 100682946,
+ "Item2": 1
+ }
+ },
+ "StateCursors": {},
+ "DefaultStateName": "",
+ "Children": [
+ {
+ "Id": 268435907,
+ "Type": 12,
+ "X": 8,
+ "Y": 0,
+ "Width": 284,
+ "Height": 337,
+ "OriginalParentWidth": 300,
+ "OriginalParentHeight": 337,
+ "HasOriginalParentSize": true,
+ "Left": 0,
+ "Top": 0,
+ "Right": 0,
+ "Bottom": 0,
+ "ReadOrder": 1,
+ "ZLevel": 0,
+ "States": {
+ "4294967295": {
+ "Id": 4294967295,
+ "Name": "",
+ "PassToChildren": false,
+ "IncorporationFlags": 30,
+ "Image": null,
+ "Cursor": null,
+ "Properties": {
+ "Values": {
+ "35": {
+ "Kind": 4,
+ "MasterPropertyId": 35,
+ "UnsignedValue": 0,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ },
+ "26": {
+ "Kind": 7,
+ "MasterPropertyId": 26,
+ "UnsignedValue": 0,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [
+ {
+ "Kind": 2,
+ "MasterPropertyId": 24,
+ "UnsignedValue": 1073741825,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ }
+ ],
+ "StructValue": {}
+ },
+ "37": {
+ "Kind": 4,
+ "MasterPropertyId": 37,
+ "UnsignedValue": 0,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ },
+ "27": {
+ "Kind": 7,
+ "MasterPropertyId": 27,
+ "UnsignedValue": 0,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [
+ {
+ "Kind": 6,
+ "MasterPropertyId": 25,
+ "UnsignedValue": 0,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 255,
+ "Green": 255,
+ "Red": 255,
+ "Alpha": 255
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ }
+ ],
+ "StructValue": {}
+ },
+ "20": {
+ "Kind": 0,
+ "MasterPropertyId": 20,
+ "UnsignedValue": 1,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ },
+ "21": {
+ "Kind": 0,
+ "MasterPropertyId": 21,
+ "UnsignedValue": 1,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ }
+ }
+ }
+ },
+ "13": {
+ "Id": 13,
+ "Name": "Ghosted",
+ "PassToChildren": false,
+ "IncorporationFlags": 0,
+ "Image": null,
+ "Cursor": null,
+ "Properties": {
+ "Values": {
+ "27": {
+ "Kind": 7,
+ "MasterPropertyId": 27,
+ "UnsignedValue": 0,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [
+ {
+ "Kind": 6,
+ "MasterPropertyId": 25,
+ "UnsignedValue": 0,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 76,
+ "Green": 76,
+ "Red": 76,
+ "Alpha": 255
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ }
+ ],
+ "StructValue": {}
+ }
+ }
+ }
+ }
+ },
+ "DefaultStateId": 0,
+ "FontDid": 1073741825,
+ "HJustify": 1,
+ "VJustify": 1,
+ "FontColor": {
+ "X": 1,
+ "Y": 1,
+ "Z": 1,
+ "W": 1
+ },
+ "StateMedia": {},
+ "StateCursors": {},
+ "DefaultStateName": "",
+ "Children": []
+ }
+ ]
+ },
+ {
+ "Id": 268435708,
+ "Type": 1,
+ "X": 276,
+ "Y": 0,
+ "Width": 24,
+ "Height": 25,
+ "OriginalParentWidth": 300,
+ "OriginalParentHeight": 362,
+ "HasOriginalParentSize": true,
+ "Left": 1,
+ "Top": 1,
+ "Right": 1,
+ "Bottom": 2,
+ "ReadOrder": 1,
+ "ZLevel": 0,
+ "States": {
+ "4294967295": {
+ "Id": 4294967295,
+ "Name": "",
+ "PassToChildren": false,
+ "IncorporationFlags": 30,
+ "Image": null,
+ "Cursor": null,
+ "Properties": {
+ "Values": {
+ "18": {
+ "Kind": 0,
+ "MasterPropertyId": 18,
+ "UnsignedValue": 268435468,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ }
+ }
+ }
+ },
+ "3": {
+ "Id": 3,
+ "Name": "Normal_pressed",
+ "PassToChildren": false,
+ "IncorporationFlags": 0,
+ "Image": {
+ "File": 100668308,
+ "DrawMode": 1
+ },
+ "Cursor": null,
+ "Properties": {
+ "Values": {}
+ }
+ },
+ "1": {
+ "Id": 1,
+ "Name": "Normal",
+ "PassToChildren": false,
+ "IncorporationFlags": 0,
+ "Image": {
+ "File": 100668307,
+ "DrawMode": 1
+ },
+ "Cursor": null,
+ "Properties": {
+ "Values": {}
+ }
+ }
+ },
+ "DefaultStateId": 1,
+ "FontDid": 0,
+ "HJustify": 1,
+ "VJustify": 1,
+ "FontColor": null,
+ "StateMedia": {
+ "Normal": {
+ "Item1": 100668307,
+ "Item2": 1
+ },
+ "Normal_pressed": {
+ "Item1": 100668308,
+ "Item2": 1
+ }
+ },
+ "StateCursors": {},
+ "DefaultStateName": "Normal",
+ "Children": []
+ },
+ {
+ "Id": 268435709,
+ "Type": 3,
+ "X": 0,
+ "Y": 0,
+ "Width": 276,
+ "Height": 25,
+ "OriginalParentWidth": 300,
+ "OriginalParentHeight": 362,
+ "HasOriginalParentSize": true,
+ "Left": 1,
+ "Top": 1,
+ "Right": 1,
+ "Bottom": 2,
+ "ReadOrder": 2,
+ "ZLevel": 0,
+ "States": {
+ "4294967295": {
+ "Id": 4294967295,
+ "Name": "",
+ "PassToChildren": false,
+ "IncorporationFlags": 30,
+ "Image": {
+ "File": 100683002,
+ "DrawMode": 3
+ },
+ "Cursor": null,
+ "Properties": {
+ "Values": {}
+ }
+ }
+ },
+ "DefaultStateId": 0,
+ "FontDid": 0,
+ "HJustify": 1,
+ "VJustify": 1,
+ "FontColor": null,
+ "StateMedia": {
+ "": {
+ "Item1": 100683002,
+ "Item2": 3
+ }
+ },
+ "StateCursors": {},
+ "DefaultStateName": "",
+ "Children": [
+ {
+ "Id": 268435710,
+ "Type": 12,
+ "X": 0,
+ "Y": 0,
+ "Width": 276,
+ "Height": 25,
+ "OriginalParentWidth": 276,
+ "OriginalParentHeight": 25,
+ "HasOriginalParentSize": true,
+ "Left": 0,
+ "Top": 0,
+ "Right": 0,
+ "Bottom": 0,
+ "ReadOrder": 1,
+ "ZLevel": 0,
+ "States": {
+ "4294967295": {
+ "Id": 4294967295,
+ "Name": "",
+ "PassToChildren": false,
+ "IncorporationFlags": 30,
+ "Image": null,
+ "Cursor": null,
+ "Properties": {
+ "Values": {
+ "35": {
+ "Kind": 4,
+ "MasterPropertyId": 35,
+ "UnsignedValue": 0,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ },
+ "26": {
+ "Kind": 7,
+ "MasterPropertyId": 26,
+ "UnsignedValue": 0,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [
+ {
+ "Kind": 2,
+ "MasterPropertyId": 24,
+ "UnsignedValue": 1073741825,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ }
+ ],
+ "StructValue": {}
+ },
+ "37": {
+ "Kind": 4,
+ "MasterPropertyId": 37,
+ "UnsignedValue": 0,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ },
+ "27": {
+ "Kind": 7,
+ "MasterPropertyId": 27,
+ "UnsignedValue": 0,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [
+ {
+ "Kind": 6,
+ "MasterPropertyId": 25,
+ "UnsignedValue": 0,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 255,
+ "Green": 255,
+ "Red": 255,
+ "Alpha": 255
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ }
+ ],
+ "StructValue": {}
+ },
+ "20": {
+ "Kind": 0,
+ "MasterPropertyId": 20,
+ "UnsignedValue": 1,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ },
+ "21": {
+ "Kind": 0,
+ "MasterPropertyId": 21,
+ "UnsignedValue": 1,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ },
+ "23": {
+ "Kind": 5,
+ "MasterPropertyId": 23,
+ "UnsignedValue": 0,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 5801986,
+ "TableId": 587202561,
+ "Override": 0,
+ "English": 1,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ }
+ }
+ }
+ },
+ "13": {
+ "Id": 13,
+ "Name": "Ghosted",
+ "PassToChildren": false,
+ "IncorporationFlags": 0,
+ "Image": null,
+ "Cursor": null,
+ "Properties": {
+ "Values": {
+ "27": {
+ "Kind": 7,
+ "MasterPropertyId": 27,
+ "UnsignedValue": 0,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 0,
+ "Green": 0,
+ "Red": 0,
+ "Alpha": 0
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [
+ {
+ "Kind": 6,
+ "MasterPropertyId": 25,
+ "UnsignedValue": 0,
+ "IntegerValue": 0,
+ "FloatValue": 0,
+ "BoolValue": false,
+ "StringInfoValue": {
+ "Token": 0,
+ "StringId": 0,
+ "TableId": 0,
+ "Override": 0,
+ "English": 0,
+ "Comment": 0
+ },
+ "ColorValue": {
+ "Blue": 76,
+ "Green": 76,
+ "Red": 76,
+ "Alpha": 255
+ },
+ "VectorValue": {
+ "X": 0,
+ "Y": 0,
+ "Z": 0
+ },
+ "ArrayValue": [],
+ "StructValue": {}
+ }
+ ],
+ "StructValue": {}
+ }
+ }
+ }
+ }
+ },
+ "DefaultStateId": 0,
+ "FontDid": 1073741825,
+ "HJustify": 1,
+ "VJustify": 1,
+ "FontColor": {
+ "X": 1,
+ "Y": 1,
+ "Z": 1,
+ "W": 1
+ },
+ "StateMedia": {},
+ "StateCursors": {},
+ "DefaultStateName": "",
+ "Children": []
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/tests/AcDream.Core.Net.Tests/Messages/SocialActionsTests.cs b/tests/AcDream.Core.Net.Tests/Messages/SocialActionsTests.cs
index ae8fe607..4cf08110 100644
--- a/tests/AcDream.Core.Net.Tests/Messages/SocialActionsTests.cs
+++ b/tests/AcDream.Core.Net.Tests/Messages/SocialActionsTests.cs
@@ -37,11 +37,14 @@ public sealed class SocialActionsTests
[Fact]
public void BuildPingRequest_HasOpcode0x01E9()
{
- byte[] body = SocialActions.BuildPingRequest(seq: 1, clientId: 42);
+ byte[] body = SocialActions.BuildPingRequest(seq: 1);
+ Assert.Equal(12, body.Length);
+ Assert.Equal(SocialActions.GameActionEnvelope,
+ BinaryPrimitives.ReadUInt32LittleEndian(body));
+ Assert.Equal(1u,
+ BinaryPrimitives.ReadUInt32LittleEndian(body.AsSpan(4)));
Assert.Equal(SocialActions.PingRequestOpcode,
BinaryPrimitives.ReadUInt32LittleEndian(body.AsSpan(8)));
- Assert.Equal(42u,
- BinaryPrimitives.ReadUInt32LittleEndian(body.AsSpan(12)));
}
[Fact]
diff --git a/tests/AcDream.Core.Net.Tests/WorldSessionLinkStatusTests.cs b/tests/AcDream.Core.Net.Tests/WorldSessionLinkStatusTests.cs
index 65abb859..999617e2 100644
--- a/tests/AcDream.Core.Net.Tests/WorldSessionLinkStatusTests.cs
+++ b/tests/AcDream.Core.Net.Tests/WorldSessionLinkStatusTests.cs
@@ -1,4 +1,6 @@
using AcDream.Core.Net;
+using AcDream.Core.Net.Messages;
+using System.Net;
using Xunit;
namespace AcDream.Core.Net.Tests;
@@ -43,4 +45,40 @@ public sealed class WorldSessionLinkStatusTests
Assert.True(snapshot.Connected);
Assert.Equal(0d, snapshot.SecondsSinceLastPacket);
}
+
+ [Fact]
+ public void ConnectedSnapshot_CarriesPacketLossAndRoundTripTelemetry()
+ {
+ LinkStatusSnapshot snapshot = WorldSession.BuildLinkStatus(
+ WorldSession.State.InWorld,
+ lastInboundPacketTicks: 100,
+ nowTicks: 200,
+ frequency: 100,
+ roundTripSeconds: 0.125d,
+ packetLossPercentage: 2.5d);
+
+ Assert.Equal(2.5d, snapshot.PacketLossPercentage);
+ Assert.Equal(0.125d, snapshot.RoundTripSeconds);
+ }
+
+ [Fact]
+ public void RequestAndEmptyPingResponse_MeasureMonotonicRoundTrip()
+ {
+ using var session = new WorldSession(
+ new IPEndPoint(IPAddress.Loopback, 65000));
+ byte[]? action = null;
+ session.GameActionCapture = bytes => action = bytes;
+
+ session.RequestLinkStatusPing();
+ session.GameEvents.Dispatch(new GameEventEnvelope(
+ PlayerGuid: 0u,
+ Sequence: 1u,
+ EventType: GameEventType.PingResponse,
+ Payload: ReadOnlyMemory.Empty));
+
+ Assert.NotNull(action);
+ Assert.Equal(12, action.Length);
+ Assert.NotNull(session.PingRoundTripSeconds);
+ Assert.True(session.PingRoundTripSeconds >= 0d);
+ }
}