From 4a3981cc868a3d9b3679935e48c2d3e529e40767 Mon Sep 17 00:00:00 2001 From: Cam Sinclair Date: Sun, 3 Nov 2024 02:08:55 +1000 Subject: [PATCH] Conversation and eventing fixes --- src/Api/Eventing/AdHocComponent.cs | 4 +-- src/Api/Eventing/AlbionTask.cs | 4 +-- src/Api/Eventing/AlbionTaskBuilder.cs | 2 +- src/Api/Eventing/AlbionTaskBuilder_T.cs | 2 +- src/Api/Eventing/AlbionTaskCore.cs | 7 ++++ src/Api/Eventing/Component.cs | 4 +-- src/Api/Eventing/EventExchange.cs | 7 ++-- src/Api/Eventing/Handler.cs | 8 ++--- src/Api/Eventing/Tasks.cs | 7 ++++ src/Game.Veldrid/Diag/CodeWindow.cs | 33 ++++++++++++++++--- src/Game.Veldrid/Diag/ThreadsWindow.cs | 7 ++-- src/Game.Veldrid/UAlbion.Game.Veldrid.csproj | 2 +- src/Game/Combat/Battle.cs | 2 +- src/Game/Combat/CombatClock.cs | 6 ++-- src/Game/Entities/ItemTransitionManager.cs | 2 +- src/Game/Entities/Map2D/FlatMap.cs | 2 +- src/Game/EventChainManager.cs | 13 +++++--- src/Game/GameClock.cs | 6 ++-- src/Game/GameComponent.cs | 17 +++++++++- src/Game/Gui/Combat/CombatDialog.cs | 2 +- src/Game/Gui/Controls/Button.cs | 2 +- src/Game/Gui/Controls/ContextMenu.cs | 2 +- src/Game/Gui/Dialogs/Conversation.cs | 4 +-- .../Gui/Dialogs/ConversationOptionsWindow.cs | 4 +++ .../Gui/Dialogs/ConversationTopicWindow.cs | 2 +- src/Game/Gui/Menus/MainMenu.cs | 4 +-- src/Game/Gui/Status/StatusBarPortrait.cs | 2 +- src/Game/Gui/Text/ConversationManager.cs | 8 ++--- src/Game/IEventManager.cs | 1 + src/Game/MapManager.cs | 12 +++---- src/Game/Querier.cs | 4 +-- src/Game/ScriptManager.cs | 4 +-- src/Game/State/GameState.cs | 4 +-- src/Game/State/Player/InventoryManager.cs | 22 ++++++------- src/Game/UAlbion.Game.csproj | 2 +- src/Tests/UAlbion.Api.Tests/AsyncTests.cs | 2 +- src/Tests/UAlbion.Api.Tests/BasicComponent.cs | 4 +-- .../UAlbion.Api.Tests/EventExchangeTests.cs | 4 +-- src/UAlbion/Albion.cs | 2 +- 39 files changed, 145 insertions(+), 81 deletions(-) diff --git a/src/Api/Eventing/AdHocComponent.cs b/src/Api/Eventing/AdHocComponent.cs index 50f090fb1..5c5f3f20c 100644 --- a/src/Api/Eventing/AdHocComponent.cs +++ b/src/Api/Eventing/AdHocComponent.cs @@ -37,8 +37,8 @@ sealed class Helper : IAdHocComponentHelper public T Resolve() => _this.Resolve(); public T TryResolve() => _this.TryResolve(); public void Raise(T e) where T : IEvent => _this.Raise(e); - public AlbionTask RaiseAsync(T e) where T : IEvent => _this.RaiseAsync(e); - public AlbionTask RaiseQueryAsync(IQueryEvent e) => _this.RaiseQueryAsync(e); + public AlbionTask RaiseAsync(T e) where T : IEvent => _this.RaiseA(e); + public AlbionTask RaiseQueryAsync(IQueryEvent e) => _this.RaiseQueryA(e); public void Enqueue(IEvent e) => _this.Enqueue(e); public void Distribute(ICancellableEvent e, List targets, Func projection) => _this.Distribute(e, targets, projection); public void On(Action callback) where T : IEvent => _this.On(callback); diff --git a/src/Api/Eventing/AlbionTask.cs b/src/Api/Eventing/AlbionTask.cs index b03b2aa05..29b64cd64 100644 --- a/src/Api/Eventing/AlbionTask.cs +++ b/src/Api/Eventing/AlbionTask.cs @@ -9,7 +9,7 @@ namespace UAlbion.Api.Eventing; [AsyncMethodBuilder(typeof(AlbionTaskBuilder))] public readonly struct AlbionTask : INotifyCompletion, IEquatable { - public static AlbionTask Complete { get; } = new(null); + public static AlbionTask CompletedTask { get; } = new(null); public static AlbionTask Unit { get; } = new(Api.Unit.V); public static AlbionTask True { get; } = new(true); public static AlbionTask False { get; } = new(false); @@ -69,7 +69,7 @@ public void GetResult() /// /// Create a completed task /// - public AlbionTask(T value) + internal AlbionTask(T value) { _core = null; _result = value; diff --git a/src/Api/Eventing/AlbionTaskBuilder.cs b/src/Api/Eventing/AlbionTaskBuilder.cs index 4e854b06b..e809bb41c 100644 --- a/src/Api/Eventing/AlbionTaskBuilder.cs +++ b/src/Api/Eventing/AlbionTaskBuilder.cs @@ -25,7 +25,7 @@ enum BuilderState /// Gets the value task for this builder. public AlbionTask Task => _state switch { - BuilderState.Complete => AlbionTask.Complete, + BuilderState.Complete => AlbionTask.CompletedTask, BuilderState.Pending => new AlbionTask(_core), _ => throw new InvalidOperationException("Tried to get Task, but it hasn't been created") }; diff --git a/src/Api/Eventing/AlbionTaskBuilder_T.cs b/src/Api/Eventing/AlbionTaskBuilder_T.cs index b314ec626..a19e879bb 100644 --- a/src/Api/Eventing/AlbionTaskBuilder_T.cs +++ b/src/Api/Eventing/AlbionTaskBuilder_T.cs @@ -26,7 +26,7 @@ enum BuilderState /// Gets the value task for this builder. public AlbionTask Task => _state switch { - BuilderState.Complete => new AlbionTask(_result), + BuilderState.Complete => AlbionTask.FromResult(_result), BuilderState.Pending => new AlbionTask(_core!), _ => throw new InvalidOperationException("Tried to get Task, but it hasn't been created") }; diff --git a/src/Api/Eventing/AlbionTaskCore.cs b/src/Api/Eventing/AlbionTaskCore.cs index 8f9811978..b2fe8188b 100644 --- a/src/Api/Eventing/AlbionTaskCore.cs +++ b/src/Api/Eventing/AlbionTaskCore.cs @@ -11,6 +11,7 @@ public interface IAlbionTaskCore : ICriticalNotifyCompletion #if DEBUG int Id { get; } string? Description { get; set; } + IAlbionTaskCore? Parent { get; } #endif bool IsCompleted { get; } } @@ -32,6 +33,7 @@ enum TaskStatus [DiagEdit] public bool BreakOnCompletion { get; set; } public string? Description { get; set; } + public IAlbionTaskCore? Parent { get; } #endif #if RECORD_TASK_STACKS @@ -56,6 +58,7 @@ public AlbionTaskCore(string? description) #if DEBUG Id = Tasks.GetNextId(); Description = description; + Parent = Tasks.Current; Tasks.AddTask(this); #endif @@ -125,11 +128,14 @@ public void SetResult(T value) { _result = value; IsCompleted = true; + #if DEBUG if (BreakOnCompletion) Debugger.Break(); _status = TaskStatus.Completing; + var previous = Tasks.Current; + Tasks.Current = this; #endif switch (_continuation) @@ -149,6 +155,7 @@ public void SetResult(T value) #if DEBUG _status = TaskStatus.Completed; Tasks.RemoveTask(this); + Tasks.Current = previous; #endif } } diff --git a/src/Api/Eventing/Component.cs b/src/Api/Eventing/Component.cs index c4ce098af..fbaae9391 100644 --- a/src/Api/Eventing/Component.cs +++ b/src/Api/Eventing/Component.cs @@ -103,7 +103,7 @@ public abstract class Component : IComponent /// /// The event to raise /// The number of async handlers which have either already called the continuation or intend to call it in the future. - protected AlbionTask RaiseAsync(T e) where T : IEvent => Exchange.RaiseA(e, this); + protected AlbionTask RaiseA(T e) where T : IEvent => Exchange.RaiseA(e, this); /// /// Raise an event via the currently subscribed event exchange (if subscribed), and @@ -114,7 +114,7 @@ public abstract class Component : IComponent /// The return value that async handlers should supply upon completion. /// The event to raise /// The number of async handlers which have either already called the continuation or intend to call it in the future. - protected AlbionTask RaiseQueryAsync(IQueryEvent e) => Exchange.RaiseQueryA(e, this); + protected AlbionTask RaiseQueryA(IQueryEvent e) => Exchange.RaiseQueryA(e, this); /// /// Enqueue an event with the currently subscribed event exchange to be raised diff --git a/src/Api/Eventing/EventExchange.cs b/src/Api/Eventing/EventExchange.cs index aa9d64c08..e7f311321 100644 --- a/src/Api/Eventing/EventExchange.cs +++ b/src/Api/Eventing/EventExchange.cs @@ -101,7 +101,7 @@ static AlbionTask RaiseInvoker(List handlers, IEvent e, object sen } } - return new(0); + return AlbionTask.FromResult(0); } // [DebuggerHidden, StackTraceHidden] @@ -178,9 +178,10 @@ static AlbionTask RaiseQueryInvoker(List handlers, IQueryEvent } } - if (!hasResult) throw new InvalidOperationException("No result found for RaiseQuery call"); + if (!hasResult) + throw new InvalidOperationException("No result found for RaiseQuery call"); - return new(result); + return AlbionTask.FromResult(result); } // [DebuggerHidden, StackTraceHidden] diff --git a/src/Api/Eventing/Handler.cs b/src/Api/Eventing/Handler.cs index 74d990268..7b4df217d 100644 --- a/src/Api/Eventing/Handler.cs +++ b/src/Api/Eventing/Handler.cs @@ -36,7 +36,7 @@ public SyncHandler(Action callback, IComponent component, bool isPostHan [DebuggerHidden, StackTraceHidden] public AlbionTask InvokeAsAsync(IEvent e) { Callback((TEvent)e); - return AlbionTask.Complete; + return AlbionTask.CompletedTask; } public override string ToString() => $"H<{Component.GetType().Name}, {Type.Name}>"; @@ -52,14 +52,14 @@ public SyncQueryHandler(Func callback, IComponent component, bo [DebuggerHidden, StackTraceHidden] AlbionTask IAsyncHandler.InvokeAsAsync(IEvent e) // Ignores result { Callback((TEvent)e); - return AlbionTask.Complete; + return AlbionTask.CompletedTask; } [DebuggerHidden, StackTraceHidden] public TResult Invoke(IQueryEvent e) => Callback((TEvent)e); [DebuggerHidden, StackTraceHidden] public AlbionTask InvokeAsAsync(IQueryEvent e) { var result = Callback((TEvent)e); - return new AlbionTask(result); + return AlbionTask.FromResult(result); } public override string ToString() => $"HQ<{Component.GetType().Name}, {Type.Name}>"; @@ -75,7 +75,7 @@ public ReceiveOnlyHandler(Action callback, IComponent component) [DebuggerHidden, StackTraceHidden] public AlbionTask InvokeAsAsync(IEvent e) { Callback((TEvent)e); - return AlbionTask.Complete; + return AlbionTask.CompletedTask; } public override string ToString() => $"HR<{Component.GetType().Name}, {Type.Name}>"; diff --git a/src/Api/Eventing/Tasks.cs b/src/Api/Eventing/Tasks.cs index 189ee3196..e91054e04 100644 --- a/src/Api/Eventing/Tasks.cs +++ b/src/Api/Eventing/Tasks.cs @@ -10,6 +10,13 @@ public static class Tasks static int _nextId; static readonly object SyncRoot = new(); static readonly List Pending = new(); // Just for debugging + static readonly ThreadLocal CurrentTask = new(); + + public static IAlbionTaskCore? Current + { + get => CurrentTask.Value; + set => CurrentTask.Value = value; + } public static int GetNextId() => Interlocked.Increment(ref _nextId); public static void AddTask(IAlbionTaskCore task) diff --git a/src/Game.Veldrid/Diag/CodeWindow.cs b/src/Game.Veldrid/Diag/CodeWindow.cs index 5fd16ebb6..da59bc7d3 100644 --- a/src/Game.Veldrid/Diag/CodeWindow.cs +++ b/src/Game.Veldrid/Diag/CodeWindow.cs @@ -1,5 +1,7 @@ -using ImGuiColorTextEditNet; +using System; +using ImGuiColorTextEditNet; using ImGuiNET; +using UAlbion.Api.Eventing; using UAlbion.Core.Veldrid; using UAlbion.Formats; using UAlbion.Formats.MapEvents; @@ -27,12 +29,12 @@ namespace UAlbion.Game.Veldrid.Diag; */ -public class CodeWindow : GameComponent, IImGuiWindow +public class ScriptWindow : GameComponent, IImGuiWindow { readonly TextEditor _editor; public string Name { get; } - public CodeWindow(string name) + public ScriptWindow(string name) { Name = name; _editor = new TextEditor @@ -67,10 +69,31 @@ void DrawContext(EventContext context) { var eventFormatter = new EventFormatter(Assets.LoadStringSafe, context.EventSet.StringSetId); set.Decompiled = eventFormatter.Decompile(set.Events, set.Chains, set.ExtraEntryPoints); - var code = set.Decompiled.Script; - _editor.AllText = code; + var code = set.Decompiled.Script.AsSpan(); + _editor.AllText = ""; + + foreach (var part in set.Decompiled.Parts) + { + PaletteIndex color = part.Type switch + { + ScriptPartType.Text => PaletteIndex.Default, + ScriptPartType.Keyword => PaletteIndex.Keyword, + ScriptPartType.EventName => PaletteIndex.KnownIdentifier, + ScriptPartType.Identifier => PaletteIndex.Identifier, + ScriptPartType.Number => PaletteIndex.Number, + ScriptPartType.Operator => PaletteIndex.Punctuation, + ScriptPartType.Label => PaletteIndex.Identifier, + ScriptPartType.StringConstant => PaletteIndex.String, + ScriptPartType.Comment => PaletteIndex.Comment, + ScriptPartType.Error => PaletteIndex.ErrorMarker, + _ => PaletteIndex.Default + }; + + _editor.Append(code[part.Range.Start..part.Range.End], color); + } // TODO: Add breakpoints } + // _editor.Selection.HighlightedLine = _editor.Render("Script"); } diff --git a/src/Game.Veldrid/Diag/ThreadsWindow.cs b/src/Game.Veldrid/Diag/ThreadsWindow.cs index 24b967767..879a64ecf 100644 --- a/src/Game.Veldrid/Diag/ThreadsWindow.cs +++ b/src/Game.Veldrid/Diag/ThreadsWindow.cs @@ -10,7 +10,6 @@ public class ThreadsWindow : Component, IImGuiWindow { readonly StringCache _stringCache = new(); - int _currentContextIndex; string[] _contextNames = []; public string Name { get; } @@ -30,9 +29,11 @@ public void Draw() for (int i = 0; i < _contextNames.Length; i++) _contextNames[i] = chainManager.Contexts[i].ToString(); - ImGui.ListBox("Active Contexts", ref _currentContextIndex, _contextNames, _contextNames.Length); + int currentContextIndex = chainManager.CurrentDebugContextIndex; + if (ImGui.ListBox("Active Contexts", ref currentContextIndex, _contextNames, _contextNames.Length)) + chainManager.CurrentDebugContextIndex = currentContextIndex; - #if DEBUG +#if DEBUG ImGui.Text("Pending Async Tasks:"); Tasks.EnumeratePendingTasks(_stringCache, static (stringCache, core) => { diff --git a/src/Game.Veldrid/UAlbion.Game.Veldrid.csproj b/src/Game.Veldrid/UAlbion.Game.Veldrid.csproj index bed98c4f9..ab15bf183 100644 --- a/src/Game.Veldrid/UAlbion.Game.Veldrid.csproj +++ b/src/Game.Veldrid/UAlbion.Game.Veldrid.csproj @@ -22,7 +22,7 @@ - + diff --git a/src/Game/Combat/Battle.cs b/src/Game/Combat/Battle.cs index 51e3e7541..47a7f8f6a 100644 --- a/src/Game/Combat/Battle.cs +++ b/src/Game/Combat/Battle.cs @@ -44,7 +44,7 @@ public Battle(MonsterGroupId groupId, SpriteId backgroundId) }); } - AlbionTask BeginRoundAsync(BeginCombatRoundEvent _) => RaiseAsync(new CombatUpdateEvent(25)); // TODO + AlbionTask BeginRoundAsync(BeginCombatRoundEvent _) => RaiseA(new CombatUpdateEvent(25)); // TODO protected override void Subscribed() { diff --git a/src/Game/Combat/CombatClock.cs b/src/Game/Combat/CombatClock.cs index 86516e742..6ad0c0694 100644 --- a/src/Game/Combat/CombatClock.cs +++ b/src/Game/Combat/CombatClock.cs @@ -27,8 +27,8 @@ public CombatClock() OnAsync(e => { - if (IsRunning) { Warn($"Ignoring {e} - clock paused"); return AlbionTask.Complete; } - if (_currentUpdate != null) { Warn($"Ignoring {e} - already running another update event"); return AlbionTask.Complete; } + if (IsRunning) { Warn($"Ignoring {e} - clock paused"); return AlbionTask.CompletedTask; } + if (_currentUpdate != null) { Warn($"Ignoring {e} - already running another update event"); return AlbionTask.CompletedTask; } GameTrace.Log.CombatClockUpdating(e.Cycles); _currentUpdate = new AlbionTaskCore("CombatClock.CombatUpdateEvent"); @@ -119,4 +119,4 @@ void RaiseTick() currentUpdate.Complete(); } } -} \ No newline at end of file +} diff --git a/src/Game/Entities/ItemTransitionManager.cs b/src/Game/Entities/ItemTransitionManager.cs index 4761a7de2..06dbad78d 100644 --- a/src/Game/Entities/ItemTransitionManager.cs +++ b/src/Game/Entities/ItemTransitionManager.cs @@ -26,7 +26,7 @@ AlbionTask LinearFromTilePosition(int x, int y, ItemId itemId, float? transition var map = TryResolve()?.Current; if (scene == null || map == null) - return AlbionTask.Complete; + return AlbionTask.CompletedTask; var worldPosition = new Vector3(x, y, 0) * map.TileSize; var normPosition = sceneManager.Camera.ProjectWorldToNorm(worldPosition); diff --git a/src/Game/Entities/Map2D/FlatMap.cs b/src/Game/Entities/Map2D/FlatMap.cs index ff4118cfb..1bc5f9cb2 100644 --- a/src/Game/Entities/Map2D/FlatMap.cs +++ b/src/Game/Entities/Map2D/FlatMap.cs @@ -158,7 +158,7 @@ async AlbionTask FireEventChains(TriggerType type, bool log) foreach (var zone in zones) { - await RaiseAsync( + await RaiseA( new TriggerChainEvent( _logicalMap.EventSet, zone.EventIndex, diff --git a/src/Game/EventChainManager.cs b/src/Game/EventChainManager.cs index cdaa03d19..4472f8027 100644 --- a/src/Game/EventChainManager.cs +++ b/src/Game/EventChainManager.cs @@ -17,7 +17,12 @@ public sealed class EventChainManager : ServiceComponent, IEventM readonly List _contexts = new(); readonly List _breakpoints = new(); - public EventContext CurrentDebugContext { get; } + public int CurrentDebugContextIndex { get; set; } = -1; + public EventContext CurrentDebugContext => + CurrentDebugContextIndex >= 0 && CurrentDebugContextIndex < _contexts.Count + ? _contexts[CurrentDebugContextIndex] + : null; + public IReadOnlyList Contexts => _contexts; public IReadOnlyList Breakpoints => _breakpoints; @@ -51,7 +56,7 @@ AlbionTask Trigger(TriggerChainEvent e) var game = Resolve(); if (e.EventSet.Id.Type == AssetType.Map && game.IsChainDisabled(e.EventSet.Id, e.EventSet.GetChainForEvent(e.EntryPoint))) { - return AlbionTask.Complete; + return AlbionTask.CompletedTask; } var isClockRunning = Resolve().IsRunning; @@ -151,7 +156,7 @@ async AlbionTask HandleAsyncEvent(EventContext context, IEvent asyncEvent) { context.Status = EventContextStatus.Waiting; - var task = RaiseAsync(asyncEvent); + var task = RaiseA(asyncEvent); #if DEBUG _ = task.Named($"ECM.HandleAsyncEvent for C{context.Id} {context.EventSet.Id}:{context.Node.Id}: {context.Node.Event}"); #endif @@ -164,7 +169,7 @@ async AlbionTask HandleAsyncEvent(EventContext context, IEvent asyncEvent) async AlbionTask HandleBoolEvent(EventContext context, IQueryEvent boolEvent, IBranchNode branch) // Return value = whether to return. { context.Status = EventContextStatus.Waiting; - var task = RaiseQueryAsync(boolEvent); + var task = RaiseQueryA(boolEvent); #if DEBUG _ = task.Named($"ECM.HandleBoolEvent for C{context.Id} {context.EventSet.Id}:{context.Node.Id}: {context.Node.Event}"); #endif diff --git a/src/Game/GameClock.cs b/src/Game/GameClock.cs index 7b8cb53f6..78df787fe 100644 --- a/src/Game/GameClock.cs +++ b/src/Game/GameClock.cs @@ -33,8 +33,8 @@ public GameClock() OnAsync(e => { - if (IsRunning) { Warn($"Ignoring {e} - clock paused"); return AlbionTask.Complete; } - if (_currentUpdate != null) { Warn($"Ignoring {e} - already running another update event"); return AlbionTask.Complete; } + if (IsRunning) { Warn($"Ignoring {e} - clock paused"); return AlbionTask.CompletedTask; } + if (_currentUpdate != null) { Warn($"Ignoring {e} - already running another update event"); return AlbionTask.CompletedTask; } GameTrace.Log.ClockUpdating(e.Cycles); _currentUpdate = new AlbionTaskCore("GameClock.GameUpdateEvent"); @@ -161,4 +161,4 @@ void RaiseTick() _currentUpdate = null; currentUpdate.Complete(); } -} \ No newline at end of file +} diff --git a/src/Game/GameComponent.cs b/src/Game/GameComponent.cs index ade7104b3..d955b443f 100644 --- a/src/Game/GameComponent.cs +++ b/src/Game/GameComponent.cs @@ -1,11 +1,26 @@ -using UAlbion.Api.Eventing; +using System; +using UAlbion.Api.Eventing; +using UAlbion.Core; using UAlbion.Formats; +using UAlbion.Game.Events; namespace UAlbion.Game; public abstract class GameComponent : Component { protected IAssetManager Assets => Resolve(); + + protected async AlbionTask WithFrozenClock(T context, Func func) + { + var wasClockRunning = Resolve()?.IsRunning ?? false; + if (wasClockRunning) + Raise(new StopClockEvent()); + + await func(context); + + if (wasClockRunning) + Raise(new StartClockEvent()); + } } public abstract class GameServiceComponent : ServiceComponent diff --git a/src/Game/Gui/Combat/CombatDialog.cs b/src/Game/Gui/Combat/CombatDialog.cs index 40a8536d7..fcd9f73d3 100644 --- a/src/Game/Gui/Combat/CombatDialog.cs +++ b/src/Game/Gui/Combat/CombatDialog.cs @@ -42,7 +42,7 @@ public CombatDialog(int depth, IReadOnlyBattle battle) : base(DialogPositioning. void StartRound() { IsActive = false; - RaiseAsync(new BeginCombatRoundEvent()).OnCompleted(() => IsActive = true); + RaiseA(new BeginCombatRoundEvent()).OnCompleted(() => IsActive = true); } HorizontalStacker BuildRow(int row) diff --git a/src/Game/Gui/Controls/Button.cs b/src/Game/Gui/Controls/Button.cs index cf8c2973c..0894c50d1 100644 --- a/src/Game/Gui/Controls/Button.cs +++ b/src/Game/Gui/Controls/Button.cs @@ -86,7 +86,7 @@ void OnLeftRelease(UiLeftReleaseEvent _) } else // For the first click, just start the double-click timer. { - RaiseAsync(new WallClockTimerEvent(ReadVar(V.Game.Ui.ButtonDoubleClickIntervalSeconds))) + RaiseA(new WallClockTimerEvent(ReadVar(V.Game.Ui.ButtonDoubleClickIntervalSeconds))) .OnCompleted(() => { if (!ClickTimerPending) // They've already double-clicked diff --git a/src/Game/Gui/Controls/ContextMenu.cs b/src/Game/Gui/Controls/ContextMenu.cs index 4af938eed..c7960a020 100644 --- a/src/Game/Gui/Controls/ContextMenu.cs +++ b/src/Game/Gui/Controls/ContextMenu.cs @@ -65,7 +65,7 @@ void OnButton(ContextMenuOption option, bool keepOpen) Close(); if (option.Event != null) - Raise(option.Event); + _ = WithFrozenClock((this, option), static x => x.Item1.RaiseA(x.option.Event)); } void Close() diff --git a/src/Game/Gui/Dialogs/Conversation.cs b/src/Game/Gui/Dialogs/Conversation.cs index 337d2dccd..250d4bb8d 100644 --- a/src/Game/Gui/Dialogs/Conversation.cs +++ b/src/Game/Gui/Dialogs/Conversation.cs @@ -331,10 +331,10 @@ async AlbionTask TriggerWordAction(WordId wordId) eventIndex.Value, new EventSource(chainSource.Id, TriggerType.Action)); - await RaiseAsync(triggerEvent); + await RaiseA(triggerEvent); var action = (ActionEvent)chainSource.Events[eventIndex.Value].Event; - await RaiseAsync(new EventVisitedEvent(chainSource.Id, action)); + await RaiseA(new EventVisitedEvent(chainSource.Id, action)); return true; } } diff --git a/src/Game/Gui/Dialogs/ConversationOptionsWindow.cs b/src/Game/Gui/Dialogs/ConversationOptionsWindow.cs index 31cb7fa74..a3ed6875c 100644 --- a/src/Game/Gui/Dialogs/ConversationOptionsWindow.cs +++ b/src/Game/Gui/Dialogs/ConversationOptionsWindow.cs @@ -24,7 +24,11 @@ public ConversationOptionsWindow(int depth) : base(DialogPositioning.Bottom, dep continue; if (e.Option == i) + { conversationOption.Trigger(); + break; + } + i++; } }); diff --git a/src/Game/Gui/Dialogs/ConversationTopicWindow.cs b/src/Game/Gui/Dialogs/ConversationTopicWindow.cs index 2f1dce4ce..5027503ff 100644 --- a/src/Game/Gui/Dialogs/ConversationTopicWindow.cs +++ b/src/Game/Gui/Dialogs/ConversationTopicWindow.cs @@ -101,7 +101,7 @@ public AlbionTask GetWord(IDictionary words) async AlbionTask PromptForWord() { - var wordString = await RaiseQueryAsync(new TextPromptEvent()); + var wordString = await RaiseQueryA(new TextPromptEvent()); var wordLookup = Resolve(); return wordLookup.Parse(wordString); } diff --git a/src/Game/Gui/Menus/MainMenu.cs b/src/Game/Gui/Menus/MainMenu.cs index 9bc05af31..9fb933e4a 100644 --- a/src/Game/Gui/Menus/MainMenu.cs +++ b/src/Game/Gui/Menus/MainMenu.cs @@ -71,12 +71,12 @@ async AlbionTask NewGame() Detach(); var e = new YesNoPromptEvent(Base.SystemText.MainMenu_DoYouReallyWantToStartANewGame); - var response = await RaiseQueryAsync(e); + var response = await RaiseQueryA(e); Attach(exchange); if (response) - await RaiseAsync(new NewGameEvent(Base.Map.TorontoBegin, 31, 76)); // TODO: Move this to config? + await RaiseA(new NewGameEvent(Base.Map.TorontoBegin, 31, 76)); // TODO: Move this to config? } void LoadGame() diff --git a/src/Game/Gui/Status/StatusBarPortrait.cs b/src/Game/Gui/Status/StatusBarPortrait.cs index 9b55b215a..a72841c9b 100644 --- a/src/Game/Gui/Status/StatusBarPortrait.cs +++ b/src/Game/Gui/Status/StatusBarPortrait.cs @@ -174,7 +174,7 @@ void OnClick(UiLeftClickEvent e) } else // For the first click, just start the double-click timer. { - RaiseAsync(new WallClockTimerEvent(ReadVar(V.Game.Ui.ButtonDoubleClickIntervalSeconds))).OnCompleted(OnTimer); + RaiseA(new WallClockTimerEvent(ReadVar(V.Game.Ui.ButtonDoubleClickIntervalSeconds))).OnCompleted(OnTimer); _isClickTimerPending = true; } } diff --git a/src/Game/Gui/Text/ConversationManager.cs b/src/Game/Gui/Text/ConversationManager.cs index 1f22bae76..90ddd62ff 100644 --- a/src/Game/Gui/Text/ConversationManager.cs +++ b/src/Game/Gui/Text/ConversationManager.cs @@ -74,7 +74,7 @@ async AlbionTask OnBaseTextEvent(TextEvent mapTextEvent) } case TextLocation.QuickInfo: - await RaiseAsync(new DescriptionTextEvent(tf.Format(mapTextEvent.ToId(ContextTextSource)))); + await RaiseA(new DescriptionTextEvent(tf.Format(mapTextEvent.ToId(ContextTextSource)))); return; case TextLocation.Conversation: @@ -84,7 +84,7 @@ async AlbionTask OnBaseTextEvent(TextEvent mapTextEvent) break; // Handled by Conversation default: - await RaiseAsync(new DescriptionTextEvent(tf.Format(mapTextEvent.ToId(ContextTextSource)))); // TODO: + await RaiseA(new DescriptionTextEvent(tf.Format(mapTextEvent.ToId(ContextTextSource)))); // TODO: return; } } @@ -105,7 +105,7 @@ async AlbionTask StartDialogueCommon(PartyMemberId left, ICharacterSheet right) { var wasRunning = Resolve().IsRunning; if (wasRunning) - await RaiseAsync(new StopClockEvent()); + await RaiseA(new StopClockEvent()); Conversation = AttachChild(new Conversation(left, right)); await Conversation.Run(); @@ -113,7 +113,7 @@ async AlbionTask StartDialogueCommon(PartyMemberId left, ICharacterSheet right) Conversation = null; if (wasRunning) - await RaiseAsync(new StartClockEvent()); + await RaiseA(new StartClockEvent()); } async AlbionTask StartDialogue(StartDialogueEvent e) diff --git a/src/Game/IEventManager.cs b/src/Game/IEventManager.cs index b1c5d2c43..b06187d49 100644 --- a/src/Game/IEventManager.cs +++ b/src/Game/IEventManager.cs @@ -7,6 +7,7 @@ namespace UAlbion.Game; public interface IEventManager { EventContext CurrentDebugContext { get; } + int CurrentDebugContextIndex { get; set; } IReadOnlyList Contexts { get; } IReadOnlyList Breakpoints { get; } void AddBreakpoint(Breakpoint bp); diff --git a/src/Game/MapManager.cs b/src/Game/MapManager.cs index cdd48ae56..e451c21d2 100644 --- a/src/Game/MapManager.cs +++ b/src/Game/MapManager.cs @@ -23,7 +23,7 @@ public MapManager() { if (!Resolve().Loaded) { - await RaiseAsync(new NewGameEvent(e.MapId, 32, 32)); + await RaiseA(new NewGameEvent(e.MapId, 32, 32)); return; } @@ -34,17 +34,17 @@ public MapManager() async AlbionTask LoadMap(MapId mapId) { - await RaiseAsync(new UnloadMapEvent()); + await RaiseA(new UnloadMapEvent()); if (mapId == MapId.None) // 0 = Build a blank scene for testing / debugging { - await RaiseAsync(new SetSceneEvent(SceneId.World2D)); + await RaiseA(new SetSceneEvent(SceneId.World2D)); return; } // Remove old map RemoveAllChildren(); - await RaiseAsync(new MuteEvent()); + await RaiseA(new MuteEvent()); var map = BuildMap(mapId); Current = map; @@ -52,11 +52,11 @@ async AlbionTask LoadMap(MapId mapId) return; // Set the scene first to ensure scene-local components from other scenes are disabled. - await RaiseAsync(new SetSceneEvent(map is Entities.Map3D.DungeonMap ? SceneId.World3D : SceneId.World2D)); + await RaiseA(new SetSceneEvent(map is Entities.Map3D.DungeonMap ? SceneId.World3D : SceneId.World2D)); AttachChild(map); Info($"Loaded map {mapId.Id}: {mapId}"); - await RaiseAsync(new MapInitEvent()); + await RaiseA(new MapInitEvent()); if (!map.MapData.SongId.IsNone) Enqueue(new SongEvent(map.MapData.SongId)); diff --git a/src/Game/Querier.cs b/src/Game/Querier.cs index 0a445383b..d88d72998 100644 --- a/src/Game/Querier.cs +++ b/src/Game/Querier.cs @@ -65,7 +65,7 @@ public Querier() return false; var innerEvent = new YesNoPromptEvent(new StringId(context.EventSet.StringSetId, q.Argument)); - return await RaiseQueryAsync(innerEvent); + return await RaiseQueryA(innerEvent); }); OnQueryAsync(async q => @@ -75,7 +75,7 @@ public Querier() return false; var innerEvent = new NumericPromptEvent(Base.SystemText.MsgBox_EnterNumber, 0, 9999); - var result = await RaiseQueryAsync(innerEvent); + var result = await RaiseQueryA(innerEvent); return result == q.Argument; }); diff --git a/src/Game/ScriptManager.cs b/src/Game/ScriptManager.cs index 5fe2c68b9..6de0b95a8 100644 --- a/src/Game/ScriptManager.cs +++ b/src/Game/ScriptManager.cs @@ -23,7 +23,7 @@ AlbionTask Run(DoScriptEvent doScriptEvent) if (events == null) { Error($"Could not load script {doScriptEvent.ScriptId}"); - return AlbionTask.Complete; + return AlbionTask.CompletedTask; } var nodes = new EventNode[events.Count]; @@ -35,7 +35,7 @@ AlbionTask Run(DoScriptEvent doScriptEvent) var set = new ScriptEventSet(doScriptEvent.ScriptId, mapManager.Current.MapId.ToMapText(), nodes); var source = new EventSource(mapManager.Current.MapId, TriggerType.Default); // TODO: Is there a better trigger type for this? var trigger = new TriggerChainEvent(set, 0, source); - return RaiseAsync(trigger); + return RaiseA(trigger); } void Dump(DumpScriptEvent dumpScriptEvent) diff --git a/src/Game/State/GameState.cs b/src/Game/State/GameState.cs index e1383c1ea..1af20e62e 100644 --- a/src/Game/State/GameState.cs +++ b/src/Game/State/GameState.cs @@ -354,7 +354,7 @@ AlbionTask LoadGame(ushort id) { _game = Assets.LoadSavedGame(IdToPath(id)); if (_game == null) - return AlbionTask.Complete; + return AlbionTask.CompletedTask; return InitialiseGame(); } @@ -390,7 +390,7 @@ async AlbionTask InitialiseGame() if (!member.IsNone) _party.AddMember(member); - await RaiseAsync(new LoadMapEvent(_game.MapId)); + await RaiseA(new LoadMapEvent(_game.MapId)); Raise(new StartClockEvent()); Raise(new SetPartyLeaderEvent(_party.Leader.Id, 0, 0)); diff --git a/src/Game/State/Player/InventoryManager.cs b/src/Game/State/Player/InventoryManager.cs index 52fe63f5a..4f944f15c 100644 --- a/src/Game/State/Player/InventoryManager.cs +++ b/src/Game/State/Player/InventoryManager.cs @@ -273,7 +273,7 @@ AlbionTask GetQuantity(bool discard, IInventory inventory, ItemSlotId slotI var (sprite, subId, _) = GetSprite(slot.Item); var promptEvent = new ItemQuantityPromptEvent(new StringId(text), sprite, subId, slot.Amount, slotId == ItemSlotId.Gold); - return RaiseQueryAsync(promptEvent); + return RaiseQueryA(promptEvent); /* if (RaiseAsync(, continuation) == 0) { @@ -365,7 +365,7 @@ async AlbionTask OnSlotEvent(InventorySlotEvent e) ItemSlot temp = new ItemSlot(new InventorySlotId(InventoryType.Temporary, 0, 0)); temp.TransferFrom(_hand, null, _getItem); SetCursor(); - await RaiseAsync(transitionEvent); + await RaiseA(transitionEvent); slot.TransferFrom(temp, null, _getItem); } @@ -403,8 +403,8 @@ async AlbionTask OnSlotEvent(InventorySlotEvent e) temp1.TransferFrom(_hand, null, _getItem); temp2.TransferFrom(slot, null, _getItem); - var transition1 = RaiseAsync(transitionEvent1); - var transition2 = RaiseAsync(transitionEvent2); + var transition1 = RaiseA(transitionEvent1); + var transition2 = RaiseA(transitionEvent2); await transition1; await transition2; @@ -453,7 +453,7 @@ async AlbionTask OnDiscard(InventoryDiscardEvent e) _ => Base.SystemText.InvMsg_ReallyThrowTheseItemsAway, }; - var response = await RaiseQueryAsync(new YesNoPromptEvent(prompt)); + var response = await RaiseQueryA(new YesNoPromptEvent(prompt)); if (!response) return; @@ -465,7 +465,7 @@ async AlbionTask OnDiscard(InventoryDiscardEvent e) var transitions = new AlbionTask[transitionsToShow]; for (int i = 0; i < transitionsToShow; i++) - transitions[i] = RaiseAsync(new GravityItemTransitionEvent(slot.Item, e.NormX, e.NormY)); + transitions[i] = RaiseA(new GravityItemTransitionEvent(slot.Item, e.NormX, e.NormY)); foreach (var transition in transitions) await transition; @@ -620,12 +620,12 @@ async AlbionTask OnDrinkItem(DrinkItemEvent e) if (item.TypeId != ItemType.Drink) return; - var result = await RaiseQueryAsync(new PartyMemberPromptEvent(Base.SystemText.InvMsg_WhoShouldDrinkThis)); + var result = await RaiseQueryA(new PartyMemberPromptEvent(Base.SystemText.InvMsg_WhoShouldDrinkThis)); if (result == PartyMemberId.None) return; - await RaiseAsync(new SetContextEvent(ContextType.Subject, result)); + await RaiseA(new SetContextEvent(ContextType.Subject, result)); await TriggerItemChain(slot.Item); slot.Amount--; @@ -637,11 +637,11 @@ AlbionTask OnReadItem(ReadItemEvent e) var inv = _getInventory(e.SlotId.Id); var slot = inv.GetSlot(e.SlotId.Slot); if (slot.Item.Type != AssetType.Item) - return AlbionTask.Complete; + return AlbionTask.CompletedTask; var item = _getItem(slot.Item); if (item.TypeId != ItemType.Document) - return AlbionTask.Complete; + return AlbionTask.CompletedTask; return TriggerItemChain(item.Id); } @@ -708,7 +708,7 @@ async AlbionTask TriggerItemChain(ItemId itemId) eventIndex, new EventSource(eventSet.Id, TriggerType.Action)); - await RaiseAsync(triggerEvent); + await RaiseA(triggerEvent); return; } } diff --git a/src/Game/UAlbion.Game.csproj b/src/Game/UAlbion.Game.csproj index bae49b7de..81f0aa333 100644 --- a/src/Game/UAlbion.Game.csproj +++ b/src/Game/UAlbion.Game.csproj @@ -32,7 +32,7 @@ - + diff --git a/src/Tests/UAlbion.Api.Tests/AsyncTests.cs b/src/Tests/UAlbion.Api.Tests/AsyncTests.cs index f8814f90d..69d4a34dc 100644 --- a/src/Tests/UAlbion.Api.Tests/AsyncTests.cs +++ b/src/Tests/UAlbion.Api.Tests/AsyncTests.cs @@ -10,7 +10,7 @@ public void TestCompletedTask() { async AlbionTask Foo() { - await AlbionTask.Complete; + await AlbionTask.CompletedTask; } Foo().GetResult(); diff --git a/src/Tests/UAlbion.Api.Tests/BasicComponent.cs b/src/Tests/UAlbion.Api.Tests/BasicComponent.cs index 565f98756..51ca6ffae 100644 --- a/src/Tests/UAlbion.Api.Tests/BasicComponent.cs +++ b/src/Tests/UAlbion.Api.Tests/BasicComponent.cs @@ -9,8 +9,8 @@ public class BasicComponent : Component public int Handled { get; private set; } public T CallResolve() => TryResolve(); public new void Raise(T e) where T : IEvent => base.Raise(e); - public new AlbionTask RaiseAsync(T e) where T : IEvent => base.RaiseAsync(e); - public new AlbionTask RaiseQueryAsync(IQueryEvent e) => base.RaiseQueryAsync(e); + public new AlbionTask RaiseAsync(T e) where T : IEvent => base.RaiseA(e); + public new AlbionTask RaiseQueryAsync(IQueryEvent e) => base.RaiseQueryA(e); public new void Enqueue(IEvent e) => base.Enqueue(e); public void AddHandler(Action handler) where T : IEvent { diff --git a/src/Tests/UAlbion.Api.Tests/EventExchangeTests.cs b/src/Tests/UAlbion.Api.Tests/EventExchangeTests.cs index 5ebf0f158..f0992eda2 100644 --- a/src/Tests/UAlbion.Api.Tests/EventExchangeTests.cs +++ b/src/Tests/UAlbion.Api.Tests/EventExchangeTests.cs @@ -226,7 +226,7 @@ public void TestRaiseAsync_HandleAsync_CompleteImmediately() var component = new AdHocComponent("C1", x => x.OnAsync(_ => { received = true; - return AlbionTask.Complete; + return AlbionTask.CompletedTask; })); exchange.Attach(component); @@ -245,7 +245,7 @@ public void TestRaiseAsync_HandleAsync_CompleteImmediately2() bool received1 = false; bool received2 = false; - var component1 = new AdHocComponent("C1", x => x.OnAsync(_ => { received1 = true; return AlbionTask.Complete; })); + var component1 = new AdHocComponent("C1", x => x.OnAsync(_ => { received1 = true; return AlbionTask.CompletedTask; })); var component2 = new AdHocComponent("C2", x => x.On(_ => received2 = true)); exchange.Attach(component1); diff --git a/src/UAlbion/Albion.cs b/src/UAlbion/Albion.cs index a88e31f56..c5fcc3076 100644 --- a/src/UAlbion/Albion.cs +++ b/src/UAlbion/Albion.cs @@ -215,7 +215,7 @@ static void ConfigureMenus(EventExchange eventExchange) new ShowWindowMenuItem("Asset Explorer", "Windows", name => new AssetExplorerWindow(name)), new ShowWindowMenuItem("Asset Viewer", "Windows", name => new AssetViewerWindow(name)), new ShowWindowMenuItem("Breakpoints", "Windows", name => new BreakpointsWindow(name)), - new ShowWindowMenuItem("Script", "Windows", name => new CodeWindow(name)), + new ShowWindowMenuItem("Script", "Windows", name => new ScriptWindow(name)), new ShowWindowMenuItem("Console", "Windows", name => new ImGuiConsoleLogger(name)), new ShowWindowMenuItem("ImGuiDemo", "Windows", name => new DemoWindow(name)), new ShowWindowMenuItem("Inspector Demo", "Windows", name => new InspectorDemoWindow(name)),