refactor(runtime): own canonical action state
Move selection, combat, and interaction target mode under one Runtime owner; make plugins, retained UI, session routing, and typed runtime views borrow its exact children; and add failure-safe reset, instance isolation, source ownership, and normalized checkpoint coverage without changing retail ordering. Co-authored-by: Codex <codex@openai.com>
This commit is contained in:
parent
bb45afef33
commit
b298f99f91
38 changed files with 711 additions and 93 deletions
|
|
@ -44,9 +44,8 @@ internal sealed record InteractionRetainedUiDependencies(
|
||||||
RetainedUiInputCaptureSlot RetainedInputCapture,
|
RetainedUiInputCaptureSlot RetainedInputCapture,
|
||||||
InputDispatcher? InputDispatcher,
|
InputDispatcher? InputDispatcher,
|
||||||
RuntimeSettingsController Settings,
|
RuntimeSettingsController Settings,
|
||||||
CombatState Combat,
|
RuntimeActionState Actions,
|
||||||
ICombatAttackOperations CombatAttackOperations,
|
ICombatAttackOperations CombatAttackOperations,
|
||||||
SelectionState Selection,
|
|
||||||
RuntimeInventoryState Inventory,
|
RuntimeInventoryState Inventory,
|
||||||
MagicCatalog MagicCatalog,
|
MagicCatalog MagicCatalog,
|
||||||
RuntimeCharacterState Character,
|
RuntimeCharacterState Character,
|
||||||
|
|
@ -257,14 +256,14 @@ internal sealed class RetailInteractionRetainedUiCompositionFactory
|
||||||
{
|
{
|
||||||
public CombatAttackController CreateCombatAttack(
|
public CombatAttackController CreateCombatAttack(
|
||||||
InteractionRetainedUiDependencies d) =>
|
InteractionRetainedUiDependencies d) =>
|
||||||
new(d.Combat, d.CombatAttackOperations);
|
new(d.Actions.Combat, d.CombatAttackOperations);
|
||||||
|
|
||||||
public CombatTargetController CreateCombatTarget(
|
public CombatTargetController CreateCombatTarget(
|
||||||
InteractionRetainedUiDependencies d,
|
InteractionRetainedUiDependencies d,
|
||||||
DeferredSelectionUiAuthority selection) =>
|
DeferredSelectionUiAuthority selection) =>
|
||||||
new(
|
new(
|
||||||
d.Combat,
|
d.Actions.Combat,
|
||||||
d.Selection,
|
d.Actions.Selection,
|
||||||
autoTarget: () => d.Settings.Gameplay.AutoTarget,
|
autoTarget: () => d.Settings.Gameplay.AutoTarget,
|
||||||
selectClosestTarget: () =>
|
selectClosestTarget: () =>
|
||||||
selection.SelectClosestCombatTarget(showToast: false));
|
selection.SelectClosestCombatTarget(showToast: false));
|
||||||
|
|
@ -286,6 +285,7 @@ internal sealed class RetailInteractionRetainedUiCompositionFactory
|
||||||
return new ItemInteractionController(
|
return new ItemInteractionController(
|
||||||
d.Inventory.Objects,
|
d.Inventory.Objects,
|
||||||
d.Inventory.Transactions,
|
d.Inventory.Transactions,
|
||||||
|
d.Actions.Interaction,
|
||||||
playerGuid: () => d.PlayerIdentity.ServerGuid,
|
playerGuid: () => d.PlayerIdentity.ServerGuid,
|
||||||
sendUse: null,
|
sendUse: null,
|
||||||
sendExamine: guid => session.CurrentSession?.SendAppraise(guid),
|
sendExamine: guid => session.CurrentSession?.SendAppraise(guid),
|
||||||
|
|
@ -304,8 +304,8 @@ internal sealed class RetailInteractionRetainedUiCompositionFactory
|
||||||
d.PlayerMode.IsPlayerMode
|
d.PlayerMode.IsPlayerMode
|
||||||
&& d.PlayerController.Controller is { IsAirborne: false },
|
&& d.PlayerController.Controller is { IsAirborne: false },
|
||||||
inNonCombatMode: () =>
|
inNonCombatMode: () =>
|
||||||
d.Combat.CurrentMode == CombatMode.NonCombat,
|
d.Actions.Combat.CurrentMode == CombatMode.NonCombat,
|
||||||
combatState: d.Combat,
|
combatState: d.Actions.Combat,
|
||||||
sendChangeCombatMode: mode =>
|
sendChangeCombatMode: mode =>
|
||||||
session.CurrentSession?.SendChangeCombatMode(mode),
|
session.CurrentSession?.SendChangeCombatMode(mode),
|
||||||
isComponentPack: d.MagicCatalog.IsComponentPack,
|
isComponentPack: d.MagicCatalog.IsComponentPack,
|
||||||
|
|
@ -316,7 +316,8 @@ internal sealed class RetailInteractionRetainedUiCompositionFactory
|
||||||
d.Inventory.ExternalContainers.CurrentContainerId,
|
d.Inventory.ExternalContainers.CurrentContainerId,
|
||||||
sendSplitToWorld: (item, amount) =>
|
sendSplitToWorld: (item, amount) =>
|
||||||
session.CurrentSession?.SendStackableSplitTo3D(item, amount),
|
session.CurrentSession?.SendStackableSplitTo3D(item, amount),
|
||||||
selectedObjectId: () => d.Selection.SelectedObjectId ?? 0u,
|
selectedObjectId: () =>
|
||||||
|
d.Actions.Selection.SelectedObjectId ?? 0u,
|
||||||
stackSplitQuantity: d.StackSplitQuantity,
|
stackSplitQuantity: d.StackSplitQuantity,
|
||||||
systemMessage:
|
systemMessage:
|
||||||
text => d.Communication.Chat.OnSystemMessage(text, 0x1Au),
|
text => d.Communication.Chat.OnSystemMessage(text, 0x1Au),
|
||||||
|
|
@ -351,7 +352,9 @@ internal sealed class RetailInteractionRetainedUiCompositionFactory
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
VitalsVM vitals = d.ExistingVitals
|
VitalsVM vitals = d.ExistingVitals
|
||||||
?? new VitalsVM(d.Combat, d.Character.LocalPlayer);
|
?? new VitalsVM(
|
||||||
|
d.Actions.Combat,
|
||||||
|
d.Character.LocalPlayer);
|
||||||
UiHost host = lease.AcquireHost(
|
UiHost host = lease.AcquireHost(
|
||||||
() => new UiHost(
|
() => new UiHost(
|
||||||
d.Gl,
|
d.Gl,
|
||||||
|
|
@ -367,7 +370,8 @@ internal sealed class RetailInteractionRetainedUiCompositionFactory
|
||||||
itemInteraction,
|
itemInteraction,
|
||||||
worldTargetProvider: () =>
|
worldTargetProvider: () =>
|
||||||
late.Selection.PickAtCursor(includeSelf: true) ?? 0u,
|
late.Selection.PickAtCursor(includeSelf: true) ?? 0u,
|
||||||
combatModeProvider: () => d.Combat.CurrentMode);
|
combatModeProvider: () =>
|
||||||
|
d.Actions.Combat.CurrentMode);
|
||||||
var cursorManager = new RetailCursorManager(d.Dats, d.DatLock);
|
var cursorManager = new RetailCursorManager(d.Dats, d.DatLock);
|
||||||
checkpoint(InteractionRetainedUiCompositionPoint.CursorAssetsCreated);
|
checkpoint(InteractionRetainedUiCompositionPoint.CursorAssetsCreated);
|
||||||
|
|
||||||
|
|
@ -404,7 +408,8 @@ internal sealed class RetailInteractionRetainedUiCompositionFactory
|
||||||
d.MagicCatalog,
|
d.MagicCatalog,
|
||||||
d.Character.Spellbook,
|
d.Character.Spellbook,
|
||||||
d.Inventory.Objects,
|
d.Inventory.Objects,
|
||||||
selectedObject: () => d.Selection.SelectedObjectId,
|
selectedObject: () =>
|
||||||
|
d.Actions.Selection.SelectedObjectId,
|
||||||
localPlayerId: () => d.PlayerIdentity.ServerGuid,
|
localPlayerId: () => d.PlayerIdentity.ServerGuid,
|
||||||
accountName: () => late.Session.AccountName
|
accountName: () => late.Session.AccountName
|
||||||
?? d.Options.LiveUser
|
?? d.Options.LiveUser
|
||||||
|
|
@ -533,10 +538,10 @@ internal sealed class RetailInteractionRetainedUiCompositionFactory
|
||||||
Chat: new ChatRuntimeBindings(chat, () => late.Session.Commands),
|
Chat: new ChatRuntimeBindings(chat, () => late.Session.Commands),
|
||||||
Radar: new RadarRuntimeBindings(
|
Radar: new RadarRuntimeBindings(
|
||||||
late.Radar.Snapshot,
|
late.Radar.Snapshot,
|
||||||
d.Selection,
|
d.Actions.Selection,
|
||||||
d.Settings.SetUiLocked),
|
d.Settings.SetUiLocked),
|
||||||
Combat: new CombatRuntimeBindings(
|
Combat: new CombatRuntimeBindings(
|
||||||
d.Combat,
|
d.Actions.Combat,
|
||||||
combatAttack,
|
combatAttack,
|
||||||
() => d.Settings.Gameplay,
|
() => d.Settings.Gameplay,
|
||||||
d.Settings.SetCombatGameplay),
|
d.Settings.SetCombatGameplay),
|
||||||
|
|
@ -550,11 +555,13 @@ internal sealed class RetailInteractionRetainedUiCompositionFactory
|
||||||
iconComposer.GetDragIcon,
|
iconComposer.GetDragIcon,
|
||||||
iconComposer.GetSpellIcon,
|
iconComposer.GetSpellIcon,
|
||||||
iconComposer.GetSpellComponentIcon,
|
iconComposer.GetSpellComponentIcon,
|
||||||
d.Selection,
|
d.Actions.Selection,
|
||||||
d.MagicCatalog.GetSpellLevel,
|
d.MagicCatalog.GetSpellLevel,
|
||||||
magic.GetExamineComponents,
|
magic.GetExamineComponents,
|
||||||
MagicSkillLevel,
|
MagicSkillLevel,
|
||||||
guid => d.Selection.Select(guid, SelectionChangeSource.Inventory),
|
guid => d.Actions.Selection.Select(
|
||||||
|
guid,
|
||||||
|
SelectionChangeSource.Inventory),
|
||||||
guid => late.Session.TryUseItem(guid, d.Log),
|
guid => late.Session.TryUseItem(guid, d.Log),
|
||||||
(tab, position, spellId) =>
|
(tab, position, spellId) =>
|
||||||
late.GameRuntime.AddFavorite(tab, position, spellId),
|
late.GameRuntime.AddFavorite(tab, position, spellId),
|
||||||
|
|
@ -574,7 +581,7 @@ internal sealed class RetailInteractionRetainedUiCompositionFactory
|
||||||
() => 1.0,
|
() => 1.0,
|
||||||
() => d.Settings.DisplayPreview.ShowFps),
|
() => d.Settings.DisplayPreview.ShowFps),
|
||||||
VividTarget: new VividTargetRuntimeBindings(
|
VividTarget: new VividTargetRuntimeBindings(
|
||||||
d.Selection,
|
d.Actions.Selection,
|
||||||
() => d.PlayerIdentity.ServerGuid,
|
() => d.PlayerIdentity.ServerGuid,
|
||||||
() => d.Settings.Gameplay.VividTargetingIndicator,
|
() => d.Settings.Gameplay.VividTargetingIndicator,
|
||||||
late.Selection.ResolveVividTargetInfo,
|
late.Selection.ResolveVividTargetInfo,
|
||||||
|
|
@ -596,19 +603,19 @@ internal sealed class RetailInteractionRetainedUiCompositionFactory
|
||||||
iconComposer.GetIcon,
|
iconComposer.GetIcon,
|
||||||
iconComposer.GetDragIcon,
|
iconComposer.GetDragIcon,
|
||||||
guid => late.Session.TryUseItem(guid, d.Log),
|
guid => late.Session.TryUseItem(guid, d.Log),
|
||||||
d.Combat,
|
d.Actions.Combat,
|
||||||
d.Inventory.ItemMana,
|
d.Inventory.ItemMana,
|
||||||
d.CombatModeCommands.Toggle,
|
d.CombatModeCommands.Toggle,
|
||||||
itemInteraction,
|
itemInteraction,
|
||||||
entry => late.GameRuntime.AddShortcut(entry),
|
entry => late.GameRuntime.AddShortcut(entry),
|
||||||
index => late.GameRuntime.RemoveShortcut(index),
|
index => late.GameRuntime.RemoveShortcut(index),
|
||||||
d.Selection,
|
d.Actions.Selection,
|
||||||
handler => d.Combat.HealthChanged += handler,
|
handler => d.Actions.Combat.HealthChanged += handler,
|
||||||
handler => d.Combat.HealthChanged -= handler,
|
handler => d.Actions.Combat.HealthChanged -= handler,
|
||||||
late.Selection.ShouldShowHealth,
|
late.Selection.ShouldShowHealth,
|
||||||
guid => d.Inventory.Objects.Get(guid)?.GetAppropriateName(),
|
guid => d.Inventory.Objects.Get(guid)?.GetAppropriateName(),
|
||||||
d.Combat.GetHealthPercent,
|
d.Actions.Combat.GetHealthPercent,
|
||||||
d.Combat.HasHealth,
|
d.Actions.Combat.HasHealth,
|
||||||
guid =>
|
guid =>
|
||||||
(uint)(d.Inventory.Objects.Get(guid)?.StackSize ?? 0),
|
(uint)(d.Inventory.Objects.Get(guid)?.StackSize ?? 0),
|
||||||
guid => late.Session.CurrentSession?.SendQueryHealth(guid),
|
guid => late.Session.CurrentSession?.SendQueryHealth(guid),
|
||||||
|
|
@ -646,14 +653,14 @@ internal sealed class RetailInteractionRetainedUiCompositionFactory
|
||||||
target,
|
target,
|
||||||
amount),
|
amount),
|
||||||
itemInteraction,
|
itemInteraction,
|
||||||
d.Selection),
|
d.Actions.Selection),
|
||||||
ExternalContainer: new ExternalContainerRuntimeBindings(
|
ExternalContainer: new ExternalContainerRuntimeBindings(
|
||||||
d.Inventory.ExternalContainers,
|
d.Inventory.ExternalContainers,
|
||||||
d.Inventory.Objects,
|
d.Inventory.Objects,
|
||||||
iconComposer.GetIcon,
|
iconComposer.GetIcon,
|
||||||
iconComposer.GetDragIcon,
|
iconComposer.GetDragIcon,
|
||||||
itemInteraction,
|
itemInteraction,
|
||||||
d.Selection,
|
d.Actions.Selection,
|
||||||
guid => late.Session.CurrentSession?.SendUse(guid),
|
guid => late.Session.CurrentSession?.SendUse(guid),
|
||||||
(item, container, placement) =>
|
(item, container, placement) =>
|
||||||
late.Session.CurrentSession?.SendPutItemInContainer(
|
late.Session.CurrentSession?.SendPutItemInContainer(
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ internal sealed record SessionPlayerDependencies(
|
||||||
PhysicsDataCache PhysicsDataCache,
|
PhysicsDataCache PhysicsDataCache,
|
||||||
WorldGameState WorldGameState,
|
WorldGameState WorldGameState,
|
||||||
WorldEvents WorldEvents,
|
WorldEvents WorldEvents,
|
||||||
SelectionState Selection,
|
RuntimeActionState Actions,
|
||||||
RuntimeEntityObjectLifetime EntityObjects,
|
RuntimeEntityObjectLifetime EntityObjects,
|
||||||
EntityClassificationCache ClassificationCache,
|
EntityClassificationCache ClassificationCache,
|
||||||
LiveEntityRuntimeSlot RuntimeSlot,
|
LiveEntityRuntimeSlot RuntimeSlot,
|
||||||
|
|
@ -80,7 +80,6 @@ internal sealed record SessionPlayerDependencies(
|
||||||
UpdateFrameClock UpdateClock,
|
UpdateFrameClock UpdateClock,
|
||||||
MovementTruthDiagnosticController MovementDiagnostics,
|
MovementTruthDiagnosticController MovementDiagnostics,
|
||||||
CombatAttackOperationsSlot CombatAttackOperations,
|
CombatAttackOperationsSlot CombatAttackOperations,
|
||||||
CombatState Combat,
|
|
||||||
CombatFeedbackSlot CombatFeedback,
|
CombatFeedbackSlot CombatFeedback,
|
||||||
RuntimeCommunicationState Communication,
|
RuntimeCommunicationState Communication,
|
||||||
RuntimeCharacterState Character,
|
RuntimeCharacterState Character,
|
||||||
|
|
@ -326,7 +325,7 @@ internal sealed class SessionPlayerCompositionPhase
|
||||||
d.DatLock);
|
d.DatLock);
|
||||||
var worldQuiescence = new WorldGenerationQuiescence(
|
var worldQuiescence = new WorldGenerationQuiescence(
|
||||||
live.WorldAvailability,
|
live.WorldAvailability,
|
||||||
d.Selection,
|
d.Actions.Selection,
|
||||||
live.WorldState,
|
live.WorldState,
|
||||||
guid => live.LiveEntities.TryGetProjectionKey(
|
guid => live.LiveEntities.TryGetProjectionKey(
|
||||||
guid,
|
guid,
|
||||||
|
|
@ -455,7 +454,7 @@ internal sealed class SessionPlayerCompositionPhase
|
||||||
live.EntityEffects,
|
live.EntityEffects,
|
||||||
live.RemoteTeleport,
|
live.RemoteTeleport,
|
||||||
live.SelectionInteractions,
|
live.SelectionInteractions,
|
||||||
d.Selection,
|
d.Actions.Selection,
|
||||||
d.AnimatedEntities,
|
d.AnimatedEntities,
|
||||||
d.RemoteMovementObservations,
|
d.RemoteMovementObservations,
|
||||||
d.TranslucencyFades,
|
d.TranslucencyFades,
|
||||||
|
|
@ -563,9 +562,9 @@ internal sealed class SessionPlayerCompositionPhase
|
||||||
"combat attack operations",
|
"combat attack operations",
|
||||||
d.CombatAttackOperations.BindOwned(
|
d.CombatAttackOperations.BindOwned(
|
||||||
new LiveCombatAttackOperations(
|
new LiveCombatAttackOperations(
|
||||||
d.Combat,
|
d.Actions.Combat,
|
||||||
new CombatAttackTargetSource(
|
new CombatAttackTargetSource(
|
||||||
d.Selection,
|
d.Actions.Selection,
|
||||||
live.LiveEntities,
|
live.LiveEntities,
|
||||||
d.EntityObjects.Objects,
|
d.EntityObjects.Objects,
|
||||||
d.PlayerIdentity),
|
d.PlayerIdentity),
|
||||||
|
|
@ -798,7 +797,7 @@ internal sealed class SessionPlayerCompositionPhase
|
||||||
new LiveSessionDomainRuntime(
|
new LiveSessionDomainRuntime(
|
||||||
d.EntityObjects,
|
d.EntityObjects,
|
||||||
d.Character,
|
d.Character,
|
||||||
d.Combat,
|
d.Actions,
|
||||||
d.Inventory,
|
d.Inventory,
|
||||||
d.Communication),
|
d.Communication),
|
||||||
new LiveSessionUiRuntime(
|
new LiveSessionUiRuntime(
|
||||||
|
|
@ -856,7 +855,7 @@ internal sealed class SessionPlayerCompositionPhase
|
||||||
new LocalPlayerCombatEquipmentSource(
|
new LocalPlayerCombatEquipmentSource(
|
||||||
d.EntityObjects.Objects,
|
d.EntityObjects.Objects,
|
||||||
d.PlayerIdentity),
|
d.PlayerIdentity),
|
||||||
d.Combat,
|
d.Actions.Combat,
|
||||||
new ItemInteractionCombatModeIntentSink(
|
new ItemInteractionCombatModeIntentSink(
|
||||||
interaction.ItemInteraction),
|
interaction.ItemInteraction),
|
||||||
d.Log,
|
d.Log,
|
||||||
|
|
@ -875,10 +874,10 @@ internal sealed class SessionPlayerCompositionPhase
|
||||||
d.Inventory,
|
d.Inventory,
|
||||||
d.Character,
|
d.Character,
|
||||||
d.Communication,
|
d.Communication,
|
||||||
|
d.Actions,
|
||||||
d.PlayerController,
|
d.PlayerController,
|
||||||
worldReveal,
|
worldReveal,
|
||||||
d.UpdateClock,
|
d.UpdateClock,
|
||||||
d.Selection,
|
|
||||||
live.SelectionInteractions,
|
live.SelectionInteractions,
|
||||||
gameplayInput,
|
gameplayInput,
|
||||||
combatCommand);
|
combatCommand);
|
||||||
|
|
@ -940,7 +939,7 @@ internal sealed class SessionPlayerCompositionPhase
|
||||||
GameplayInputActionRouter gameplayActions =
|
GameplayInputActionRouter gameplayActions =
|
||||||
GameplayInputActionRouter.Create(
|
GameplayInputActionRouter.Create(
|
||||||
dispatcher,
|
dispatcher,
|
||||||
d.Combat,
|
d.Actions.Combat,
|
||||||
targets,
|
targets,
|
||||||
d.HostQuiescence,
|
d.HostQuiescence,
|
||||||
d.Log);
|
d.Log);
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ internal sealed record LiveSessionPlayerRuntime(
|
||||||
internal sealed record LiveSessionDomainRuntime(
|
internal sealed record LiveSessionDomainRuntime(
|
||||||
RuntimeEntityObjectLifetime EntityObjects,
|
RuntimeEntityObjectLifetime EntityObjects,
|
||||||
RuntimeCharacterState Character,
|
RuntimeCharacterState Character,
|
||||||
CombatState Combat,
|
RuntimeActionState Actions,
|
||||||
RuntimeInventoryState Inventory,
|
RuntimeInventoryState Inventory,
|
||||||
RuntimeCommunicationState Communication);
|
RuntimeCommunicationState Communication);
|
||||||
|
|
||||||
|
|
@ -134,7 +134,7 @@ internal sealed class LiveSessionRuntimeFactory
|
||||||
SetChatIdentity: _domain.Communication.Chat.SetLocalPlayerGuid,
|
SetChatIdentity: _domain.Communication.Chat.SetLocalPlayerGuid,
|
||||||
MarkPersistent: _world.WorldState.MarkPersistent,
|
MarkPersistent: _world.WorldState.MarkPersistent,
|
||||||
SetVanishProbeIdentity: id => EntityVanishProbe.PlayerGuid = id,
|
SetVanishProbeIdentity: id => EntityVanishProbe.PlayerGuid = id,
|
||||||
ClearCombat: _domain.Combat.Clear),
|
ClearCombat: _domain.Actions.Combat.Clear),
|
||||||
EnteredWorld: new(
|
EnteredWorld: new(
|
||||||
SetActiveCharacter: _interaction.Settings.SetActiveCharacter,
|
SetActiveCharacter: _interaction.Settings.SetActiveCharacter,
|
||||||
RestoreLayout: () => _ui.RetailUi?.RestoreLayout(),
|
RestoreLayout: () => _ui.RetailUi?.RestoreLayout(),
|
||||||
|
|
@ -170,7 +170,7 @@ internal sealed class LiveSessionRuntimeFactory
|
||||||
Spellbook = _domain.Character.ResetSpellbook,
|
Spellbook = _domain.Character.ResetSpellbook,
|
||||||
MagicRuntime = () => _ui.Magic?.Reset(),
|
MagicRuntime = () => _ui.Magic?.Reset(),
|
||||||
CombatAttack = _interaction.CombatAttack.ResetSession,
|
CombatAttack = _interaction.CombatAttack.ResetSession,
|
||||||
CombatState = _domain.Combat.Clear,
|
CombatState = _domain.Actions.Combat.Clear,
|
||||||
ItemMana = _domain.Inventory.ResetItemMana,
|
ItemMana = _domain.Inventory.ResetItemMana,
|
||||||
LocalPlayer = _domain.Character.ResetLocalPlayer,
|
LocalPlayer = _domain.Character.ResetLocalPlayer,
|
||||||
Friends = _domain.Communication.ResetFriends,
|
Friends = _domain.Communication.ResetFriends,
|
||||||
|
|
@ -275,7 +275,7 @@ internal sealed class LiveSessionRuntimeFactory
|
||||||
{
|
{
|
||||||
var skillCreditResolver = new LiveSkillCreditResolver(skillTable);
|
var skillCreditResolver = new LiveSkillCreditResolver(skillTable);
|
||||||
return new LiveCharacterSessionBindings(
|
return new LiveCharacterSessionBindings(
|
||||||
_domain.Combat,
|
_domain.Actions.Combat,
|
||||||
_domain.Character,
|
_domain.Character,
|
||||||
ResolveSkillFormulaBonus: skillCreditResolver.Resolve,
|
ResolveSkillFormulaBonus: skillCreditResolver.Resolve,
|
||||||
OnSkillsUpdated: (runSkill, jumpSkill) =>
|
OnSkillsUpdated: (runSkill, jumpSkill) =>
|
||||||
|
|
|
||||||
|
|
@ -31,10 +31,18 @@ var runtimeOptions = RuntimeOptions.FromEnvironment(datDir);
|
||||||
|
|
||||||
var worldGameState = new AcDream.Core.Plugins.WorldGameState();
|
var worldGameState = new AcDream.Core.Plugins.WorldGameState();
|
||||||
var worldEvents = new AcDream.Core.Plugins.WorldEvents();
|
var worldEvents = new AcDream.Core.Plugins.WorldEvents();
|
||||||
var selection = new AcDream.Core.Selection.SelectionState();
|
|
||||||
var uiRegistry = new AcDream.App.Plugins.BufferedUiRegistry();
|
var uiRegistry = new AcDream.App.Plugins.BufferedUiRegistry();
|
||||||
|
using var window = new GameWindow(
|
||||||
|
runtimeOptions,
|
||||||
|
worldGameState,
|
||||||
|
worldEvents,
|
||||||
|
uiRegistry);
|
||||||
var host = new AppPluginHost(
|
var host = new AppPluginHost(
|
||||||
new SerilogAdapter(Log.Logger), worldGameState, worldEvents, selection, uiRegistry);
|
new SerilogAdapter(Log.Logger),
|
||||||
|
worldGameState,
|
||||||
|
worldEvents,
|
||||||
|
window.Selection,
|
||||||
|
uiRegistry);
|
||||||
|
|
||||||
var pluginsDir = Path.Combine(AppContext.BaseDirectory, "plugins");
|
var pluginsDir = Path.Combine(AppContext.BaseDirectory, "plugins");
|
||||||
Log.Information("scanning plugins in {PluginsDir}", pluginsDir);
|
Log.Information("scanning plugins in {PluginsDir}", pluginsDir);
|
||||||
|
|
@ -67,8 +75,6 @@ try
|
||||||
catch (Exception ex) { Log.Error(ex, "plugin enable failed: {Id}", plugin.Manifest.Id); }
|
catch (Exception ex) { Log.Error(ex, "plugin enable failed: {Id}", plugin.Manifest.Id); }
|
||||||
}
|
}
|
||||||
|
|
||||||
using var window = new GameWindow(
|
|
||||||
runtimeOptions, worldGameState, worldEvents, selection, uiRegistry);
|
|
||||||
window.Run();
|
window.Run();
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,6 @@ public sealed class GameWindow :
|
||||||
private readonly string _datDir;
|
private readonly string _datDir;
|
||||||
private readonly WorldGameState _worldGameState;
|
private readonly WorldGameState _worldGameState;
|
||||||
private readonly WorldEvents _worldEvents;
|
private readonly WorldEvents _worldEvents;
|
||||||
private readonly AcDream.Core.Selection.SelectionState _selection;
|
|
||||||
private readonly HostQuiescenceGate _hostQuiescence = new();
|
private readonly HostQuiescenceGate _hostQuiescence = new();
|
||||||
private IWindow? _window;
|
private IWindow? _window;
|
||||||
private SilkWindowCallbackBinding? _windowCallbacks;
|
private SilkWindowCallbackBinding? _windowCallbacks;
|
||||||
|
|
@ -324,9 +323,12 @@ public sealed class GameWindow :
|
||||||
private AcDream.App.World.LiveEntityRuntime? _liveEntities;
|
private AcDream.App.World.LiveEntityRuntime? _liveEntities;
|
||||||
private AcDream.App.World.LiveEntityLivenessController? _liveEntityLiveness;
|
private AcDream.App.World.LiveEntityLivenessController? _liveEntityLiveness;
|
||||||
|
|
||||||
// J4.1: Runtime owns communication/social state. App, UI, plugins, and
|
// J4/J5.1: Runtime owns communication, selection, combat, and target-mode
|
||||||
// live-session routing borrow these exact instances.
|
// state. App, UI, plugins, and live-session routing borrow exact children.
|
||||||
private readonly RuntimeCommunicationState _runtimeCommunication = new();
|
private readonly RuntimeCommunicationState _runtimeCommunication = new();
|
||||||
|
private readonly RuntimeActionState _runtimeActions = new();
|
||||||
|
public AcDream.Core.Selection.SelectionState Selection =>
|
||||||
|
_runtimeActions.Selection;
|
||||||
public AcDream.Core.Chat.ChatLog Chat => _runtimeCommunication.Chat;
|
public AcDream.Core.Chat.ChatLog Chat => _runtimeCommunication.Chat;
|
||||||
public AcDream.Core.Chat.TurbineChatState TurbineChat =>
|
public AcDream.Core.Chat.TurbineChatState TurbineChat =>
|
||||||
_runtimeCommunication.TurbineChat;
|
_runtimeCommunication.TurbineChat;
|
||||||
|
|
@ -334,7 +336,8 @@ public sealed class GameWindow :
|
||||||
_runtimeCommunication.Friends;
|
_runtimeCommunication.Friends;
|
||||||
public AcDream.Core.Social.SquelchState Squelch =>
|
public AcDream.Core.Social.SquelchState Squelch =>
|
||||||
_runtimeCommunication.Squelch;
|
_runtimeCommunication.Squelch;
|
||||||
public readonly AcDream.Core.Combat.CombatState Combat = new();
|
public AcDream.Core.Combat.CombatState Combat =>
|
||||||
|
_runtimeActions.Combat;
|
||||||
private readonly RuntimeEntityObjectLifetime _runtimeEntityObjects = new();
|
private readonly RuntimeEntityObjectLifetime _runtimeEntityObjects = new();
|
||||||
private readonly RuntimeInventoryState _runtimeInventory;
|
private readonly RuntimeInventoryState _runtimeInventory;
|
||||||
private readonly RuntimeCharacterState _runtimeCharacter;
|
private readonly RuntimeCharacterState _runtimeCharacter;
|
||||||
|
|
@ -554,7 +557,6 @@ public sealed class GameWindow :
|
||||||
AcDream.App.RuntimeOptions options,
|
AcDream.App.RuntimeOptions options,
|
||||||
WorldGameState worldGameState,
|
WorldGameState worldGameState,
|
||||||
WorldEvents worldEvents,
|
WorldEvents worldEvents,
|
||||||
AcDream.Core.Selection.SelectionState selection,
|
|
||||||
AcDream.App.Plugins.BufferedUiRegistry? uiRegistry = null)
|
AcDream.App.Plugins.BufferedUiRegistry? uiRegistry = null)
|
||||||
{
|
{
|
||||||
_options = options ?? throw new System.ArgumentNullException(nameof(options));
|
_options = options ?? throw new System.ArgumentNullException(nameof(options));
|
||||||
|
|
@ -584,7 +586,6 @@ public sealed class GameWindow :
|
||||||
new JsonRuntimeSettingsStorage(
|
new JsonRuntimeSettingsStorage(
|
||||||
AcDream.UI.Abstractions.Panels.Settings.SettingsStore.DefaultPath()),
|
AcDream.UI.Abstractions.Panels.Settings.SettingsStore.DefaultPath()),
|
||||||
log: Console.WriteLine);
|
log: Console.WriteLine);
|
||||||
_selection = selection ?? throw new System.ArgumentNullException(nameof(selection));
|
|
||||||
_animationDiagnostics = AnimationPresentationDiagnostics.FromEnvironment();
|
_animationDiagnostics = AnimationPresentationDiagnostics.FromEnvironment();
|
||||||
_uiRegistry = uiRegistry;
|
_uiRegistry = uiRegistry;
|
||||||
_animatedEntities = new LiveEntityAnimationRuntimeView<LiveEntityAnimationState>(
|
_animatedEntities = new LiveEntityAnimationRuntimeView<LiveEntityAnimationState>(
|
||||||
|
|
@ -1252,9 +1253,8 @@ public sealed class GameWindow :
|
||||||
_retainedInputCapture,
|
_retainedInputCapture,
|
||||||
hostInputCamera.InputDispatcher,
|
hostInputCamera.InputDispatcher,
|
||||||
_runtimeSettings,
|
_runtimeSettings,
|
||||||
Combat,
|
_runtimeActions,
|
||||||
_combatAttackOperations,
|
_combatAttackOperations,
|
||||||
_selection,
|
|
||||||
_runtimeInventory,
|
_runtimeInventory,
|
||||||
contentEffectsAudio.MagicCatalog,
|
contentEffectsAudio.MagicCatalog,
|
||||||
_runtimeCharacter,
|
_runtimeCharacter,
|
||||||
|
|
@ -1304,7 +1304,7 @@ public sealed class GameWindow :
|
||||||
_physicsDataCache,
|
_physicsDataCache,
|
||||||
_worldGameState,
|
_worldGameState,
|
||||||
_worldEvents,
|
_worldEvents,
|
||||||
_selection,
|
Selection,
|
||||||
_runtimeEntityObjects,
|
_runtimeEntityObjects,
|
||||||
_liveEntityRuntimeSlot,
|
_liveEntityRuntimeSlot,
|
||||||
_liveEntityMotionBindings,
|
_liveEntityMotionBindings,
|
||||||
|
|
@ -1362,7 +1362,7 @@ public sealed class GameWindow :
|
||||||
_physicsDataCache,
|
_physicsDataCache,
|
||||||
_worldGameState,
|
_worldGameState,
|
||||||
_worldEvents,
|
_worldEvents,
|
||||||
_selection,
|
_runtimeActions,
|
||||||
_runtimeEntityObjects,
|
_runtimeEntityObjects,
|
||||||
_classificationCache,
|
_classificationCache,
|
||||||
_liveEntityRuntimeSlot,
|
_liveEntityRuntimeSlot,
|
||||||
|
|
@ -1394,7 +1394,6 @@ public sealed class GameWindow :
|
||||||
_updateFrameClock,
|
_updateFrameClock,
|
||||||
_movementTruthDiagnostics,
|
_movementTruthDiagnostics,
|
||||||
_combatAttackOperations,
|
_combatAttackOperations,
|
||||||
Combat,
|
|
||||||
_combatFeedback,
|
_combatFeedback,
|
||||||
_runtimeCommunication,
|
_runtimeCommunication,
|
||||||
_runtimeCharacter,
|
_runtimeCharacter,
|
||||||
|
|
@ -1444,7 +1443,7 @@ public sealed class GameWindow :
|
||||||
_debugVmRenderFacts,
|
_debugVmRenderFacts,
|
||||||
_inputCapture,
|
_inputCapture,
|
||||||
_cameraInput,
|
_cameraInput,
|
||||||
_selection,
|
Selection,
|
||||||
_animatedEntities,
|
_animatedEntities,
|
||||||
_updateFrameClock,
|
_updateFrameClock,
|
||||||
Combat,
|
Combat,
|
||||||
|
|
@ -1575,6 +1574,7 @@ public sealed class GameWindow :
|
||||||
_runtimeInventory,
|
_runtimeInventory,
|
||||||
_runtimeCharacter,
|
_runtimeCharacter,
|
||||||
_runtimeCommunication,
|
_runtimeCommunication,
|
||||||
|
_runtimeActions,
|
||||||
_renderSceneShadow,
|
_renderSceneShadow,
|
||||||
_livePresentationBindings,
|
_livePresentationBindings,
|
||||||
_entityEffectAdvance,
|
_entityEffectAdvance,
|
||||||
|
|
|
||||||
|
|
@ -87,6 +87,7 @@ internal sealed record LiveShutdownRoots(
|
||||||
RuntimeInventoryState Inventory,
|
RuntimeInventoryState Inventory,
|
||||||
RuntimeCharacterState Character,
|
RuntimeCharacterState Character,
|
||||||
RuntimeCommunicationState Communication,
|
RuntimeCommunicationState Communication,
|
||||||
|
RuntimeActionState Actions,
|
||||||
RenderSceneShadowRuntime? RenderSceneShadow,
|
RenderSceneShadowRuntime? RenderSceneShadow,
|
||||||
LivePresentationRuntimeBindings? PresentationBindings,
|
LivePresentationRuntimeBindings? PresentationBindings,
|
||||||
DeferredEntityEffectAdvanceSource EffectAdvance,
|
DeferredEntityEffectAdvanceSource EffectAdvance,
|
||||||
|
|
@ -398,6 +399,9 @@ internal static class GameWindowShutdownManifest
|
||||||
Hard(
|
Hard(
|
||||||
"runtime inventory state",
|
"runtime inventory state",
|
||||||
live.Inventory.Dispose),
|
live.Inventory.Dispose),
|
||||||
|
Hard(
|
||||||
|
"runtime action state",
|
||||||
|
live.Actions.Dispose),
|
||||||
Hard(
|
Hard(
|
||||||
"runtime entity/object lifetime",
|
"runtime entity/object lifetime",
|
||||||
live.EntityObjects.Dispose),
|
live.EntityObjects.Dispose),
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,6 @@ using AcDream.App.Interaction;
|
||||||
using AcDream.App.Net;
|
using AcDream.App.Net;
|
||||||
using AcDream.App.Streaming;
|
using AcDream.App.Streaming;
|
||||||
using AcDream.App.World;
|
using AcDream.App.World;
|
||||||
using AcDream.Core.Chat;
|
|
||||||
using AcDream.Core.Selection;
|
|
||||||
using AcDream.Runtime;
|
using AcDream.Runtime;
|
||||||
using AcDream.Runtime.Entities;
|
using AcDream.Runtime.Entities;
|
||||||
using AcDream.Runtime.Gameplay;
|
using AcDream.Runtime.Gameplay;
|
||||||
|
|
@ -40,10 +38,10 @@ internal sealed class CurrentGameRuntimeAdapter
|
||||||
RuntimeInventoryState inventory,
|
RuntimeInventoryState inventory,
|
||||||
RuntimeCharacterState character,
|
RuntimeCharacterState character,
|
||||||
RuntimeCommunicationState communication,
|
RuntimeCommunicationState communication,
|
||||||
|
RuntimeActionState actions,
|
||||||
ILocalPlayerControllerSource playerController,
|
ILocalPlayerControllerSource playerController,
|
||||||
WorldRevealCoordinator worldReveal,
|
WorldRevealCoordinator worldReveal,
|
||||||
IGameRuntimeClock clock,
|
IGameRuntimeClock clock,
|
||||||
SelectionState selectionState,
|
|
||||||
SelectionInteractionController selection,
|
SelectionInteractionController selection,
|
||||||
GameplayInputFrameController gameplayInput,
|
GameplayInputFrameController gameplayInput,
|
||||||
ILiveCombatModeCommand combat)
|
ILiveCombatModeCommand combat)
|
||||||
|
|
@ -56,6 +54,7 @@ internal sealed class CurrentGameRuntimeAdapter
|
||||||
inventory,
|
inventory,
|
||||||
character,
|
character,
|
||||||
communication,
|
communication,
|
||||||
|
actions,
|
||||||
playerController,
|
playerController,
|
||||||
worldReveal,
|
worldReveal,
|
||||||
clock);
|
clock);
|
||||||
|
|
@ -73,7 +72,7 @@ internal sealed class CurrentGameRuntimeAdapter
|
||||||
_view,
|
_view,
|
||||||
inventory,
|
inventory,
|
||||||
character,
|
character,
|
||||||
selectionState,
|
actions,
|
||||||
selection,
|
selection,
|
||||||
gameplayInput,
|
gameplayInput,
|
||||||
combat,
|
combat,
|
||||||
|
|
@ -89,6 +88,7 @@ internal sealed class CurrentGameRuntimeAdapter
|
||||||
public IRuntimeCharacterView Character => _view.Character;
|
public IRuntimeCharacterView Character => _view.Character;
|
||||||
public IRuntimeSocialView Social => _view.Social;
|
public IRuntimeSocialView Social => _view.Social;
|
||||||
public IRuntimeChatView Chat => _view.Chat;
|
public IRuntimeChatView Chat => _view.Chat;
|
||||||
|
public IRuntimeActionView Actions => _view.Actions;
|
||||||
public IRuntimeMovementView Movement => _view.Movement;
|
public IRuntimeMovementView Movement => _view.Movement;
|
||||||
public IRuntimePortalView Portal => _view.Portal;
|
public IRuntimePortalView Portal => _view.Portal;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ using AcDream.App.Combat;
|
||||||
using AcDream.App.Input;
|
using AcDream.App.Input;
|
||||||
using AcDream.App.Interaction;
|
using AcDream.App.Interaction;
|
||||||
using AcDream.App.Net;
|
using AcDream.App.Net;
|
||||||
using AcDream.Core.Selection;
|
|
||||||
using AcDream.Core.Items;
|
using AcDream.Core.Items;
|
||||||
using AcDream.Runtime;
|
using AcDream.Runtime;
|
||||||
using AcDream.Runtime.Gameplay;
|
using AcDream.Runtime.Gameplay;
|
||||||
|
|
@ -34,7 +33,7 @@ internal sealed class CurrentGameRuntimeCommandAdapter
|
||||||
private readonly CurrentGameRuntimeViewAdapter _view;
|
private readonly CurrentGameRuntimeViewAdapter _view;
|
||||||
private readonly RuntimeInventoryState _inventory;
|
private readonly RuntimeInventoryState _inventory;
|
||||||
private readonly RuntimeCharacterState _character;
|
private readonly RuntimeCharacterState _character;
|
||||||
private readonly SelectionState _selectionState;
|
private readonly RuntimeActionState _actions;
|
||||||
private readonly SelectionInteractionController _selection;
|
private readonly SelectionInteractionController _selection;
|
||||||
private readonly GameplayInputFrameController _gameplayInput;
|
private readonly GameplayInputFrameController _gameplayInput;
|
||||||
private readonly ILiveCombatModeCommand _combat;
|
private readonly ILiveCombatModeCommand _combat;
|
||||||
|
|
@ -47,7 +46,7 @@ internal sealed class CurrentGameRuntimeCommandAdapter
|
||||||
CurrentGameRuntimeViewAdapter view,
|
CurrentGameRuntimeViewAdapter view,
|
||||||
RuntimeInventoryState inventory,
|
RuntimeInventoryState inventory,
|
||||||
RuntimeCharacterState character,
|
RuntimeCharacterState character,
|
||||||
SelectionState selectionState,
|
RuntimeActionState actions,
|
||||||
SelectionInteractionController selection,
|
SelectionInteractionController selection,
|
||||||
GameplayInputFrameController gameplayInput,
|
GameplayInputFrameController gameplayInput,
|
||||||
ILiveCombatModeCommand combat,
|
ILiveCombatModeCommand combat,
|
||||||
|
|
@ -59,8 +58,8 @@ internal sealed class CurrentGameRuntimeCommandAdapter
|
||||||
_view = view ?? throw new ArgumentNullException(nameof(view));
|
_view = view ?? throw new ArgumentNullException(nameof(view));
|
||||||
_inventory = inventory ?? throw new ArgumentNullException(nameof(inventory));
|
_inventory = inventory ?? throw new ArgumentNullException(nameof(inventory));
|
||||||
_character = character ?? throw new ArgumentNullException(nameof(character));
|
_character = character ?? throw new ArgumentNullException(nameof(character));
|
||||||
_selectionState = selectionState
|
_actions = actions
|
||||||
?? throw new ArgumentNullException(nameof(selectionState));
|
?? throw new ArgumentNullException(nameof(actions));
|
||||||
_selection = selection ?? throw new ArgumentNullException(nameof(selection));
|
_selection = selection ?? throw new ArgumentNullException(nameof(selection));
|
||||||
_gameplayInput = gameplayInput
|
_gameplayInput = gameplayInput
|
||||||
?? throw new ArgumentNullException(nameof(gameplayInput));
|
?? throw new ArgumentNullException(nameof(gameplayInput));
|
||||||
|
|
@ -159,7 +158,7 @@ internal sealed class CurrentGameRuntimeCommandAdapter
|
||||||
&& _selection.HandleInputAction(action)
|
&& _selection.HandleInputAction(action)
|
||||||
? RuntimeCommandStatus.Accepted
|
? RuntimeCommandStatus.Accepted
|
||||||
: RuntimeCommandStatus.Unsupported;
|
: RuntimeCommandStatus.Unsupported;
|
||||||
uint selected = _selectionState.SelectedObjectId ?? 0u;
|
uint selected = _actions.Selection.SelectedObjectId ?? 0u;
|
||||||
_events.EmitCommand(
|
_events.EmitCommand(
|
||||||
RuntimeCommandDomain.Selection,
|
RuntimeCommandDomain.Selection,
|
||||||
(int)command,
|
(int)command,
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ internal sealed class CurrentGameRuntimeViewAdapter : IGameRuntimeView
|
||||||
private readonly IRuntimeCharacterView _characterView;
|
private readonly IRuntimeCharacterView _characterView;
|
||||||
private readonly IRuntimeSocialView _socialView;
|
private readonly IRuntimeSocialView _socialView;
|
||||||
private readonly IRuntimeChatView _chatView;
|
private readonly IRuntimeChatView _chatView;
|
||||||
|
private readonly IRuntimeActionView _actionView;
|
||||||
private readonly MovementView _movementView;
|
private readonly MovementView _movementView;
|
||||||
private readonly PortalView _portalView;
|
private readonly PortalView _portalView;
|
||||||
private bool _active = true;
|
private bool _active = true;
|
||||||
|
|
@ -40,6 +41,7 @@ internal sealed class CurrentGameRuntimeViewAdapter : IGameRuntimeView
|
||||||
RuntimeInventoryState inventory,
|
RuntimeInventoryState inventory,
|
||||||
RuntimeCharacterState character,
|
RuntimeCharacterState character,
|
||||||
RuntimeCommunicationState communication,
|
RuntimeCommunicationState communication,
|
||||||
|
RuntimeActionState actions,
|
||||||
ILocalPlayerControllerSource playerController,
|
ILocalPlayerControllerSource playerController,
|
||||||
WorldRevealCoordinator worldReveal,
|
WorldRevealCoordinator worldReveal,
|
||||||
IGameRuntimeClock clock)
|
IGameRuntimeClock clock)
|
||||||
|
|
@ -61,6 +63,8 @@ internal sealed class CurrentGameRuntimeViewAdapter : IGameRuntimeView
|
||||||
character ?? throw new ArgumentNullException(nameof(character))).View;
|
character ?? throw new ArgumentNullException(nameof(character))).View;
|
||||||
_chatView = communication.View;
|
_chatView = communication.View;
|
||||||
_socialView = communication.SocialView;
|
_socialView = communication.SocialView;
|
||||||
|
_actionView = (
|
||||||
|
actions ?? throw new ArgumentNullException(nameof(actions))).View;
|
||||||
_movementView = new MovementView(
|
_movementView = new MovementView(
|
||||||
playerController
|
playerController
|
||||||
?? throw new ArgumentNullException(nameof(playerController)),
|
?? throw new ArgumentNullException(nameof(playerController)),
|
||||||
|
|
@ -104,6 +108,7 @@ internal sealed class CurrentGameRuntimeViewAdapter : IGameRuntimeView
|
||||||
public IRuntimeCharacterView Character => _characterView;
|
public IRuntimeCharacterView Character => _characterView;
|
||||||
public IRuntimeSocialView Social => _socialView;
|
public IRuntimeSocialView Social => _socialView;
|
||||||
public IRuntimeChatView Chat => _chatView;
|
public IRuntimeChatView Chat => _chatView;
|
||||||
|
public IRuntimeActionView Actions => _actionView;
|
||||||
public IRuntimeMovementView Movement => _movementView;
|
public IRuntimeMovementView Movement => _movementView;
|
||||||
public IRuntimePortalView Portal => _portalView;
|
public IRuntimePortalView Portal => _portalView;
|
||||||
|
|
||||||
|
|
@ -121,6 +126,7 @@ internal sealed class CurrentGameRuntimeViewAdapter : IGameRuntimeView
|
||||||
_socialView.Snapshot,
|
_socialView.Snapshot,
|
||||||
_chatView.Revision,
|
_chatView.Revision,
|
||||||
_chatView.Count,
|
_chatView.Count,
|
||||||
|
_actionView.Snapshot,
|
||||||
_movementView.Snapshot,
|
_movementView.Snapshot,
|
||||||
_portalView.Snapshot);
|
_portalView.Snapshot);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -142,6 +142,7 @@ public static class FixtureProvider
|
||||||
var itemInteraction = new ItemInteractionController(
|
var itemInteraction = new ItemInteractionController(
|
||||||
objects,
|
objects,
|
||||||
new InventoryTransactionState(objects),
|
new InventoryTransactionState(objects),
|
||||||
|
new AcDream.Runtime.Gameplay.InteractionState(),
|
||||||
() => SampleData.PlayerGuid,
|
() => SampleData.PlayerGuid,
|
||||||
sendUse: null,
|
sendUse: null,
|
||||||
sendUseWithTarget: null,
|
sendUseWithTarget: null,
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
using AcDream.Core.Combat;
|
using AcDream.Core.Combat;
|
||||||
|
using AcDream.Runtime.Gameplay;
|
||||||
|
|
||||||
namespace AcDream.App.UI;
|
namespace AcDream.App.UI;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
using System;
|
using System;
|
||||||
using AcDream.Core.Combat;
|
using AcDream.Core.Combat;
|
||||||
using AcDream.Core.Items;
|
using AcDream.Core.Items;
|
||||||
|
using AcDream.Runtime.Gameplay;
|
||||||
|
|
||||||
namespace AcDream.App.UI;
|
namespace AcDream.App.UI;
|
||||||
|
|
||||||
|
|
@ -74,6 +75,7 @@ public sealed class ItemInteractionController : IDisposable
|
||||||
public ItemInteractionController(
|
public ItemInteractionController(
|
||||||
ClientObjectTable objects,
|
ClientObjectTable objects,
|
||||||
InventoryTransactionState transactions,
|
InventoryTransactionState transactions,
|
||||||
|
InteractionState interactionState,
|
||||||
Func<uint> playerGuid,
|
Func<uint> playerGuid,
|
||||||
Action<uint>? sendUse,
|
Action<uint>? sendUse,
|
||||||
Action<uint, uint>? sendUseWithTarget,
|
Action<uint, uint>? sendUseWithTarget,
|
||||||
|
|
@ -82,7 +84,6 @@ public sealed class ItemInteractionController : IDisposable
|
||||||
Action<uint>? sendExamine = null,
|
Action<uint>? sendExamine = null,
|
||||||
Func<long>? nowMs = null,
|
Func<long>? nowMs = null,
|
||||||
Action<string>? toast = null,
|
Action<string>? toast = null,
|
||||||
InteractionState? interactionState = null,
|
|
||||||
Func<bool>? readyForInventoryRequest = null,
|
Func<bool>? readyForInventoryRequest = null,
|
||||||
Func<uint>? activeVendorId = null,
|
Func<uint>? activeVendorId = null,
|
||||||
Func<uint>? groundObjectId = null,
|
Func<uint>? groundObjectId = null,
|
||||||
|
|
@ -133,7 +134,8 @@ public sealed class ItemInteractionController : IDisposable
|
||||||
_dragOnPlayerOpensSecureTrade = dragOnPlayerOpensSecureTrade ?? (() => true);
|
_dragOnPlayerOpensSecureTrade = dragOnPlayerOpensSecureTrade ?? (() => true);
|
||||||
_systemMessage = systemMessage;
|
_systemMessage = systemMessage;
|
||||||
_requestUse = requestUse;
|
_requestUse = requestUse;
|
||||||
_interactionState = interactionState ?? new InteractionState();
|
_interactionState = interactionState
|
||||||
|
?? throw new ArgumentNullException(nameof(interactionState));
|
||||||
_transactions = transactions
|
_transactions = transactions
|
||||||
?? throw new ArgumentNullException(nameof(transactions));
|
?? throw new ArgumentNullException(nameof(transactions));
|
||||||
if (!ReferenceEquals(_transactions.Objects, _objects))
|
if (!ReferenceEquals(_transactions.Objects, _objects))
|
||||||
|
|
|
||||||
23
src/AcDream.Runtime/GameRuntimeActionViews.cs
Normal file
23
src/AcDream.Runtime/GameRuntimeActionViews.cs
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
using AcDream.Core.Combat;
|
||||||
|
using AcDream.Runtime.Gameplay;
|
||||||
|
|
||||||
|
namespace AcDream.Runtime;
|
||||||
|
|
||||||
|
public readonly record struct RuntimeActionSnapshot(
|
||||||
|
long SelectionRevision,
|
||||||
|
uint SelectedObjectId,
|
||||||
|
uint PreviousObjectId,
|
||||||
|
uint PreviousValidObjectId,
|
||||||
|
long CombatRevision,
|
||||||
|
CombatMode CombatMode,
|
||||||
|
int TrackedTargetHealthCount,
|
||||||
|
long InteractionRevision,
|
||||||
|
InteractionModeKind InteractionMode,
|
||||||
|
uint InteractionSourceObjectId);
|
||||||
|
|
||||||
|
public interface IRuntimeActionView
|
||||||
|
{
|
||||||
|
RuntimeActionSnapshot Snapshot { get; }
|
||||||
|
|
||||||
|
bool TryGetHealth(uint objectId, out float healthPercent);
|
||||||
|
}
|
||||||
|
|
@ -162,6 +162,14 @@ public sealed class RuntimeTraceRecorder : IRuntimeEventObserver
|
||||||
$"social={checkpoint.Social.FriendsRevision}:" +
|
$"social={checkpoint.Social.FriendsRevision}:" +
|
||||||
$"{checkpoint.Social.FriendCount}:" +
|
$"{checkpoint.Social.FriendCount}:" +
|
||||||
$"{checkpoint.Social.SquelchRevision};" +
|
$"{checkpoint.Social.SquelchRevision};" +
|
||||||
|
$"actions={checkpoint.Actions.SelectionRevision}:" +
|
||||||
|
$"{checkpoint.Actions.SelectedObjectId:X8}:" +
|
||||||
|
$"{checkpoint.Actions.CombatRevision}:" +
|
||||||
|
$"{(int)checkpoint.Actions.CombatMode}:" +
|
||||||
|
$"{checkpoint.Actions.TrackedTargetHealthCount}:" +
|
||||||
|
$"{checkpoint.Actions.InteractionRevision}:" +
|
||||||
|
$"{(int)checkpoint.Actions.InteractionMode}:" +
|
||||||
|
$"{checkpoint.Actions.InteractionSourceObjectId:X8};" +
|
||||||
$"portal={checkpoint.Portal.Generation}:{(int)checkpoint.Portal.Kind}:" +
|
$"portal={checkpoint.Portal.Generation}:{(int)checkpoint.Portal.Kind}:" +
|
||||||
$"{checkpoint.Portal.DestinationCell:X8}:{checkpoint.Portal.IsReady}")));
|
$"{checkpoint.Portal.DestinationCell:X8}:{checkpoint.Portal.IsReady}")));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -115,6 +115,7 @@ public readonly record struct RuntimeStateCheckpoint(
|
||||||
RuntimeSocialSnapshot Social,
|
RuntimeSocialSnapshot Social,
|
||||||
long ChatRevision,
|
long ChatRevision,
|
||||||
int ChatCount,
|
int ChatCount,
|
||||||
|
RuntimeActionSnapshot Actions,
|
||||||
RuntimeMovementSnapshot Movement,
|
RuntimeMovementSnapshot Movement,
|
||||||
RuntimePortalSnapshot Portal);
|
RuntimePortalSnapshot Portal);
|
||||||
|
|
||||||
|
|
@ -138,6 +139,8 @@ public interface IGameRuntimeView
|
||||||
|
|
||||||
IRuntimeChatView Chat { get; }
|
IRuntimeChatView Chat { get; }
|
||||||
|
|
||||||
|
IRuntimeActionView Actions { get; }
|
||||||
|
|
||||||
IRuntimeMovementView Movement { get; }
|
IRuntimeMovementView Movement { get; }
|
||||||
|
|
||||||
IRuntimePortalView Portal { get; }
|
IRuntimePortalView Portal { get; }
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
namespace AcDream.App.UI;
|
namespace AcDream.Runtime.Gameplay;
|
||||||
|
|
||||||
public enum InteractionModeKind
|
public enum InteractionModeKind
|
||||||
{
|
{
|
||||||
|
|
@ -20,8 +20,9 @@ public readonly record struct InteractionModeTransition(
|
||||||
InteractionMode Current);
|
InteractionMode Current);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Single App-layer owner for retail's pointer interaction mode. Core selection
|
/// Canonical presentation-independent owner for retail's temporary target
|
||||||
/// remains session truth; this state represents temporary UI orchestration.
|
/// mode. App projects this state into a cursor image; it does not own or copy
|
||||||
|
/// the mode.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class InteractionState
|
public sealed class InteractionState
|
||||||
{
|
{
|
||||||
|
|
@ -31,13 +32,16 @@ public sealed class InteractionState
|
||||||
|
|
||||||
public bool EnterUse() => Set(new InteractionMode(InteractionModeKind.Use));
|
public bool EnterUse() => Set(new InteractionMode(InteractionModeKind.Use));
|
||||||
|
|
||||||
public bool EnterExamine() => Set(new InteractionMode(InteractionModeKind.Examine));
|
public bool EnterExamine() =>
|
||||||
|
Set(new InteractionMode(InteractionModeKind.Examine));
|
||||||
|
|
||||||
public bool EnterUseItemOnTarget(uint sourceObjectId)
|
public bool EnterUseItemOnTarget(uint sourceObjectId)
|
||||||
{
|
{
|
||||||
if (sourceObjectId == 0)
|
if (sourceObjectId == 0)
|
||||||
throw new ArgumentOutOfRangeException(nameof(sourceObjectId));
|
throw new ArgumentOutOfRangeException(nameof(sourceObjectId));
|
||||||
return Set(new InteractionMode(InteractionModeKind.UseItemOnTarget, sourceObjectId));
|
return Set(new InteractionMode(
|
||||||
|
InteractionModeKind.UseItemOnTarget,
|
||||||
|
sourceObjectId));
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Clear() => Set(InteractionMode.None);
|
public bool Clear() => Set(InteractionMode.None);
|
||||||
|
|
@ -45,6 +49,7 @@ public sealed class InteractionState
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Publishes the session-reset edge even when the mode is already clear so
|
/// Publishes the session-reset edge even when the mode is already clear so
|
||||||
/// a retry can repair any observer that missed an earlier notification.
|
/// a retry can repair any observer that missed an earlier notification.
|
||||||
|
/// State commits before observers run and every observer is attempted.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void ResetSession()
|
public void ResetSession()
|
||||||
{
|
{
|
||||||
|
|
@ -54,18 +59,30 @@ public sealed class InteractionState
|
||||||
if (listeners is null)
|
if (listeners is null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var transition = new InteractionModeTransition(previous, InteractionMode.None);
|
var transition = new InteractionModeTransition(
|
||||||
|
previous,
|
||||||
|
InteractionMode.None);
|
||||||
List<Exception>? failures = null;
|
List<Exception>? failures = null;
|
||||||
foreach (Action<InteractionModeTransition> listener in listeners.GetInvocationList())
|
foreach (Action<InteractionModeTransition> listener
|
||||||
|
in listeners.GetInvocationList())
|
||||||
{
|
{
|
||||||
try { listener(transition); }
|
try
|
||||||
catch (Exception error) { (failures ??= []).Add(error); }
|
{
|
||||||
|
listener(transition);
|
||||||
}
|
}
|
||||||
|
catch (Exception error)
|
||||||
|
{
|
||||||
|
(failures ??= []).Add(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (failures is not null)
|
if (failures is not null)
|
||||||
|
{
|
||||||
throw new AggregateException(
|
throw new AggregateException(
|
||||||
"One or more interaction-mode reset observers failed.",
|
"One or more interaction-mode reset observers failed.",
|
||||||
failures);
|
failures);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private bool Set(InteractionMode mode)
|
private bool Set(InteractionMode mode)
|
||||||
{
|
{
|
||||||
177
src/AcDream.Runtime/Gameplay/RuntimeActionState.cs
Normal file
177
src/AcDream.Runtime/Gameplay/RuntimeActionState.cs
Normal file
|
|
@ -0,0 +1,177 @@
|
||||||
|
using AcDream.Core.Combat;
|
||||||
|
using AcDream.Core.Selection;
|
||||||
|
|
||||||
|
namespace AcDream.Runtime.Gameplay;
|
||||||
|
|
||||||
|
public readonly record struct RuntimeActionOwnershipSnapshot(
|
||||||
|
bool IsDisposed,
|
||||||
|
bool InternalSubscriptionsAttached,
|
||||||
|
uint SelectedObjectId,
|
||||||
|
uint PreviousObjectId,
|
||||||
|
uint PreviousValidObjectId,
|
||||||
|
CombatMode CombatMode,
|
||||||
|
int TrackedTargetHealthCount,
|
||||||
|
InteractionMode InteractionMode,
|
||||||
|
long SelectionRevision,
|
||||||
|
long CombatRevision,
|
||||||
|
long InteractionRevision)
|
||||||
|
{
|
||||||
|
public bool IsConverged =>
|
||||||
|
IsDisposed
|
||||||
|
&& !InternalSubscriptionsAttached
|
||||||
|
&& SelectedObjectId == 0u
|
||||||
|
&& PreviousObjectId == 0u
|
||||||
|
&& PreviousValidObjectId == 0u
|
||||||
|
&& CombatMode == CombatMode.NonCombat
|
||||||
|
&& TrackedTargetHealthCount == 0
|
||||||
|
&& InteractionMode == InteractionMode.None;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Canonical presentation-independent owner for selection, combat
|
||||||
|
/// notifications/mode, and temporary interaction target mode. Graphical and
|
||||||
|
/// no-window hosts borrow these exact instances.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class RuntimeActionState : IDisposable
|
||||||
|
{
|
||||||
|
private bool _disposed;
|
||||||
|
private bool _internalSubscriptionsAttached;
|
||||||
|
private long _selectionRevision;
|
||||||
|
private long _combatRevision;
|
||||||
|
private long _interactionRevision;
|
||||||
|
|
||||||
|
public RuntimeActionState()
|
||||||
|
{
|
||||||
|
Selection = new SelectionState();
|
||||||
|
Combat = new CombatState();
|
||||||
|
Interaction = new InteractionState();
|
||||||
|
View = new ActionView(this);
|
||||||
|
|
||||||
|
Selection.Changed += OnSelectionChanged;
|
||||||
|
Combat.CombatModeChanged += OnCombatModeChanged;
|
||||||
|
Combat.HealthChanged += OnHealthChanged;
|
||||||
|
Interaction.Changed += OnInteractionChanged;
|
||||||
|
_internalSubscriptionsAttached = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SelectionState Selection { get; }
|
||||||
|
public CombatState Combat { get; }
|
||||||
|
public InteractionState Interaction { get; }
|
||||||
|
public IRuntimeActionView View { get; }
|
||||||
|
public bool IsDisposed => _disposed;
|
||||||
|
|
||||||
|
public RuntimeActionOwnershipSnapshot CaptureOwnership() => new(
|
||||||
|
_disposed,
|
||||||
|
_internalSubscriptionsAttached,
|
||||||
|
Selection.SelectedObjectId ?? 0u,
|
||||||
|
Selection.PreviousObjectId ?? 0u,
|
||||||
|
Selection.PreviousValidObjectId ?? 0u,
|
||||||
|
Combat.CurrentMode,
|
||||||
|
Combat.TrackedTargetCount,
|
||||||
|
Interaction.Current,
|
||||||
|
Interlocked.Read(ref _selectionRevision),
|
||||||
|
Interlocked.Read(ref _combatRevision),
|
||||||
|
Interlocked.Read(ref _interactionRevision));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Restores the action group to the equivalent of a fresh retail character
|
||||||
|
/// session. Each owner commits before notifying observers, so every suffix
|
||||||
|
/// is attempted and a retry converges after observer failure.
|
||||||
|
/// </summary>
|
||||||
|
public void ResetSession()
|
||||||
|
{
|
||||||
|
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||||
|
List<Exception>? failures = null;
|
||||||
|
Try(Interaction.ResetSession, ref failures);
|
||||||
|
Try(() => Selection.Reset(), ref failures);
|
||||||
|
Try(Combat.Clear, ref failures);
|
||||||
|
if (failures is not null)
|
||||||
|
{
|
||||||
|
throw new AggregateException(
|
||||||
|
"Runtime action state did not converge during reset.",
|
||||||
|
failures);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (_disposed)
|
||||||
|
return;
|
||||||
|
|
||||||
|
List<Exception>? failures = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Try(Interaction.ResetSession, ref failures);
|
||||||
|
Try(() => Selection.Reset(), ref failures);
|
||||||
|
Try(Combat.Clear, ref failures);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Selection.Changed -= OnSelectionChanged;
|
||||||
|
Combat.CombatModeChanged -= OnCombatModeChanged;
|
||||||
|
Combat.HealthChanged -= OnHealthChanged;
|
||||||
|
Interaction.Changed -= OnInteractionChanged;
|
||||||
|
_internalSubscriptionsAttached = false;
|
||||||
|
_disposed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (failures is not null)
|
||||||
|
{
|
||||||
|
throw new AggregateException(
|
||||||
|
"Runtime action state did not converge during disposal.",
|
||||||
|
failures);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnSelectionChanged(SelectionTransition _) =>
|
||||||
|
Interlocked.Increment(ref _selectionRevision);
|
||||||
|
|
||||||
|
private void OnCombatModeChanged(CombatMode _) =>
|
||||||
|
Interlocked.Increment(ref _combatRevision);
|
||||||
|
|
||||||
|
private void OnHealthChanged(uint _, float __) =>
|
||||||
|
Interlocked.Increment(ref _combatRevision);
|
||||||
|
|
||||||
|
private void OnInteractionChanged(InteractionModeTransition _) =>
|
||||||
|
Interlocked.Increment(ref _interactionRevision);
|
||||||
|
|
||||||
|
private static void Try(Action action, ref List<Exception>? failures)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
action();
|
||||||
|
}
|
||||||
|
catch (Exception error)
|
||||||
|
{
|
||||||
|
(failures ??= []).Add(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class ActionView(RuntimeActionState owner)
|
||||||
|
: IRuntimeActionView
|
||||||
|
{
|
||||||
|
public RuntimeActionSnapshot Snapshot => new(
|
||||||
|
Interlocked.Read(ref owner._selectionRevision),
|
||||||
|
owner.Selection.SelectedObjectId ?? 0u,
|
||||||
|
owner.Selection.PreviousObjectId ?? 0u,
|
||||||
|
owner.Selection.PreviousValidObjectId ?? 0u,
|
||||||
|
Interlocked.Read(ref owner._combatRevision),
|
||||||
|
owner.Combat.CurrentMode,
|
||||||
|
owner.Combat.TrackedTargetCount,
|
||||||
|
Interlocked.Read(ref owner._interactionRevision),
|
||||||
|
owner.Interaction.Current.Kind,
|
||||||
|
owner.Interaction.Current.SourceObjectId);
|
||||||
|
|
||||||
|
public bool TryGetHealth(uint objectId, out float healthPercent)
|
||||||
|
{
|
||||||
|
if (!owner.Combat.HasHealth(objectId))
|
||||||
|
{
|
||||||
|
healthPercent = 0f;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
healthPercent = owner.Combat.GetHealthPercent(objectId);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,19 +1,21 @@
|
||||||
namespace AcDream.Runtime.Gameplay;
|
namespace AcDream.Runtime.Gameplay;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// One allocation-free ledger over the complete J4 gameplay-state lifetime
|
/// One allocation-free ledger over the gameplay-state lifetime group through
|
||||||
/// group. It contains no state of its own; graphical and no-window hosts
|
/// J5.1. It contains no state of its own; graphical and no-window hosts capture
|
||||||
/// capture the same three canonical owners.
|
/// the same four canonical owners.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly record struct RuntimeGameplayOwnershipSnapshot(
|
public readonly record struct RuntimeGameplayOwnershipSnapshot(
|
||||||
RuntimeInventoryOwnershipSnapshot Inventory,
|
RuntimeInventoryOwnershipSnapshot Inventory,
|
||||||
RuntimeCharacterOwnershipSnapshot Character,
|
RuntimeCharacterOwnershipSnapshot Character,
|
||||||
RuntimeCommunicationOwnershipSnapshot Communication)
|
RuntimeCommunicationOwnershipSnapshot Communication,
|
||||||
|
RuntimeActionOwnershipSnapshot Actions)
|
||||||
{
|
{
|
||||||
public bool IsConverged =>
|
public bool IsConverged =>
|
||||||
Inventory.IsConverged
|
Inventory.IsConverged
|
||||||
&& Character.IsConverged
|
&& Character.IsConverged
|
||||||
&& Communication.IsConverged;
|
&& Communication.IsConverged
|
||||||
|
&& Actions.IsConverged;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class RuntimeGameplayOwnership
|
public static class RuntimeGameplayOwnership
|
||||||
|
|
@ -21,14 +23,17 @@ public static class RuntimeGameplayOwnership
|
||||||
public static RuntimeGameplayOwnershipSnapshot Capture(
|
public static RuntimeGameplayOwnershipSnapshot Capture(
|
||||||
RuntimeInventoryState inventory,
|
RuntimeInventoryState inventory,
|
||||||
RuntimeCharacterState character,
|
RuntimeCharacterState character,
|
||||||
RuntimeCommunicationState communication)
|
RuntimeCommunicationState communication,
|
||||||
|
RuntimeActionState actions)
|
||||||
{
|
{
|
||||||
ArgumentNullException.ThrowIfNull(inventory);
|
ArgumentNullException.ThrowIfNull(inventory);
|
||||||
ArgumentNullException.ThrowIfNull(character);
|
ArgumentNullException.ThrowIfNull(character);
|
||||||
ArgumentNullException.ThrowIfNull(communication);
|
ArgumentNullException.ThrowIfNull(communication);
|
||||||
|
ArgumentNullException.ThrowIfNull(actions);
|
||||||
return new RuntimeGameplayOwnershipSnapshot(
|
return new RuntimeGameplayOwnershipSnapshot(
|
||||||
inventory.CaptureOwnership(),
|
inventory.CaptureOwnership(),
|
||||||
character.CaptureOwnership(),
|
character.CaptureOwnership(),
|
||||||
communication.CaptureOwnership());
|
communication.CaptureOwnership(),
|
||||||
|
actions.CaptureOwnership());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Using Include="Xunit" />
|
<Using Include="Xunit" />
|
||||||
|
<Using Include="AcDream.Runtime.Gameplay" />
|
||||||
<Using Include="AcDream.App.Tests.BoundedTestDatCollection" Alias="DatCollection" />
|
<Using Include="AcDream.App.Tests.BoundedTestDatCollection" Alias="DatCollection" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -186,9 +186,8 @@ public sealed class InteractionRetainedUiCompositionTests
|
||||||
RetainedInputCapture: null!,
|
RetainedInputCapture: null!,
|
||||||
InputDispatcher: null,
|
InputDispatcher: null,
|
||||||
Settings: null!,
|
Settings: null!,
|
||||||
Combat: null!,
|
Actions: null!,
|
||||||
CombatAttackOperations: null!,
|
CombatAttackOperations: null!,
|
||||||
Selection: null!,
|
|
||||||
Inventory: null!,
|
Inventory: null!,
|
||||||
MagicCatalog: null!,
|
MagicCatalog: null!,
|
||||||
Character: null!,
|
Character: null!,
|
||||||
|
|
|
||||||
|
|
@ -234,6 +234,7 @@ public sealed class InteractionUiRuntimeSourcesTests
|
||||||
public IRuntimeCharacterView Character => null!;
|
public IRuntimeCharacterView Character => null!;
|
||||||
public IRuntimeSocialView Social => null!;
|
public IRuntimeSocialView Social => null!;
|
||||||
public IRuntimeChatView Chat => null!;
|
public IRuntimeChatView Chat => null!;
|
||||||
|
public IRuntimeActionView Actions => null!;
|
||||||
public IRuntimeMovementView Movement => null!;
|
public IRuntimeMovementView Movement => null!;
|
||||||
public IRuntimePortalView Portal => null!;
|
public IRuntimePortalView Portal => null!;
|
||||||
public IRuntimeSessionCommands Session => null!;
|
public IRuntimeSessionCommands Session => null!;
|
||||||
|
|
|
||||||
|
|
@ -199,6 +199,7 @@ public sealed class GameplayInputCommandControllerTests
|
||||||
throw new NotSupportedException();
|
throw new NotSupportedException();
|
||||||
public IRuntimeSocialView Social => throw new NotSupportedException();
|
public IRuntimeSocialView Social => throw new NotSupportedException();
|
||||||
public IRuntimeChatView Chat => throw new NotSupportedException();
|
public IRuntimeChatView Chat => throw new NotSupportedException();
|
||||||
|
public IRuntimeActionView Actions => throw new NotSupportedException();
|
||||||
public IRuntimeMovementView Movement => throw new NotSupportedException();
|
public IRuntimeMovementView Movement => throw new NotSupportedException();
|
||||||
public IRuntimePortalView Portal => throw new NotSupportedException();
|
public IRuntimePortalView Portal => throw new NotSupportedException();
|
||||||
public RuntimeStateCheckpoint CaptureCheckpoint() =>
|
public RuntimeStateCheckpoint CaptureCheckpoint() =>
|
||||||
|
|
|
||||||
|
|
@ -161,6 +161,7 @@ public sealed class SelectionInteractionControllerTests
|
||||||
Items = new ItemInteractionController(
|
Items = new ItemInteractionController(
|
||||||
Objects,
|
Objects,
|
||||||
new InventoryTransactionState(Objects),
|
new InventoryTransactionState(Objects),
|
||||||
|
new InteractionState(),
|
||||||
() => Player,
|
() => Player,
|
||||||
sendUse: null,
|
sendUse: null,
|
||||||
sendUseWithTarget: null,
|
sendUseWithTarget: null,
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,7 @@ public sealed class CurrentGameRuntimeAdapterTests
|
||||||
Assert.Equal(new RuntimeGenerationToken(1), start.Generation);
|
Assert.Equal(new RuntimeGenerationToken(1), start.Generation);
|
||||||
Assert.Equal(Harness.PlayerGuid, harness.Identity.ServerGuid);
|
Assert.Equal(Harness.PlayerGuid, harness.Identity.ServerGuid);
|
||||||
Assert.Equal(RuntimeLifecycleState.InWorld, harness.Runtime.Lifecycle.State);
|
Assert.Equal(RuntimeLifecycleState.InWorld, harness.Runtime.Lifecycle.State);
|
||||||
|
Assert.Same(harness.Actions.View, harness.Runtime.Actions);
|
||||||
|
|
||||||
LiveEntityRecord liveRecord =
|
LiveEntityRecord liveRecord =
|
||||||
harness.Entities.RegisterAndMaterializeProjection(Spawn(
|
harness.Entities.RegisterAndMaterializeProjection(Spawn(
|
||||||
|
|
@ -117,6 +118,10 @@ public sealed class CurrentGameRuntimeAdapterTests
|
||||||
Assert.Equal(1, checkpoint.ChatCount);
|
Assert.Equal(1, checkpoint.ChatCount);
|
||||||
Assert.Equal(1L, checkpoint.ChatRevision);
|
Assert.Equal(1L, checkpoint.ChatRevision);
|
||||||
Assert.Equal(1UL, checkpoint.FrameNumber);
|
Assert.Equal(1UL, checkpoint.FrameNumber);
|
||||||
|
Assert.Equal(
|
||||||
|
Harness.TargetGuid,
|
||||||
|
checkpoint.Actions.SelectedObjectId);
|
||||||
|
Assert.Equal(1, checkpoint.Actions.SelectionRevision);
|
||||||
Assert.Equal(RuntimePortalKind.Portal, checkpoint.Portal.Kind);
|
Assert.Equal(RuntimePortalKind.Portal, checkpoint.Portal.Kind);
|
||||||
Assert.Equal(0x12340001u, checkpoint.Portal.DestinationCell);
|
Assert.Equal(0x12340001u, checkpoint.Portal.DestinationCell);
|
||||||
Assert.True(checkpoint.Portal.IsReady);
|
Assert.True(checkpoint.Portal.IsReady);
|
||||||
|
|
@ -676,7 +681,7 @@ public sealed class CurrentGameRuntimeAdapterTests
|
||||||
EntityObjects);
|
EntityObjects);
|
||||||
Objects = EntityObjects.Objects;
|
Objects = EntityObjects.Objects;
|
||||||
Communication = new RuntimeCommunicationState();
|
Communication = new RuntimeCommunicationState();
|
||||||
Selection = new SelectionState();
|
Actions = new RuntimeActionState();
|
||||||
MovementInput = new DispatcherMovementInputSource();
|
MovementInput = new DispatcherMovementInputSource();
|
||||||
GameplayInput = new GameplayInputFrameController(
|
GameplayInput = new GameplayInputFrameController(
|
||||||
dispatcher: null,
|
dispatcher: null,
|
||||||
|
|
@ -697,6 +702,7 @@ public sealed class CurrentGameRuntimeAdapterTests
|
||||||
_items = new ItemInteractionController(
|
_items = new ItemInteractionController(
|
||||||
Objects,
|
Objects,
|
||||||
InventoryState.Transactions,
|
InventoryState.Transactions,
|
||||||
|
Actions.Interaction,
|
||||||
() => PlayerGuid,
|
() => PlayerGuid,
|
||||||
sendUse: null,
|
sendUse: null,
|
||||||
sendUseWithTarget: null,
|
sendUseWithTarget: null,
|
||||||
|
|
@ -726,10 +732,10 @@ public sealed class CurrentGameRuntimeAdapterTests
|
||||||
InventoryState,
|
InventoryState,
|
||||||
Character,
|
Character,
|
||||||
Communication,
|
Communication,
|
||||||
|
Actions,
|
||||||
new LocalPlayerControllerSlot(),
|
new LocalPlayerControllerSlot(),
|
||||||
WorldReveal,
|
WorldReveal,
|
||||||
Clock,
|
Clock,
|
||||||
Selection,
|
|
||||||
selectionController,
|
selectionController,
|
||||||
GameplayInput,
|
GameplayInput,
|
||||||
Combat);
|
Combat);
|
||||||
|
|
@ -744,7 +750,8 @@ public sealed class CurrentGameRuntimeAdapterTests
|
||||||
public ClientObjectTable Objects { get; }
|
public ClientObjectTable Objects { get; }
|
||||||
public RuntimeCommunicationState Communication { get; }
|
public RuntimeCommunicationState Communication { get; }
|
||||||
public ChatLog Chat => Communication.Chat;
|
public ChatLog Chat => Communication.Chat;
|
||||||
public SelectionState Selection { get; }
|
public RuntimeActionState Actions { get; }
|
||||||
|
public SelectionState Selection => Actions.Selection;
|
||||||
public DispatcherMovementInputSource MovementInput { get; }
|
public DispatcherMovementInputSource MovementInput { get; }
|
||||||
public GameplayInputFrameController GameplayInput { get; }
|
public GameplayInputFrameController GameplayInput { get; }
|
||||||
public RecordingCombatCommand Combat { get; }
|
public RecordingCombatCommand Combat { get; }
|
||||||
|
|
@ -763,6 +770,7 @@ public sealed class CurrentGameRuntimeAdapterTests
|
||||||
Communication.Dispose();
|
Communication.Dispose();
|
||||||
_session.Dispose();
|
_session.Dispose();
|
||||||
_items.Dispose();
|
_items.Dispose();
|
||||||
|
Actions.Dispose();
|
||||||
Entities.Clear();
|
Entities.Clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
159
tests/AcDream.App.Tests/Runtime/RuntimeActionOwnershipTests.cs
Normal file
159
tests/AcDream.App.Tests/Runtime/RuntimeActionOwnershipTests.cs
Normal file
|
|
@ -0,0 +1,159 @@
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
namespace AcDream.App.Tests.Runtime;
|
||||||
|
|
||||||
|
public sealed class RuntimeActionOwnershipTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void ProductionConstructsOneCanonicalActionOwner()
|
||||||
|
{
|
||||||
|
string root = FindRepositoryRoot();
|
||||||
|
string gameWindow = ReadAppSource(
|
||||||
|
root,
|
||||||
|
"Rendering",
|
||||||
|
"GameWindow.cs");
|
||||||
|
string program = ReadAppSource(root, "Program.cs");
|
||||||
|
|
||||||
|
Assert.Contains(
|
||||||
|
"private readonly RuntimeActionState _runtimeActions = new();",
|
||||||
|
gameWindow,
|
||||||
|
StringComparison.Ordinal);
|
||||||
|
Assert.Contains(
|
||||||
|
"_runtimeActions.Selection;",
|
||||||
|
gameWindow,
|
||||||
|
StringComparison.Ordinal);
|
||||||
|
Assert.Contains(
|
||||||
|
"_runtimeActions.Combat;",
|
||||||
|
gameWindow,
|
||||||
|
StringComparison.Ordinal);
|
||||||
|
Assert.Contains(
|
||||||
|
"window.Selection,",
|
||||||
|
program,
|
||||||
|
StringComparison.Ordinal);
|
||||||
|
|
||||||
|
string[] productionFiles = Directory
|
||||||
|
.EnumerateFiles(
|
||||||
|
Path.Combine(root, "src", "AcDream.App"),
|
||||||
|
"*.cs",
|
||||||
|
SearchOption.AllDirectories)
|
||||||
|
.Where(static path =>
|
||||||
|
!path.Contains(
|
||||||
|
$"{Path.DirectorySeparatorChar}Studio{Path.DirectorySeparatorChar}",
|
||||||
|
StringComparison.Ordinal))
|
||||||
|
.ToArray();
|
||||||
|
string production = string.Join(
|
||||||
|
"\n",
|
||||||
|
productionFiles.Select(File.ReadAllText));
|
||||||
|
|
||||||
|
Assert.Empty(Regex.Matches(
|
||||||
|
production,
|
||||||
|
@"\bnew\s+(?:AcDream\.Core\.Selection\.)?SelectionState\s*\("));
|
||||||
|
Assert.Empty(Regex.Matches(
|
||||||
|
production,
|
||||||
|
@"\bnew\s+(?:AcDream\.Core\.Combat\.)?CombatState\s*\("));
|
||||||
|
Assert.Empty(Regex.Matches(
|
||||||
|
production,
|
||||||
|
@"\bnew\s+(?:AcDream\.Runtime\.Gameplay\.)?InteractionState\s*\("));
|
||||||
|
Assert.Equal(
|
||||||
|
1,
|
||||||
|
Regex.Matches(
|
||||||
|
production,
|
||||||
|
@"\bnew\s+RuntimeActionState\s*\(").Count
|
||||||
|
+ Regex.Matches(
|
||||||
|
production,
|
||||||
|
@"RuntimeActionState\s+\w+\s*=\s*new\s*\(\s*\)").Count);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void UiSessionRuntimeAndShutdownBorrowTheExactActionChildren()
|
||||||
|
{
|
||||||
|
string root = FindRepositoryRoot();
|
||||||
|
string ui = ReadAppSource(
|
||||||
|
root,
|
||||||
|
"Composition",
|
||||||
|
"InteractionRetainedUiComposition.cs");
|
||||||
|
string session = ReadAppSource(
|
||||||
|
root,
|
||||||
|
"Composition",
|
||||||
|
"SessionPlayerComposition.cs");
|
||||||
|
string liveSession = ReadAppSource(
|
||||||
|
root,
|
||||||
|
"Net",
|
||||||
|
"LiveSessionRuntimeFactory.cs");
|
||||||
|
string commands = ReadAppSource(
|
||||||
|
root,
|
||||||
|
"Runtime",
|
||||||
|
"CurrentGameRuntimeCommandAdapter.cs");
|
||||||
|
string itemInteraction = ReadAppSource(
|
||||||
|
root,
|
||||||
|
"UI",
|
||||||
|
"ItemInteractionController.cs");
|
||||||
|
string shutdown = ReadAppSource(
|
||||||
|
root,
|
||||||
|
"Rendering",
|
||||||
|
"GameWindowLifetime.cs");
|
||||||
|
|
||||||
|
Assert.Contains("d.Actions.Interaction,", ui, StringComparison.Ordinal);
|
||||||
|
Assert.Contains("d.Actions.Selection", ui, StringComparison.Ordinal);
|
||||||
|
Assert.Contains("d.Actions.Combat", ui, StringComparison.Ordinal);
|
||||||
|
Assert.Contains("d.Actions.Selection", session, StringComparison.Ordinal);
|
||||||
|
Assert.Contains("d.Actions.Combat", session, StringComparison.Ordinal);
|
||||||
|
Assert.Contains("_domain.Actions.Combat", liveSession, StringComparison.Ordinal);
|
||||||
|
Assert.Contains("_actions.Selection", commands, StringComparison.Ordinal);
|
||||||
|
Assert.Contains(
|
||||||
|
"InteractionState interactionState,",
|
||||||
|
itemInteraction,
|
||||||
|
StringComparison.Ordinal);
|
||||||
|
Assert.DoesNotContain(
|
||||||
|
"new InteractionState",
|
||||||
|
itemInteraction,
|
||||||
|
StringComparison.Ordinal);
|
||||||
|
Assert.Contains(
|
||||||
|
"\"runtime action state\"",
|
||||||
|
shutdown,
|
||||||
|
StringComparison.Ordinal);
|
||||||
|
AssertAppearsInOrder(
|
||||||
|
shutdown,
|
||||||
|
"\"runtime inventory state\"",
|
||||||
|
"\"runtime action state\"",
|
||||||
|
"\"runtime entity/object lifetime\"");
|
||||||
|
Assert.False(File.Exists(Path.Combine(
|
||||||
|
root,
|
||||||
|
"src",
|
||||||
|
"AcDream.App",
|
||||||
|
"UI",
|
||||||
|
"InteractionState.cs")));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string ReadAppSource(string root, params string[] relative) =>
|
||||||
|
File.ReadAllText(Path.Combine(
|
||||||
|
[root, "src", "AcDream.App", .. relative]));
|
||||||
|
|
||||||
|
private static string FindRepositoryRoot()
|
||||||
|
{
|
||||||
|
var current = new DirectoryInfo(AppContext.BaseDirectory);
|
||||||
|
while (current is not null)
|
||||||
|
{
|
||||||
|
if (File.Exists(Path.Combine(current.FullName, "AcDream.slnx")))
|
||||||
|
return current.FullName;
|
||||||
|
current = current.Parent;
|
||||||
|
}
|
||||||
|
throw new DirectoryNotFoundException("AcDream.slnx was not found.");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void AssertAppearsInOrder(
|
||||||
|
string source,
|
||||||
|
params string[] fragments)
|
||||||
|
{
|
||||||
|
int cursor = 0;
|
||||||
|
foreach (string fragment in fragments)
|
||||||
|
{
|
||||||
|
int index = source.IndexOf(
|
||||||
|
fragment,
|
||||||
|
cursor,
|
||||||
|
StringComparison.Ordinal);
|
||||||
|
Assert.True(index >= 0, $"Missing source fragment: {fragment}");
|
||||||
|
cursor = index + fragment.Length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -154,6 +154,7 @@ public sealed class CursorFeedbackControllerTests
|
||||||
var interaction = new ItemInteractionController(
|
var interaction = new ItemInteractionController(
|
||||||
objects,
|
objects,
|
||||||
new InventoryTransactionState(objects),
|
new InventoryTransactionState(objects),
|
||||||
|
new InteractionState(),
|
||||||
playerGuid: () => Player,
|
playerGuid: () => Player,
|
||||||
sendUse: null,
|
sendUse: null,
|
||||||
sendUseWithTarget: null,
|
sendUseWithTarget: null,
|
||||||
|
|
@ -190,6 +191,7 @@ public sealed class CursorFeedbackControllerTests
|
||||||
var interaction = new ItemInteractionController(
|
var interaction = new ItemInteractionController(
|
||||||
objects,
|
objects,
|
||||||
new InventoryTransactionState(objects),
|
new InventoryTransactionState(objects),
|
||||||
|
new InteractionState(),
|
||||||
playerGuid: () => Player,
|
playerGuid: () => Player,
|
||||||
sendUse: null,
|
sendUse: null,
|
||||||
sendUseWithTarget: null,
|
sendUseWithTarget: null,
|
||||||
|
|
@ -229,6 +231,7 @@ public sealed class CursorFeedbackControllerTests
|
||||||
var interaction = new ItemInteractionController(
|
var interaction = new ItemInteractionController(
|
||||||
objects,
|
objects,
|
||||||
new InventoryTransactionState(objects),
|
new InventoryTransactionState(objects),
|
||||||
|
new InteractionState(),
|
||||||
playerGuid: () => Player,
|
playerGuid: () => Player,
|
||||||
sendUse: null,
|
sendUse: null,
|
||||||
sendUseWithTarget: null,
|
sendUseWithTarget: null,
|
||||||
|
|
@ -258,6 +261,7 @@ public sealed class CursorFeedbackControllerTests
|
||||||
var interaction = new ItemInteractionController(
|
var interaction = new ItemInteractionController(
|
||||||
objects,
|
objects,
|
||||||
new InventoryTransactionState(objects),
|
new InventoryTransactionState(objects),
|
||||||
|
new InteractionState(),
|
||||||
playerGuid: () => Player,
|
playerGuid: () => Player,
|
||||||
sendUse: null,
|
sendUse: null,
|
||||||
sendUseWithTarget: null,
|
sendUseWithTarget: null,
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,7 @@ public sealed class ItemInteractionControllerTests
|
||||||
Controller = new ItemInteractionController(
|
Controller = new ItemInteractionController(
|
||||||
Objects,
|
Objects,
|
||||||
SharedTransactions,
|
SharedTransactions,
|
||||||
|
new InteractionState(),
|
||||||
playerGuid: () => Player,
|
playerGuid: () => Player,
|
||||||
sendUse: requestUse is null ? Uses.Add : null,
|
sendUse: requestUse is null ? Uses.Add : null,
|
||||||
sendExamine: Examines.Add,
|
sendExamine: Examines.Add,
|
||||||
|
|
@ -341,6 +342,7 @@ public sealed class ItemInteractionControllerTests
|
||||||
Assert.Throws<ArgumentException>(() => new ItemInteractionController(
|
Assert.Throws<ArgumentException>(() => new ItemInteractionController(
|
||||||
objects,
|
objects,
|
||||||
transactions,
|
transactions,
|
||||||
|
new InteractionState(),
|
||||||
playerGuid: () => Player,
|
playerGuid: () => Player,
|
||||||
sendUse: null,
|
sendUse: null,
|
||||||
sendUseWithTarget: null,
|
sendUseWithTarget: null,
|
||||||
|
|
|
||||||
|
|
@ -859,6 +859,7 @@ public sealed class AppraisalUiControllerTests
|
||||||
=> new(
|
=> new(
|
||||||
objects,
|
objects,
|
||||||
new InventoryTransactionState(objects),
|
new InventoryTransactionState(objects),
|
||||||
|
new InteractionState(),
|
||||||
playerGuid: () => 0x50000002u,
|
playerGuid: () => 0x50000002u,
|
||||||
sendUse: null,
|
sendUse: null,
|
||||||
sendUseWithTarget: null,
|
sendUseWithTarget: null,
|
||||||
|
|
|
||||||
|
|
@ -107,6 +107,7 @@ public sealed class ExternalContainerControllerTests
|
||||||
Interaction = new ItemInteractionController(
|
Interaction = new ItemInteractionController(
|
||||||
Objects,
|
Objects,
|
||||||
new InventoryTransactionState(Objects),
|
new InventoryTransactionState(Objects),
|
||||||
|
new InteractionState(),
|
||||||
playerGuid: () => Player,
|
playerGuid: () => Player,
|
||||||
sendUse: Uses.Add,
|
sendUse: Uses.Add,
|
||||||
sendUseWithTarget: null,
|
sendUseWithTarget: null,
|
||||||
|
|
|
||||||
|
|
@ -509,6 +509,7 @@ public class InventoryControllerTests
|
||||||
using var interaction = new ItemInteractionController(
|
using var interaction = new ItemInteractionController(
|
||||||
objects,
|
objects,
|
||||||
new InventoryTransactionState(objects),
|
new InventoryTransactionState(objects),
|
||||||
|
new InteractionState(),
|
||||||
playerGuid: () => Player,
|
playerGuid: () => Player,
|
||||||
sendUse: null,
|
sendUse: null,
|
||||||
sendUseWithTarget: null,
|
sendUseWithTarget: null,
|
||||||
|
|
@ -546,6 +547,7 @@ public class InventoryControllerTests
|
||||||
var interaction = new ItemInteractionController(
|
var interaction = new ItemInteractionController(
|
||||||
objects,
|
objects,
|
||||||
new InventoryTransactionState(objects),
|
new InventoryTransactionState(objects),
|
||||||
|
new InteractionState(),
|
||||||
playerGuid: () => Player,
|
playerGuid: () => Player,
|
||||||
sendUse: null,
|
sendUse: null,
|
||||||
sendUseWithTarget: null,
|
sendUseWithTarget: null,
|
||||||
|
|
@ -715,6 +717,7 @@ public class InventoryControllerTests
|
||||||
using var interaction = new ItemInteractionController(
|
using var interaction = new ItemInteractionController(
|
||||||
objects,
|
objects,
|
||||||
new InventoryTransactionState(objects),
|
new InventoryTransactionState(objects),
|
||||||
|
new InteractionState(),
|
||||||
playerGuid: () => Player,
|
playerGuid: () => Player,
|
||||||
sendUse: null,
|
sendUse: null,
|
||||||
sendUseWithTarget: null,
|
sendUseWithTarget: null,
|
||||||
|
|
@ -760,6 +763,7 @@ public class InventoryControllerTests
|
||||||
using var interaction = new ItemInteractionController(
|
using var interaction = new ItemInteractionController(
|
||||||
objects,
|
objects,
|
||||||
new InventoryTransactionState(objects),
|
new InventoryTransactionState(objects),
|
||||||
|
new InteractionState(),
|
||||||
playerGuid: () => Player,
|
playerGuid: () => Player,
|
||||||
sendUse: null,
|
sendUse: null,
|
||||||
sendUseWithTarget: null,
|
sendUseWithTarget: null,
|
||||||
|
|
@ -812,6 +816,7 @@ public class InventoryControllerTests
|
||||||
using var interaction = new ItemInteractionController(
|
using var interaction = new ItemInteractionController(
|
||||||
objects,
|
objects,
|
||||||
new InventoryTransactionState(objects),
|
new InventoryTransactionState(objects),
|
||||||
|
new InteractionState(),
|
||||||
playerGuid: () => Player,
|
playerGuid: () => Player,
|
||||||
sendUse: null,
|
sendUse: null,
|
||||||
sendUseWithTarget: null,
|
sendUseWithTarget: null,
|
||||||
|
|
@ -870,6 +875,7 @@ public class InventoryControllerTests
|
||||||
using var interaction = new ItemInteractionController(
|
using var interaction = new ItemInteractionController(
|
||||||
objects,
|
objects,
|
||||||
new InventoryTransactionState(objects),
|
new InventoryTransactionState(objects),
|
||||||
|
new InteractionState(),
|
||||||
playerGuid: () => Player,
|
playerGuid: () => Player,
|
||||||
sendUse: null,
|
sendUse: null,
|
||||||
sendUseWithTarget: null,
|
sendUseWithTarget: null,
|
||||||
|
|
@ -939,6 +945,7 @@ public class InventoryControllerTests
|
||||||
using var interaction = new ItemInteractionController(
|
using var interaction = new ItemInteractionController(
|
||||||
objects,
|
objects,
|
||||||
new InventoryTransactionState(objects),
|
new InventoryTransactionState(objects),
|
||||||
|
new InteractionState(),
|
||||||
playerGuid: () => Player,
|
playerGuid: () => Player,
|
||||||
sendUse: null,
|
sendUse: null,
|
||||||
sendUseWithTarget: null,
|
sendUseWithTarget: null,
|
||||||
|
|
@ -1002,6 +1009,7 @@ public class InventoryControllerTests
|
||||||
using var interaction = new ItemInteractionController(
|
using var interaction = new ItemInteractionController(
|
||||||
objects,
|
objects,
|
||||||
new InventoryTransactionState(objects),
|
new InventoryTransactionState(objects),
|
||||||
|
new InteractionState(),
|
||||||
playerGuid: () => Player,
|
playerGuid: () => Player,
|
||||||
sendUse: null,
|
sendUse: null,
|
||||||
sendUseWithTarget: null,
|
sendUseWithTarget: null,
|
||||||
|
|
@ -1062,6 +1070,7 @@ public class InventoryControllerTests
|
||||||
using var interaction = new ItemInteractionController(
|
using var interaction = new ItemInteractionController(
|
||||||
objects,
|
objects,
|
||||||
new InventoryTransactionState(objects),
|
new InventoryTransactionState(objects),
|
||||||
|
new InteractionState(),
|
||||||
playerGuid: () => Player,
|
playerGuid: () => Player,
|
||||||
sendUse: null,
|
sendUse: null,
|
||||||
sendUseWithTarget: null,
|
sendUseWithTarget: null,
|
||||||
|
|
@ -1109,6 +1118,7 @@ public class InventoryControllerTests
|
||||||
using var interaction = new ItemInteractionController(
|
using var interaction = new ItemInteractionController(
|
||||||
objects,
|
objects,
|
||||||
new InventoryTransactionState(objects),
|
new InventoryTransactionState(objects),
|
||||||
|
new InteractionState(),
|
||||||
playerGuid: () => Player,
|
playerGuid: () => Player,
|
||||||
sendUse: null,
|
sendUse: null,
|
||||||
sendUseWithTarget: null,
|
sendUseWithTarget: null,
|
||||||
|
|
@ -1149,6 +1159,7 @@ public class InventoryControllerTests
|
||||||
using var interaction = new ItemInteractionController(
|
using var interaction = new ItemInteractionController(
|
||||||
objects,
|
objects,
|
||||||
new InventoryTransactionState(objects),
|
new InventoryTransactionState(objects),
|
||||||
|
new InteractionState(),
|
||||||
playerGuid: () => Player,
|
playerGuid: () => Player,
|
||||||
sendUse: null,
|
sendUse: null,
|
||||||
sendUseWithTarget: null,
|
sendUseWithTarget: null,
|
||||||
|
|
@ -1188,6 +1199,7 @@ public class InventoryControllerTests
|
||||||
using var interaction = new ItemInteractionController(
|
using var interaction = new ItemInteractionController(
|
||||||
objects,
|
objects,
|
||||||
new InventoryTransactionState(objects),
|
new InventoryTransactionState(objects),
|
||||||
|
new InteractionState(),
|
||||||
playerGuid: () => Player,
|
playerGuid: () => Player,
|
||||||
sendUse: null,
|
sendUse: null,
|
||||||
sendUseWithTarget: null,
|
sendUseWithTarget: null,
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,7 @@ public class PaperdollControllerTests
|
||||||
var itemInteraction = new ItemInteractionController(
|
var itemInteraction = new ItemInteractionController(
|
||||||
objects,
|
objects,
|
||||||
new InventoryTransactionState(objects),
|
new InventoryTransactionState(objects),
|
||||||
|
new InteractionState(),
|
||||||
() => Player,
|
() => Player,
|
||||||
sendUse: null,
|
sendUse: null,
|
||||||
sendUseWithTarget: null,
|
sendUseWithTarget: null,
|
||||||
|
|
|
||||||
|
|
@ -266,6 +266,7 @@ public class ToolbarControllerTests
|
||||||
var interaction = new ItemInteractionController(
|
var interaction = new ItemInteractionController(
|
||||||
repo,
|
repo,
|
||||||
new InventoryTransactionState(repo),
|
new InventoryTransactionState(repo),
|
||||||
|
new InteractionState(),
|
||||||
playerGuid: () => player,
|
playerGuid: () => player,
|
||||||
sendUse: null,
|
sendUse: null,
|
||||||
sendUseWithTarget: (source, target) => useWithTarget.Add((source, target)),
|
sendUseWithTarget: (source, target) => useWithTarget.Add((source, target)),
|
||||||
|
|
@ -332,6 +333,7 @@ public class ToolbarControllerTests
|
||||||
using var interaction = new ItemInteractionController(
|
using var interaction = new ItemInteractionController(
|
||||||
repo,
|
repo,
|
||||||
new InventoryTransactionState(repo),
|
new InventoryTransactionState(repo),
|
||||||
|
new InteractionState(),
|
||||||
playerGuid: () => player,
|
playerGuid: () => player,
|
||||||
sendUse: null,
|
sendUse: null,
|
||||||
sendUseWithTarget: null,
|
sendUseWithTarget: null,
|
||||||
|
|
@ -381,6 +383,7 @@ public class ToolbarControllerTests
|
||||||
using var interaction = new ItemInteractionController(
|
using var interaction = new ItemInteractionController(
|
||||||
repo,
|
repo,
|
||||||
new InventoryTransactionState(repo),
|
new InventoryTransactionState(repo),
|
||||||
|
new InteractionState(),
|
||||||
playerGuid: () => player,
|
playerGuid: () => player,
|
||||||
sendUse: null,
|
sendUse: null,
|
||||||
sendUseWithTarget: null,
|
sendUseWithTarget: null,
|
||||||
|
|
@ -510,6 +513,7 @@ public class ToolbarControllerTests
|
||||||
var interaction = new ItemInteractionController(
|
var interaction = new ItemInteractionController(
|
||||||
repo,
|
repo,
|
||||||
new InventoryTransactionState(repo),
|
new InventoryTransactionState(repo),
|
||||||
|
new InteractionState(),
|
||||||
() => player,
|
() => player,
|
||||||
sendUse: uses.Add,
|
sendUse: uses.Add,
|
||||||
sendUseWithTarget: null,
|
sendUseWithTarget: null,
|
||||||
|
|
@ -568,6 +572,7 @@ public class ToolbarControllerTests
|
||||||
using var interaction = new ItemInteractionController(
|
using var interaction = new ItemInteractionController(
|
||||||
repo,
|
repo,
|
||||||
new InventoryTransactionState(repo),
|
new InventoryTransactionState(repo),
|
||||||
|
new InteractionState(),
|
||||||
() => player,
|
() => player,
|
||||||
sendUse: null,
|
sendUse: null,
|
||||||
sendUseWithTarget: null,
|
sendUseWithTarget: null,
|
||||||
|
|
@ -633,6 +638,7 @@ public class ToolbarControllerTests
|
||||||
using var interaction = new ItemInteractionController(
|
using var interaction = new ItemInteractionController(
|
||||||
repo,
|
repo,
|
||||||
new InventoryTransactionState(repo),
|
new InventoryTransactionState(repo),
|
||||||
|
new InteractionState(),
|
||||||
() => player,
|
() => player,
|
||||||
sendUse: null,
|
sendUse: null,
|
||||||
sendUseWithTarget: null,
|
sendUseWithTarget: null,
|
||||||
|
|
@ -799,6 +805,7 @@ public class ToolbarControllerTests
|
||||||
using var interaction = new ItemInteractionController(
|
using var interaction = new ItemInteractionController(
|
||||||
repo,
|
repo,
|
||||||
new InventoryTransactionState(repo),
|
new InventoryTransactionState(repo),
|
||||||
|
new InteractionState(),
|
||||||
playerGuid: () => player,
|
playerGuid: () => player,
|
||||||
sendUse: null,
|
sendUse: null,
|
||||||
sendUseWithTarget: null,
|
sendUseWithTarget: null,
|
||||||
|
|
@ -848,6 +855,7 @@ public class ToolbarControllerTests
|
||||||
var interaction = new ItemInteractionController(
|
var interaction = new ItemInteractionController(
|
||||||
repo,
|
repo,
|
||||||
new InventoryTransactionState(repo),
|
new InventoryTransactionState(repo),
|
||||||
|
new InteractionState(),
|
||||||
() => player,
|
() => player,
|
||||||
sendUse: null,
|
sendUse: null,
|
||||||
sendUseWithTarget: (s, t) => (sentSource, sentTarget) = (s, t),
|
sendUseWithTarget: (s, t) => (sentSource, sentTarget) = (s, t),
|
||||||
|
|
@ -890,6 +898,7 @@ public class ToolbarControllerTests
|
||||||
var interaction = new ItemInteractionController(
|
var interaction = new ItemInteractionController(
|
||||||
repo,
|
repo,
|
||||||
new InventoryTransactionState(repo),
|
new InventoryTransactionState(repo),
|
||||||
|
new InteractionState(),
|
||||||
() => player,
|
() => player,
|
||||||
sendUse: null,
|
sendUse: null,
|
||||||
sendUseWithTarget: null,
|
sendUseWithTarget: null,
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ public sealed class RetailItemConfirmationControllerTests
|
||||||
var items = new ItemInteractionController(
|
var items = new ItemInteractionController(
|
||||||
objects,
|
objects,
|
||||||
new InventoryTransactionState(objects),
|
new InventoryTransactionState(objects),
|
||||||
|
new InteractionState(),
|
||||||
playerGuid: () => Player,
|
playerGuid: () => Player,
|
||||||
sendUse: uses.Add,
|
sendUse: uses.Add,
|
||||||
sendUseWithTarget: null,
|
sendUseWithTarget: null,
|
||||||
|
|
@ -54,6 +55,7 @@ public sealed class RetailItemConfirmationControllerTests
|
||||||
var items = new ItemInteractionController(
|
var items = new ItemInteractionController(
|
||||||
objects,
|
objects,
|
||||||
new InventoryTransactionState(objects),
|
new InventoryTransactionState(objects),
|
||||||
|
new InteractionState(),
|
||||||
playerGuid: () => Player,
|
playerGuid: () => Player,
|
||||||
sendUse: uses.Add,
|
sendUse: uses.Add,
|
||||||
sendUseWithTarget: null,
|
sendUseWithTarget: null,
|
||||||
|
|
@ -83,6 +85,7 @@ public sealed class RetailItemConfirmationControllerTests
|
||||||
var items = new ItemInteractionController(
|
var items = new ItemInteractionController(
|
||||||
objects,
|
objects,
|
||||||
new InventoryTransactionState(objects),
|
new InventoryTransactionState(objects),
|
||||||
|
new InteractionState(),
|
||||||
playerGuid: () => Player,
|
playerGuid: () => Player,
|
||||||
sendUse: uses.Add,
|
sendUse: uses.Add,
|
||||||
sendUseWithTarget: null,
|
sendUseWithTarget: null,
|
||||||
|
|
|
||||||
|
|
@ -164,6 +164,7 @@ public sealed class RetailUiInteractionFlowTests
|
||||||
var interaction = new ItemInteractionController(
|
var interaction = new ItemInteractionController(
|
||||||
Objects,
|
Objects,
|
||||||
new InventoryTransactionState(Objects),
|
new InventoryTransactionState(Objects),
|
||||||
|
new InteractionState(),
|
||||||
playerGuid: () => Player,
|
playerGuid: () => Player,
|
||||||
sendUse: Uses.Add,
|
sendUse: Uses.Add,
|
||||||
sendUseWithTarget: (source, target) => UseWithTarget.Add((source, target)),
|
sendUseWithTarget: (source, target) => UseWithTarget.Add((source, target)),
|
||||||
|
|
@ -199,6 +200,7 @@ public sealed class RetailUiInteractionFlowTests
|
||||||
itemInteraction ??= new ItemInteractionController(
|
itemInteraction ??= new ItemInteractionController(
|
||||||
Objects,
|
Objects,
|
||||||
new InventoryTransactionState(Objects),
|
new InventoryTransactionState(Objects),
|
||||||
|
new InteractionState(),
|
||||||
() => Player,
|
() => Player,
|
||||||
sendUse: null,
|
sendUse: null,
|
||||||
sendUseWithTarget: null,
|
sendUseWithTarget: null,
|
||||||
|
|
|
||||||
|
|
@ -169,6 +169,17 @@ public sealed class GameRuntimeContractTests
|
||||||
new RuntimeSocialSnapshot(9, 10, 11, 0, 0, 0, 0),
|
new RuntimeSocialSnapshot(9, 10, 11, 0, 0, 0, 0),
|
||||||
ChatRevision: 12,
|
ChatRevision: 12,
|
||||||
ChatCount: 13,
|
ChatCount: 13,
|
||||||
|
new RuntimeActionSnapshot(
|
||||||
|
SelectionRevision: 14,
|
||||||
|
SelectedObjectId: 0x50000001u,
|
||||||
|
PreviousObjectId: 0u,
|
||||||
|
PreviousValidObjectId: 0u,
|
||||||
|
CombatRevision: 15,
|
||||||
|
CombatMode: AcDream.Core.Combat.CombatMode.Missile,
|
||||||
|
TrackedTargetHealthCount: 1,
|
||||||
|
InteractionRevision: 16,
|
||||||
|
InteractionMode: InteractionModeKind.Use,
|
||||||
|
InteractionSourceObjectId: 0u),
|
||||||
default,
|
default,
|
||||||
default);
|
default);
|
||||||
|
|
||||||
|
|
@ -178,6 +189,9 @@ public sealed class GameRuntimeContractTests
|
||||||
Assert.Contains("inventory-state=2:4:1:3", entry.Text);
|
Assert.Contains("inventory-state=2:4:1:3", entry.Text);
|
||||||
Assert.Contains("character=5:6:7:8:0:0", entry.Text);
|
Assert.Contains("character=5:6:7:8:0:0", entry.Text);
|
||||||
Assert.Contains("social=9:10:11", entry.Text);
|
Assert.Contains("social=9:10:11", entry.Text);
|
||||||
|
Assert.Contains(
|
||||||
|
"actions=14:50000001:15:4:1:16:1:00000000",
|
||||||
|
entry.Text);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -190,6 +204,8 @@ public sealed class GameRuntimeContractTests
|
||||||
typeof(RuntimeCharacterState),
|
typeof(RuntimeCharacterState),
|
||||||
typeof(RuntimeCharacterOptionsState),
|
typeof(RuntimeCharacterOptionsState),
|
||||||
typeof(RuntimeMovementSkillState),
|
typeof(RuntimeMovementSkillState),
|
||||||
|
typeof(RuntimeActionState),
|
||||||
|
typeof(InteractionState),
|
||||||
];
|
];
|
||||||
|
|
||||||
foreach (Type owner in owners)
|
foreach (Type owner in owners)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
using AcDream.App.UI;
|
using AcDream.Runtime.Gameplay;
|
||||||
|
|
||||||
namespace AcDream.App.Tests.UI;
|
namespace AcDream.Runtime.Tests.Gameplay;
|
||||||
|
|
||||||
public sealed class InteractionStateTests
|
public sealed class InteractionStateTests
|
||||||
{
|
{
|
||||||
|
|
@ -26,7 +26,8 @@ public sealed class InteractionStateTests
|
||||||
public void UseItemOnTarget_RejectsMissingSource()
|
public void UseItemOnTarget_RejectsMissingSource()
|
||||||
{
|
{
|
||||||
var state = new InteractionState();
|
var state = new InteractionState();
|
||||||
Assert.Throws<ArgumentOutOfRangeException>(() => state.EnterUseItemOnTarget(0));
|
Assert.Throws<ArgumentOutOfRangeException>(
|
||||||
|
() => state.EnterUseItemOnTarget(0));
|
||||||
Assert.Equal(InteractionMode.None, state.Current);
|
Assert.Equal(InteractionMode.None, state.Current);
|
||||||
}
|
}
|
||||||
|
|
||||||
120
tests/AcDream.Runtime.Tests/Gameplay/RuntimeActionStateTests.cs
Normal file
120
tests/AcDream.Runtime.Tests/Gameplay/RuntimeActionStateTests.cs
Normal file
|
|
@ -0,0 +1,120 @@
|
||||||
|
using AcDream.Core.Combat;
|
||||||
|
using AcDream.Core.Selection;
|
||||||
|
using AcDream.Runtime.Gameplay;
|
||||||
|
|
||||||
|
namespace AcDream.Runtime.Tests.Gameplay;
|
||||||
|
|
||||||
|
public sealed class RuntimeActionStateTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void ViewProjectsTheExactCanonicalChildrenAndRevisions()
|
||||||
|
{
|
||||||
|
using var actions = new RuntimeActionState();
|
||||||
|
|
||||||
|
actions.Selection.Select(
|
||||||
|
0x50000001u,
|
||||||
|
SelectionChangeSource.World);
|
||||||
|
actions.Combat.SetCombatMode(CombatMode.Missile);
|
||||||
|
actions.Combat.OnUpdateHealth(0x50000001u, 0.625f);
|
||||||
|
actions.Interaction.EnterUseItemOnTarget(0x70000001u);
|
||||||
|
|
||||||
|
RuntimeActionSnapshot snapshot = actions.View.Snapshot;
|
||||||
|
|
||||||
|
Assert.Equal(1, snapshot.SelectionRevision);
|
||||||
|
Assert.Equal(0x50000001u, snapshot.SelectedObjectId);
|
||||||
|
Assert.Equal(2, snapshot.CombatRevision);
|
||||||
|
Assert.Equal(CombatMode.Missile, snapshot.CombatMode);
|
||||||
|
Assert.Equal(1, snapshot.TrackedTargetHealthCount);
|
||||||
|
Assert.Equal(1, snapshot.InteractionRevision);
|
||||||
|
Assert.Equal(
|
||||||
|
InteractionModeKind.UseItemOnTarget,
|
||||||
|
snapshot.InteractionMode);
|
||||||
|
Assert.Equal(0x70000001u, snapshot.InteractionSourceObjectId);
|
||||||
|
Assert.True(
|
||||||
|
actions.View.TryGetHealth(0x50000001u, out float health));
|
||||||
|
Assert.Equal(0.625f, health);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ResetSessionConvergesEveryChildAfterObserverFailures()
|
||||||
|
{
|
||||||
|
var actions = new RuntimeActionState();
|
||||||
|
actions.Selection.Select(
|
||||||
|
0x50000001u,
|
||||||
|
SelectionChangeSource.World);
|
||||||
|
actions.Combat.SetCombatMode(CombatMode.Melee);
|
||||||
|
actions.Combat.OnUpdateHealth(0x50000001u, 0.25f);
|
||||||
|
actions.Interaction.EnterExamine();
|
||||||
|
|
||||||
|
actions.Selection.Changed += static _ =>
|
||||||
|
throw new InvalidOperationException("selection observer");
|
||||||
|
actions.Combat.CombatModeChanged += static _ =>
|
||||||
|
throw new InvalidOperationException("combat observer");
|
||||||
|
actions.Interaction.Changed += static _ =>
|
||||||
|
throw new InvalidOperationException("interaction observer");
|
||||||
|
|
||||||
|
Assert.Throws<AggregateException>(actions.ResetSession);
|
||||||
|
|
||||||
|
RuntimeActionOwnershipSnapshot snapshot =
|
||||||
|
actions.CaptureOwnership();
|
||||||
|
Assert.Equal(0u, snapshot.SelectedObjectId);
|
||||||
|
Assert.Equal(0u, snapshot.PreviousObjectId);
|
||||||
|
Assert.Equal(0u, snapshot.PreviousValidObjectId);
|
||||||
|
Assert.Equal(CombatMode.NonCombat, snapshot.CombatMode);
|
||||||
|
Assert.Equal(0, snapshot.TrackedTargetHealthCount);
|
||||||
|
Assert.Equal(InteractionMode.None, snapshot.InteractionMode);
|
||||||
|
Assert.Throws<AggregateException>(actions.Dispose);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void DisposeIsTerminalAndConvergedAfterObserverFailures()
|
||||||
|
{
|
||||||
|
var actions = new RuntimeActionState();
|
||||||
|
actions.Selection.Select(
|
||||||
|
0x50000001u,
|
||||||
|
SelectionChangeSource.World);
|
||||||
|
actions.Combat.SetCombatMode(CombatMode.Magic);
|
||||||
|
actions.Combat.OnUpdateHealth(0x50000001u, 0.5f);
|
||||||
|
actions.Interaction.EnterUse();
|
||||||
|
actions.Selection.Changed += static _ =>
|
||||||
|
throw new InvalidOperationException("selection observer");
|
||||||
|
actions.Combat.CombatModeChanged += static _ =>
|
||||||
|
throw new InvalidOperationException("combat observer");
|
||||||
|
actions.Interaction.Changed += static _ =>
|
||||||
|
throw new InvalidOperationException("interaction observer");
|
||||||
|
|
||||||
|
Assert.Throws<AggregateException>(actions.Dispose);
|
||||||
|
|
||||||
|
RuntimeActionOwnershipSnapshot snapshot =
|
||||||
|
actions.CaptureOwnership();
|
||||||
|
Assert.True(snapshot.IsConverged);
|
||||||
|
actions.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void InstancesAreFullyIsolated()
|
||||||
|
{
|
||||||
|
using var first = new RuntimeActionState();
|
||||||
|
using var second = new RuntimeActionState();
|
||||||
|
|
||||||
|
first.Selection.Select(
|
||||||
|
0x50000001u,
|
||||||
|
SelectionChangeSource.World);
|
||||||
|
first.Combat.SetCombatMode(CombatMode.Missile);
|
||||||
|
first.Interaction.EnterUse();
|
||||||
|
|
||||||
|
Assert.Equal(
|
||||||
|
new RuntimeActionSnapshot(
|
||||||
|
0,
|
||||||
|
0u,
|
||||||
|
0u,
|
||||||
|
0u,
|
||||||
|
0,
|
||||||
|
CombatMode.NonCombat,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
InteractionModeKind.None,
|
||||||
|
0u),
|
||||||
|
second.View.Snapshot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -15,6 +15,7 @@ public sealed class RuntimeGameplayOwnershipTests
|
||||||
var inventory = new RuntimeInventoryState(entities);
|
var inventory = new RuntimeInventoryState(entities);
|
||||||
var character = new RuntimeCharacterState();
|
var character = new RuntimeCharacterState();
|
||||||
var communication = new RuntimeCommunicationState();
|
var communication = new RuntimeCommunicationState();
|
||||||
|
var actions = new RuntimeActionState();
|
||||||
inventory.Shortcuts.Changed += static () => { };
|
inventory.Shortcuts.Changed += static () => { };
|
||||||
inventory.Shortcuts.Load([new ShortcutEntry(1, 2u, 3u)]);
|
inventory.Shortcuts.Load([new ShortcutEntry(1, 2u, 3u)]);
|
||||||
inventory.ItemMana.OnQueryItemManaResponse(2u, 0.5f, true);
|
inventory.ItemMana.OnQueryItemManaResponse(2u, 0.5f, true);
|
||||||
|
|
@ -60,12 +61,19 @@ public sealed class RuntimeGameplayOwnershipTests
|
||||||
new HashSet<uint> { 4u })));
|
new HashSet<uint> { 4u })));
|
||||||
IDisposable subscription =
|
IDisposable subscription =
|
||||||
communication.Events.Subscribe(new NoopObserver());
|
communication.Events.Subscribe(new NoopObserver());
|
||||||
|
actions.Selection.Select(
|
||||||
|
0x50000002u,
|
||||||
|
AcDream.Core.Selection.SelectionChangeSource.World);
|
||||||
|
actions.Combat.SetCombatMode(AcDream.Core.Combat.CombatMode.Melee);
|
||||||
|
actions.Combat.OnUpdateHealth(0x50000002u, 0.75f);
|
||||||
|
actions.Interaction.EnterUse();
|
||||||
|
|
||||||
RuntimeGameplayOwnershipSnapshot populated =
|
RuntimeGameplayOwnershipSnapshot populated =
|
||||||
RuntimeGameplayOwnership.Capture(
|
RuntimeGameplayOwnership.Capture(
|
||||||
inventory,
|
inventory,
|
||||||
character,
|
character,
|
||||||
communication);
|
communication,
|
||||||
|
actions);
|
||||||
|
|
||||||
Assert.False(populated.IsConverged);
|
Assert.False(populated.IsConverged);
|
||||||
Assert.Equal(1, populated.Inventory.ShortcutCount);
|
Assert.Equal(1, populated.Inventory.ShortcutCount);
|
||||||
|
|
@ -73,7 +81,13 @@ public sealed class RuntimeGameplayOwnershipTests
|
||||||
Assert.Equal(1, populated.Communication.StreamSubscriberCount);
|
Assert.Equal(1, populated.Communication.StreamSubscriberCount);
|
||||||
Assert.True(populated.Communication.HasReplyTarget);
|
Assert.True(populated.Communication.HasReplyTarget);
|
||||||
Assert.True(populated.Communication.HasRetellTarget);
|
Assert.True(populated.Communication.HasRetellTarget);
|
||||||
|
Assert.Equal(0x50000002u, populated.Actions.SelectedObjectId);
|
||||||
|
Assert.Equal(
|
||||||
|
AcDream.Core.Combat.CombatMode.Melee,
|
||||||
|
populated.Actions.CombatMode);
|
||||||
|
Assert.Equal(1, populated.Actions.TrackedTargetHealthCount);
|
||||||
|
|
||||||
|
actions.Dispose();
|
||||||
communication.Dispose();
|
communication.Dispose();
|
||||||
character.Dispose();
|
character.Dispose();
|
||||||
inventory.Dispose();
|
inventory.Dispose();
|
||||||
|
|
@ -83,7 +97,8 @@ public sealed class RuntimeGameplayOwnershipTests
|
||||||
RuntimeGameplayOwnership.Capture(
|
RuntimeGameplayOwnership.Capture(
|
||||||
inventory,
|
inventory,
|
||||||
character,
|
character,
|
||||||
communication);
|
communication,
|
||||||
|
actions);
|
||||||
|
|
||||||
Assert.True(retired.IsConverged);
|
Assert.True(retired.IsConverged);
|
||||||
Assert.Equal(0, retired.Inventory.ShortcutSubscriberCount);
|
Assert.Equal(0, retired.Inventory.ShortcutSubscriberCount);
|
||||||
|
|
@ -99,6 +114,7 @@ public sealed class RuntimeGameplayOwnershipTests
|
||||||
var inventory = new RuntimeInventoryState(entities);
|
var inventory = new RuntimeInventoryState(entities);
|
||||||
var character = new RuntimeCharacterState();
|
var character = new RuntimeCharacterState();
|
||||||
var communication = new RuntimeCommunicationState();
|
var communication = new RuntimeCommunicationState();
|
||||||
|
var actions = new RuntimeActionState();
|
||||||
|
|
||||||
inventory.ExternalContainers.RequestOpen(0x70000001u);
|
inventory.ExternalContainers.RequestOpen(0x70000001u);
|
||||||
inventory.ExternalContainers.ApplyViewContents(0x70000001u);
|
inventory.ExternalContainers.ApplyViewContents(0x70000001u);
|
||||||
|
|
@ -119,12 +135,14 @@ public sealed class RuntimeGameplayOwnershipTests
|
||||||
Assert.Throws<AggregateException>(inventory.Dispose);
|
Assert.Throws<AggregateException>(inventory.Dispose);
|
||||||
Assert.Throws<AggregateException>(character.Dispose);
|
Assert.Throws<AggregateException>(character.Dispose);
|
||||||
communication.Dispose();
|
communication.Dispose();
|
||||||
|
actions.Dispose();
|
||||||
|
|
||||||
RuntimeGameplayOwnershipSnapshot retired =
|
RuntimeGameplayOwnershipSnapshot retired =
|
||||||
RuntimeGameplayOwnership.Capture(
|
RuntimeGameplayOwnership.Capture(
|
||||||
inventory,
|
inventory,
|
||||||
character,
|
character,
|
||||||
communication);
|
communication,
|
||||||
|
actions);
|
||||||
|
|
||||||
Assert.True(retired.IsConverged);
|
Assert.True(retired.IsConverged);
|
||||||
Assert.Equal(1, retired.Communication.DispatchFailureCount);
|
Assert.Equal(1, retired.Communication.DispatchFailureCount);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue