diff --git a/mods/Shaders/Shaders/BlendedSpriteSF.h.frag b/mods/Shaders/Shaders/BlendedSpriteSF.h.frag index b77fa0903..30b4d3169 100644 --- a/mods/Shaders/Shaders/BlendedSpriteSF.h.frag +++ b/mods/Shaders/Shaders/BlendedSpriteSF.h.frag @@ -20,7 +20,6 @@ #define SF_FLOOR 0x10U #define SF_BILLBOARD 0x20U #define SF_ONLY_EVEN_FRAMES 0x40U -#define SF_TRANSPARENT 0x80U #define SF_HIGHLIGHT 0x100U #define SF_RED_TINT 0x200U #define SF_GREEN_TINT 0x400U diff --git a/mods/Shaders/Shaders/BlendedSpriteSV.h.vert b/mods/Shaders/Shaders/BlendedSpriteSV.h.vert index 3f1b60603..9dae9383a 100644 --- a/mods/Shaders/Shaders/BlendedSpriteSV.h.vert +++ b/mods/Shaders/Shaders/BlendedSpriteSV.h.vert @@ -20,7 +20,6 @@ #define SF_FLOOR 0x10U #define SF_BILLBOARD 0x20U #define SF_ONLY_EVEN_FRAMES 0x40U -#define SF_TRANSPARENT 0x80U #define SF_HIGHLIGHT 0x100U #define SF_RED_TINT 0x200U #define SF_GREEN_TINT 0x400U diff --git a/mods/Shaders/Shaders/SpriteSF.frag b/mods/Shaders/Shaders/SpriteSF.frag index 917f0bd0b..d33b1002f 100644 --- a/mods/Shaders/Shaders/SpriteSF.frag +++ b/mods/Shaders/Shaders/SpriteSF.frag @@ -15,9 +15,7 @@ vec4 Pal(float color) void main() { - vec2 uv = ((iFlags & SF_FLIP_VERTICAL) != 0) - ? vec2(iTexPosition.x, 1 - iTexPosition.y) - : iTexPosition; + vec2 uv = iTexPosition; if ((uFlags & SKF_CLAMP_EDGES) != 0) uv = vec2(clamp(uv.x, iUvClamp.x, iUvClamp.z), clamp(uv.y, iUvClamp.y, iUvClamp.w)); diff --git a/mods/Shaders/Shaders/SpriteSF.h.frag b/mods/Shaders/Shaders/SpriteSF.h.frag index 1a7207e42..84ff1a388 100644 --- a/mods/Shaders/Shaders/SpriteSF.h.frag +++ b/mods/Shaders/Shaders/SpriteSF.h.frag @@ -20,7 +20,6 @@ #define SF_FLOOR 0x10U #define SF_BILLBOARD 0x20U #define SF_ONLY_EVEN_FRAMES 0x40U -#define SF_TRANSPARENT 0x80U #define SF_HIGHLIGHT 0x100U #define SF_RED_TINT 0x200U #define SF_GREEN_TINT 0x400U diff --git a/mods/Shaders/Shaders/SpriteSV.h.vert b/mods/Shaders/Shaders/SpriteSV.h.vert index ad067805f..67a989afa 100644 --- a/mods/Shaders/Shaders/SpriteSV.h.vert +++ b/mods/Shaders/Shaders/SpriteSV.h.vert @@ -20,7 +20,6 @@ #define SF_FLOOR 0x10U #define SF_BILLBOARD 0x20U #define SF_ONLY_EVEN_FRAMES 0x40U -#define SF_TRANSPARENT 0x80U #define SF_HIGHLIGHT 0x100U #define SF_RED_TINT 0x200U #define SF_GREEN_TINT 0x400U diff --git a/mods/Shaders/Shaders/SpriteSV.vert b/mods/Shaders/Shaders/SpriteSV.vert index aedb2b2df..c3cf4bdb3 100644 --- a/mods/Shaders/Shaders/SpriteSV.vert +++ b/mods/Shaders/Shaders/SpriteSV.vert @@ -47,7 +47,11 @@ void main() } else oUvClamp = vec4(0,0,1,1); - oTexPosition = iTexCoords * iTexSize + iTexOffset; + vec2 uv = ((iFlags & SF_FLIP_VERTICAL) != 0) + ? vec2(iTexCoords.x, 1 - iTexCoords.y) + : iTexCoords; + + oTexPosition = uv * iTexSize + iTexOffset; oLayer = float(iTexLayer); oFlags = iFlags; oNormCoords = iTexCoords; diff --git a/src/Core/Visual/MonsterSprite.cs b/src/Core/Visual/MonsterSprite.cs index 7e89d0944..470f94147 100644 --- a/src/Core/Visual/MonsterSprite.cs +++ b/src/Core/Visual/MonsterSprite.cs @@ -9,7 +9,7 @@ namespace UAlbion.Core.Visual; public class MonsterSprite : Component { readonly Sprite _sprite; - readonly Sprite _shadow; + // readonly Sprite _shadow; Vector2 _scale = Vector2.One; Vector2 _maxSize = Vector2.One; @@ -22,9 +22,9 @@ public MonsterSprite( Func textureLoaderFunc = null, IBatchManager batchManager = null) { - var flags = SpriteFlags.BottomMid | SpriteFlags.Transparent; + var flags = SpriteFlags.TopMid | SpriteFlags.FlipVertical; _sprite = AttachChild(new Sprite(id, layer, keyFlags, flags, textureLoaderFunc, batchManager)); - _shadow = AttachChild(new Sprite(id, layer, keyFlags, flags, textureLoaderFunc, batchManager)); + // _shadow = AttachChild(new Sprite(id, layer, keyFlags, flags, textureLoaderFunc, batchManager)); } protected override void Subscribed() @@ -32,10 +32,13 @@ protected override void Subscribed() base.Subscribed(); _maxSize = Vector2.Zero; - foreach (var region in _sprite.Texture.Regions) + if (_sprite.Texture != null) { - if (region.Width > _maxSize.X) _maxSize.X = region.Width; - if (region.Height > _maxSize.Y) _maxSize.Y = region.Height; + foreach (var region in _sprite.Texture.Regions) + { + if (region.Width > _maxSize.X) _maxSize.X = region.Width; + if (region.Height > _maxSize.Y) _maxSize.Y = region.Height; + } } Update(); @@ -63,7 +66,7 @@ public int Frame return; _sprite.Frame = 2 * value; - _shadow.Frame = 2 * value + 1; + // _shadow.Frame = 2 * value + 1; Update(); } @@ -89,8 +92,8 @@ public Vector2 Scale void Update() { _sprite.Position = _position; - _shadow.Position = _position + new Vector3(0, 0, 0.1f); + // _shadow.Position = _position + new Vector3(0, 0, 0.1f); _sprite.Size = _scale * _sprite.FrameSize; - _shadow.Size = _scale * _shadow.FrameSize; + // _shadow.Size = _scale * _shadow.FrameSize; } } \ No newline at end of file diff --git a/src/Core/Visual/Sprite.cs b/src/Core/Visual/Sprite.cs index 8fab4848b..1f8d3aef2 100644 --- a/src/Core/Visual/Sprite.cs +++ b/src/Core/Visual/Sprite.cs @@ -146,6 +146,7 @@ bool Dirty protected override void Subscribed() { + Dirty = true; UpdateSprite(); Raise(new AddPositionedComponentEvent(this)); } diff --git a/src/Core/Visual/SpriteFlags.cs b/src/Core/Visual/SpriteFlags.cs index 0cba02b3e..46b6da692 100644 --- a/src/Core/Visual/SpriteFlags.cs +++ b/src/Core/Visual/SpriteFlags.cs @@ -34,7 +34,7 @@ public enum SpriteFlags : uint Floor = 0x10, // On the floor rather than standing upright Billboard = 0x20, // Autorotate to face the camera OnlyEvenFrames = 0x40, // Used for monsters etc where the odd frames are shadows - Transparent = 0x80, + // 0x80 Unused Highlight = 0x100, RedTint = 0x200, // Debug Flag GreenTint = 0x400, // Debug Flag diff --git a/src/Game/Combat/Battle.cs b/src/Game/Combat/Battle.cs index 47a7f8f6a..755037dff 100644 --- a/src/Game/Combat/Battle.cs +++ b/src/Game/Combat/Battle.cs @@ -7,6 +7,7 @@ using UAlbion.Formats.Assets.Save; using UAlbion.Formats.Ids; using UAlbion.Game.Gui.Combat; +using UAlbion.Game.Gui.Dialogs; using UAlbion.Game.State; namespace UAlbion.Game.Combat; @@ -17,7 +18,6 @@ namespace UAlbion.Game.Combat; /// public class Battle : GameComponent, IReadOnlyBattle { - readonly MonsterGroupId _groupId; readonly List _mobs = new(); readonly ICombatParticipant[] _tiles = new ICombatParticipant[SavedGame.CombatRows * SavedGame.CombatColumns]; @@ -29,6 +29,7 @@ public Battle(MonsterGroupId groupId, SpriteId backgroundId) { On(_ => Complete?.Invoke()); OnAsync(BeginRoundAsync); + OnAsync(Observe); _groupId = groupId; Mobs = _mobs; @@ -44,7 +45,20 @@ public Battle(MonsterGroupId groupId, SpriteId backgroundId) }); } - AlbionTask BeginRoundAsync(BeginCombatRoundEvent _) => RaiseA(new CombatUpdateEvent(25)); // TODO + AlbionTask Observe(ObserveCombatEvent _) => + WithFrozenClock(this, async x => + { + Raise(new CombatDialog.ShowCombatDialogEvent(false)); + var dlg = x.AttachChild(new InvisibleWaitForClickDialog()); + await dlg.Task; + Raise(new CombatDialog.ShowCombatDialogEvent(true)); + }); + + AlbionTask BeginRoundAsync(BeginCombatRoundEvent _) + { + return RaiseA(new CombatUpdateEvent(100)); + // TODO + } protected override void Subscribed() { diff --git a/src/Game/Combat/CombatManager.cs b/src/Game/Combat/CombatManager.cs index f04321266..036ffd030 100644 --- a/src/Game/Combat/CombatManager.cs +++ b/src/Game/Combat/CombatManager.cs @@ -42,7 +42,7 @@ void BeginCombat(MonsterGroupId groupId, SpriteId backgroundId) var battle = new Battle(groupId, backgroundId); scene.Add(battle); - Raise(new DialogManager.ShowCombatDialogEvent(battle)); + Raise(new DialogManager.CombatDialogEvent(battle)); battle.Complete += () => { diff --git a/src/Game/Combat/Monster.cs b/src/Game/Combat/Monster.cs index 511d376af..2c1189c97 100644 --- a/src/Game/Combat/Monster.cs +++ b/src/Game/Combat/Monster.cs @@ -74,15 +74,41 @@ public override string ToString() void UpdatePosition() { - var tileX = CombatPosition % SavedGame.CombatColumns; - var tileY = CombatPosition / SavedGame.CombatColumns; + int tileX = CombatPosition % SavedGame.CombatColumns; + int tileY = CombatPosition / SavedGame.CombatColumns; - const float fieldWidth = 500.0f; - const float fieldDepth = 500.0f; + /* + Fear1 (1,0) -> -90 90 -260 or -97, 104, -267 (moves around) + Animal1 (0,2) -> -160 80 -145 + Krondir1 (4,2) -> 100 100 -145 - _sprite.Position = new Vector3( - fieldWidth * (tileX / (float)SavedGame.CombatColumns - 0.5f), - 0, - -fieldDepth * (1.0f - tileY / (float)SavedGame.CombatRows)); + ty: -270 -210 -150 -90 -30 (monsters probably never actually go past 3) + tx: -150 -90 -30 30 90 150 + + + Name tx x ty z Unk37 y Unk152 % + Fear1, 1, -090, 0, -270, 2, 105, -48, 100 Flying + Rinrii2, 3, +040, 0, -250, 0, 0, 20, 110 + + Rinrii2, 0, -160, 1, -210, 0, 10, 20, 110 + Krondir1, 2, -025, 1, -190, 2, 90, 13, 100 + Warniak1, 4, +100, 1, -220, 0, 110, -80, 100 Flying + Fear1, 5, +170, 1, -210, 2, 80, -48, 100 Flying + + Animal1, 0, -160, 2, -140, 0, 80, 33, 100 + Skrinn1, 1, -080, 2, -130, 18, 40, 24, 100 + Krondir1, 4, +100, 2, -150, 2, 100, 13, 100 + + */ + + const float fieldWidth = 300.0f; + const float fieldDepth = 240.0f; + const float fieldDepthOffset = 30.0f; + + float x = fieldWidth * ((float)tileX / (SavedGame.CombatColumns - 1) - 0.5f); + float y = -60.0f - _sheet.Monster.Unk152; + float z = -(fieldDepthOffset + fieldDepth * (1.0f - (float)tileY / (SavedGame.CombatRows - 1))); + + _sprite.Position = new Vector3(x, y, z); } -} \ No newline at end of file +} diff --git a/src/Game/Combat/ObserveCombatEvent.cs b/src/Game/Combat/ObserveCombatEvent.cs new file mode 100644 index 000000000..c993b8180 --- /dev/null +++ b/src/Game/Combat/ObserveCombatEvent.cs @@ -0,0 +1,5 @@ +using UAlbion.Api.Eventing; + +namespace UAlbion.Game.Combat; + +public record ObserveCombatEvent : EventRecord; diff --git a/src/Game/EventChainManager.cs b/src/Game/EventChainManager.cs index 4472f8027..fea751bd5 100644 --- a/src/Game/EventChainManager.cs +++ b/src/Game/EventChainManager.cs @@ -12,9 +12,6 @@ namespace UAlbion.Game; public sealed class EventChainManager : ServiceComponent, IEventManager, IDisposable { - static readonly StartClockEvent StartClockEvent = new(); - static readonly StopClockEvent StopClockEvent = new(); - readonly List _contexts = new(); readonly List _breakpoints = new(); public int CurrentDebugContextIndex { get; set; } = -1; @@ -59,19 +56,19 @@ AlbionTask Trigger(TriggerChainEvent e) return AlbionTask.CompletedTask; } - var isClockRunning = Resolve().IsRunning; + var wasClockRunning = Resolve().IsRunning; var firstNode = e.EventSet.Events[e.EntryPoint]; var context = new EventContext(e.Source, (EventContext)Context) { EntryPoint = e.EntryPoint, EventSet = e.EventSet, Node = firstNode, - ClockWasRunning = isClockRunning, + ClockWasRunning = wasClockRunning, LastAction = firstNode.Event as ActionEvent }; - if (isClockRunning) - Raise(StopClockEvent); + if (wasClockRunning) + Raise(StopClockEvent.Instance); ReadyContext(context); _contexts.Add(context); @@ -139,7 +136,7 @@ async AlbionTask Resume(EventContext context) context.Status = EventContextStatus.Completing; if (context.ClockWasRunning) - Raise(StartClockEvent); + Raise(StartClockEvent.Instance); _contexts.Remove(context); diff --git a/src/Game/Events/StartClockEvent.cs b/src/Game/Events/StartClockEvent.cs index 751559630..6985d2df6 100644 --- a/src/Game/Events/StartClockEvent.cs +++ b/src/Game/Events/StartClockEvent.cs @@ -3,4 +3,7 @@ namespace UAlbion.Game.Events; [Event("start_clock", "Resume automatically updating the game clock.")] -public class StartClockEvent : GameEvent { } \ No newline at end of file +public class StartClockEvent : GameEvent +{ + public static StartClockEvent Instance { get; } = new(); +} \ No newline at end of file diff --git a/src/Game/Events/StopClockEvent.cs b/src/Game/Events/StopClockEvent.cs index ecd9e0cbf..aa7053c11 100644 --- a/src/Game/Events/StopClockEvent.cs +++ b/src/Game/Events/StopClockEvent.cs @@ -3,4 +3,7 @@ namespace UAlbion.Game.Events; [Event("stop_clock", "Stop the game clock from advancing automatically.")] -public class StopClockEvent : GameEvent { } \ No newline at end of file +public class StopClockEvent : GameEvent +{ + public static StopClockEvent Instance { get; } = new(); +} \ No newline at end of file diff --git a/src/Game/GameComponent.cs b/src/Game/GameComponent.cs index d955b443f..910afc86f 100644 --- a/src/Game/GameComponent.cs +++ b/src/Game/GameComponent.cs @@ -14,16 +14,28 @@ protected async AlbionTask WithFrozenClock(T context, Func fun { var wasClockRunning = Resolve()?.IsRunning ?? false; if (wasClockRunning) - Raise(new StopClockEvent()); + Raise(StopClockEvent.Instance); await func(context); if (wasClockRunning) - Raise(new StartClockEvent()); + Raise(StartClockEvent.Instance); } } public abstract class GameServiceComponent : ServiceComponent { protected IAssetManager Assets => Resolve(); + + protected async AlbionTask WithFrozenClock(T context, Func func) + { + var wasClockRunning = Resolve()?.IsRunning ?? false; + if (wasClockRunning) + Raise(StopClockEvent.Instance); + + await func(context); + + if (wasClockRunning) + Raise(StartClockEvent.Instance); + } } \ No newline at end of file diff --git a/src/Game/Gui/Combat/CombatDialog.cs b/src/Game/Gui/Combat/CombatDialog.cs index fcd9f73d3..e1f7a53b9 100644 --- a/src/Game/Gui/Combat/CombatDialog.cs +++ b/src/Game/Gui/Combat/CombatDialog.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using UAlbion.Api.Eventing; using UAlbion.Formats.Assets.Save; using UAlbion.Game.Combat; using UAlbion.Game.Gui.Controls; @@ -11,11 +12,17 @@ namespace UAlbion.Game.Gui.Combat; /// public class CombatDialog : Dialog { + public record ShowCombatDialogEvent(bool Show) : EventRecord, IVerboseEvent; readonly IReadOnlyBattle _battle; public CombatDialog(int depth, IReadOnlyBattle battle) : base(DialogPositioning.Center, depth) { On(_ => Remove()); + On(e => + { + foreach(var child in Children) + child.IsActive = e.Show; + }); _battle = battle ?? throw new ArgumentNullException(nameof(battle)); var stack = new List(); diff --git a/src/Game/Gui/Combat/LogicalCombatTile.cs b/src/Game/Gui/Combat/LogicalCombatTile.cs index feab385d5..7b1c0324a 100644 --- a/src/Game/Gui/Combat/LogicalCombatTile.cs +++ b/src/Game/Gui/Combat/LogicalCombatTile.cs @@ -143,7 +143,7 @@ IText S(TextId textId, bool disabled = false) options.Add(new ContextMenuOption( S(Base.SystemText.Combat_Observe), - new NopEvent(), + new ObserveCombatEvent(), ContextMenuGroup.System)); options.Add(new ContextMenuOption( diff --git a/src/Game/Gui/Controls/FixedPosition.cs b/src/Game/Gui/Controls/FixedPosition.cs index 7c6bf7321..d3000f689 100644 --- a/src/Game/Gui/Controls/FixedPosition.cs +++ b/src/Game/Gui/Controls/FixedPosition.cs @@ -10,7 +10,8 @@ public class FixedPosition : UiElement, IFixedSizeUiElement public FixedPosition(Rectangle extents, IUiElement child) { _extents = extents; - AttachChild(child); + if (child != null) + AttachChild(child); } public override Vector2 GetSize() => new(_extents.Width, _extents.Height); diff --git a/src/Game/Gui/DialogManager.cs b/src/Game/Gui/DialogManager.cs index 422b3d117..cb007e5f3 100644 --- a/src/Game/Gui/DialogManager.cs +++ b/src/Game/Gui/DialogManager.cs @@ -15,7 +15,7 @@ namespace UAlbion.Game.Gui; public class DialogManager : ServiceComponent, IDialogManager { - public record ShowCombatDialogEvent(IReadOnlyBattle Battle) : EventRecord, IVerboseEvent; + public record CombatDialogEvent(IReadOnlyBattle Battle) : EventRecord, IVerboseEvent; int MaxLayer => Children.OfType().Select(x => (int?)x.Depth).Max() ?? 0; @@ -34,7 +34,7 @@ public DialogManager() OnQueryAsync(OnPartyMemberPrompt); On(OnMapNumberPrompt); On(_ => AttachChild(new CombatPositionDialog(MaxLayer + 1))); - On(e => AttachChild(new CombatDialog(MaxLayer + 1, e.Battle))); + On(e => AttachChild(new CombatDialog(MaxLayer + 1, e.Battle))); } AlbionTask OnYesNoPrompt(YesNoPromptEvent e) diff --git a/src/Game/Gui/Dialogs/ConversationTextWindow.cs b/src/Game/Gui/Dialogs/ConversationTextWindow.cs index 4404e0a0d..4ccf111db 100644 --- a/src/Game/Gui/Dialogs/ConversationTextWindow.cs +++ b/src/Game/Gui/Dialogs/ConversationTextWindow.cs @@ -1,6 +1,5 @@ using System; using System.Diagnostics; -using System.Numerics; using UAlbion.Api; using UAlbion.Api.Eventing; using UAlbion.Core.Events; @@ -19,7 +18,7 @@ public class ConversationTextWindow : ModalDialog public ConversationTextWindow(int depth) : base(DialogPositioning.Bottom, depth) { - On(_ => Complete()); + On(e => { Complete(); e.Propagating = false; }); On(_ => Complete()); _uiText = new UiText(_text).Scrollable(); diff --git a/src/Game/Gui/Dialogs/InvisibleWaitForClickDialog.cs b/src/Game/Gui/Dialogs/InvisibleWaitForClickDialog.cs new file mode 100644 index 000000000..18befd4fd --- /dev/null +++ b/src/Game/Gui/Dialogs/InvisibleWaitForClickDialog.cs @@ -0,0 +1,28 @@ +using UAlbion.Api.Eventing; +using UAlbion.Core; +using UAlbion.Game.Events; +using UAlbion.Game.Gui.Controls; + +namespace UAlbion.Game.Gui.Dialogs; + +public class InvisibleWaitForClickDialog : ModalDialog +{ + readonly AlbionTaskCore _source = new(nameof(InvisibleWaitForClickDialog)); + public AlbionTask Task => _source.UntypedTask; + + public InvisibleWaitForClickDialog( int depth = 0) : base(DialogPositioning.Top, depth) + { + On(_ => Close()); + On(_ => Close()); + On(e => { Close(); e.Propagating = false; }); + On(e => { Close(); e.Propagating = false; }); + + AttachChild(new FixedPosition(UiConstants.UiExtents, null)); + } + + void Close() + { + Remove(); + _source.Complete(); + } +} \ No newline at end of file diff --git a/src/Game/Gui/Text/ConversationManager.cs b/src/Game/Gui/Text/ConversationManager.cs index 90ddd62ff..dd8453b29 100644 --- a/src/Game/Gui/Text/ConversationManager.cs +++ b/src/Game/Gui/Text/ConversationManager.cs @@ -1,7 +1,5 @@ using UAlbion.Api; using UAlbion.Api.Eventing; -using UAlbion.Config; -using UAlbion.Core; using UAlbion.Formats.Assets.Sheets; using UAlbion.Formats.Ids; using UAlbion.Formats.MapEvents; @@ -101,20 +99,15 @@ SpriteId GetPortrait(SheetId id) return leader.PortraitId; } - async AlbionTask StartDialogueCommon(PartyMemberId left, ICharacterSheet right) - { - var wasRunning = Resolve().IsRunning; - if (wasRunning) - await RaiseA(new StopClockEvent()); - - Conversation = AttachChild(new Conversation(left, right)); - await Conversation.Run(); - Conversation.Remove(); - Conversation = null; - - if (wasRunning) - await RaiseA(new StartClockEvent()); - } + AlbionTask StartDialogueCommon(PartyMemberId left, ICharacterSheet right) => + WithFrozenClock((this, left, right), async static tuple => + { + var (x, left, right) = tuple; + x.Conversation = x.AttachChild(new Conversation(left, right)); + await x.Conversation.Run(); + x.Conversation.Remove(); + x.Conversation = null; + }); async AlbionTask StartDialogue(StartDialogueEvent e) { diff --git a/src/Tests/UAlbion.Api.Tests/BasicComponent.cs b/src/Tests/UAlbion.Api.Tests/BasicComponent.cs index 51ca6ffae..ba630375f 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.RaiseA(e); - public new AlbionTask RaiseQueryAsync(IQueryEvent e) => base.RaiseQueryA(e); + public new AlbionTask RaiseA(T e) where T : IEvent => base.RaiseA(e); + public new AlbionTask RaiseQueryA(IQueryEvent e) => base.RaiseQueryA(e); public new void Enqueue(IEvent e) => base.Enqueue(e); public void AddHandler(Action handler) where T : IEvent {