From 83e8d06ea7275cbf39a106b859b7f2e4b662ad43 Mon Sep 17 00:00:00 2001 From: Erik Date: Sat, 18 Apr 2026 17:12:59 +0200 Subject: [PATCH] feat(app): GameWindow wires Chat/Combat/Spellbook/Items into session Exposes Core state classes as public fields on GameWindow so plugins + UI panels can bind directly, then calls GameEventWiring.WireAll inside live-session setup to connect the parsed GameEvent stream to them. HearSpeech (standalone GameMessage, not 0xF7B0-wrapped) is routed via a separate lambda since it has its own dispatch branch. After this commit, every server-sent event that the Phase F.1 / E.4 / E.5 / H.1 parsers handle actually mutates client state live: - Chat: ChannelBroadcast, Tell, TransientMessage, Popup, HearSpeech - Combat: UpdateHealth, Victim/Defender/Attacker/Evasion notifications, AttackDone - Spellbook: MagicUpdateSpell, MagicRemoveSpell, enchantment add/remove/dispel/purge - Items: WieldObject, InventoryPutObjInContainer WorldTime + Lighting added as public-field Core services so the renderer and plugin API can read "current day fraction" / "active lights" directly. Build green, 602 tests still pass. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/AcDream.App/Rendering/GameWindow.cs | 30 +++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/AcDream.App/Rendering/GameWindow.cs b/src/AcDream.App/Rendering/GameWindow.cs index 97a8d9f..31415dd 100644 --- a/src/AcDream.App/Rendering/GameWindow.cs +++ b/src/AcDream.App/Rendering/GameWindow.cs @@ -141,6 +141,19 @@ public sealed class GameWindow : IDisposable private AcDream.Core.Vfx.ParticleSystem? _particleSystem; private AcDream.Core.Vfx.ParticleHookSink? _particleSink; + // Phase F.1-H.1 — client-side state classes fed by GameEventWiring. + // Exposed publicly so plugins + UI panels can bind directly. + public readonly AcDream.Core.Chat.ChatLog Chat = new(); + public readonly AcDream.Core.Combat.CombatState Combat = new(); + public readonly AcDream.Core.Spells.Spellbook SpellBook = new(); + public readonly AcDream.Core.Items.ItemRepository Items = new(); + + // Phase G.1-G.2 world lighting/time state. + public readonly AcDream.Core.World.WorldTimeService WorldTime = + new AcDream.Core.World.WorldTimeService( + AcDream.Core.World.SkyStateProvider.Default()); + public readonly AcDream.Core.Lighting.LightManager Lighting = new(); + // Phase B.2: player movement mode. private AcDream.App.Input.PlayerMovementController? _playerController; private AcDream.App.Rendering.ChaseCamera? _chaseCamera; @@ -710,6 +723,23 @@ public sealed class GameWindow : IDisposable _liveSession.MotionUpdated += OnLiveMotionUpdated; _liveSession.PositionUpdated += OnLivePositionUpdated; _liveSession.TeleportStarted += OnTeleportStarted; + + // Phase F.1-H.1: wire every parsed GameEvent into the right + // Core state class (chat, combat, spellbook, items). After + // this one call, server-sent ChannelBroadcast / damage + // notifications / spell learns / wield events all update + // the corresponding client-side state without further glue. + AcDream.Core.Net.GameEventWiring.WireAll( + _liveSession.GameEvents, Items, Combat, SpellBook, Chat); + + // Phase H.1: feed inbound HearSpeech into the chat log. + _liveSession.SpeechHeard += speech => + Chat.OnLocalSpeech( + sender: speech.SenderName, + text: speech.Text, + senderGuid: speech.SenderGuid, + isRanged: speech.IsRanged); + _liveSession.Connect(user, pass); if (_liveSession.Characters is null || _liveSession.Characters.Characters.Count == 0)