feat(app): FA2 -- fellowship/allegiance command routing for graphical + headless hosts
Both LiveSocialSessionBindings construction sites updated together (LiveSessionRuntimeFactory.cs, HeadlessSessionHost.cs) so the single GameEventWiring.WireAll registration site serves both hosts identically (the K-slice unification). CurrentGameRuntimeCommandAdapter implements IRuntimeFellowshipCommands/IRuntimeAllegianceCommands over the App command bus (LiveSessionCommandRouter gains 12 new *RuntimeCmd records + registrations + LiveSessionCommandBindings send delegates), mirroring DirectGameRuntimeCommandAdapter's direct-session shape including the identical Quit leader-hand-off rule read from RuntimeFellowshipState. CurrentGameRuntimeAdapter (the graphical IGameRuntimeView/ IGameRuntimeCommands composite) exposes the two new views/command groups. Headless's DirectGameRuntimeCommandAdapter needed no changes -- it already implements both new interfaces from the Runtime-layer commit. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
369729f06a
commit
cced83b483
8 changed files with 363 additions and 3 deletions
|
|
@ -26,7 +26,9 @@ internal sealed class CurrentGameRuntimeCommandAdapter
|
|||
IRuntimeInventoryStateCommands,
|
||||
IRuntimeSpellbookCommands,
|
||||
IRuntimeCharacterCommands,
|
||||
IRuntimeSocialCommands
|
||||
IRuntimeSocialCommands,
|
||||
IRuntimeFellowshipCommands,
|
||||
IRuntimeAllegianceCommands
|
||||
{
|
||||
private readonly LiveSessionController _session;
|
||||
private readonly LiveSessionHost _sessionHost;
|
||||
|
|
@ -36,6 +38,7 @@ internal sealed class CurrentGameRuntimeCommandAdapter
|
|||
private readonly RuntimeCharacterState _character;
|
||||
private readonly RuntimeActionState _actions;
|
||||
private readonly RuntimeLocalPlayerMovementState _movement;
|
||||
private readonly RuntimeFellowshipState _fellowship;
|
||||
private readonly SelectionInteractionController _selection;
|
||||
private readonly IGameRuntimeEventSink _events;
|
||||
|
||||
|
|
@ -48,6 +51,7 @@ internal sealed class CurrentGameRuntimeCommandAdapter
|
|||
RuntimeCharacterState character,
|
||||
RuntimeActionState actions,
|
||||
RuntimeLocalPlayerMovementState movement,
|
||||
RuntimeFellowshipState fellowship,
|
||||
SelectionInteractionController selection,
|
||||
IGameRuntimeEventSink events)
|
||||
{
|
||||
|
|
@ -61,6 +65,8 @@ internal sealed class CurrentGameRuntimeCommandAdapter
|
|||
?? throw new ArgumentNullException(nameof(actions));
|
||||
_movement = movement
|
||||
?? throw new ArgumentNullException(nameof(movement));
|
||||
_fellowship = fellowship
|
||||
?? throw new ArgumentNullException(nameof(fellowship));
|
||||
_selection = selection ?? throw new ArgumentNullException(nameof(selection));
|
||||
_events = events ?? throw new ArgumentNullException(nameof(events));
|
||||
}
|
||||
|
|
@ -793,6 +799,249 @@ internal sealed class CurrentGameRuntimeCommandAdapter
|
|||
command.Name);
|
||||
}
|
||||
|
||||
// ── Fellowship / Allegiance (Campaign FA slice FA2, 2026-08-12) ────────
|
||||
|
||||
public RuntimeCommandResult Create(
|
||||
RuntimeGenerationToken expectedGeneration,
|
||||
string fellowshipName,
|
||||
bool shareXp)
|
||||
{
|
||||
RuntimeCommandStatus gate = Validate(expectedGeneration, requireWorld: true);
|
||||
if (gate != RuntimeCommandStatus.Accepted)
|
||||
return Result(gate);
|
||||
if (string.IsNullOrWhiteSpace(fellowshipName))
|
||||
{
|
||||
return EmitResult(
|
||||
RuntimeCommandDomain.Fellowship,
|
||||
operation: 0,
|
||||
RuntimeCommandStatus.Rejected);
|
||||
}
|
||||
_commands.Publish(new FellowshipCreateRuntimeCmd(fellowshipName, shareXp));
|
||||
return EmitResult(
|
||||
RuntimeCommandDomain.Fellowship,
|
||||
operation: 0,
|
||||
RuntimeCommandStatus.Accepted);
|
||||
}
|
||||
|
||||
public RuntimeCommandResult Recruit(
|
||||
RuntimeGenerationToken expectedGeneration,
|
||||
uint targetGuid)
|
||||
{
|
||||
RuntimeCommandStatus gate = Validate(expectedGeneration, requireWorld: true);
|
||||
if (gate != RuntimeCommandStatus.Accepted)
|
||||
return Result(gate);
|
||||
if (targetGuid == 0u)
|
||||
{
|
||||
return EmitResult(
|
||||
RuntimeCommandDomain.Fellowship,
|
||||
operation: 1,
|
||||
RuntimeCommandStatus.Rejected,
|
||||
targetGuid);
|
||||
}
|
||||
_commands.Publish(new FellowshipRecruitRuntimeCmd(targetGuid));
|
||||
return EmitResult(
|
||||
RuntimeCommandDomain.Fellowship,
|
||||
operation: 1,
|
||||
RuntimeCommandStatus.Accepted,
|
||||
targetGuid);
|
||||
}
|
||||
|
||||
public RuntimeCommandResult Dismiss(
|
||||
RuntimeGenerationToken expectedGeneration,
|
||||
uint targetGuid)
|
||||
{
|
||||
RuntimeCommandStatus gate = Validate(expectedGeneration, requireWorld: true);
|
||||
if (gate != RuntimeCommandStatus.Accepted)
|
||||
return Result(gate);
|
||||
if (targetGuid == 0u)
|
||||
{
|
||||
return EmitResult(
|
||||
RuntimeCommandDomain.Fellowship,
|
||||
operation: 2,
|
||||
RuntimeCommandStatus.Rejected,
|
||||
targetGuid);
|
||||
}
|
||||
_commands.Publish(new FellowshipDismissRuntimeCmd(targetGuid));
|
||||
return EmitResult(
|
||||
RuntimeCommandDomain.Fellowship,
|
||||
operation: 2,
|
||||
RuntimeCommandStatus.Accepted,
|
||||
targetGuid);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retail's Quit-button leader hand-off (lane B §2.5/§3.6) — see
|
||||
/// <see cref="DirectGameRuntimeCommandAdapter.Quit"/>'s identical rule.
|
||||
/// </summary>
|
||||
public RuntimeCommandResult Quit(
|
||||
RuntimeGenerationToken expectedGeneration,
|
||||
bool disband)
|
||||
{
|
||||
RuntimeCommandStatus gate = Validate(expectedGeneration, requireWorld: true);
|
||||
if (gate != RuntimeCommandStatus.Accepted)
|
||||
return Result(gate);
|
||||
if (_fellowship.RequiresLeaderHandoffBeforeQuit(
|
||||
_view.Lifecycle.PlayerGuid,
|
||||
disband,
|
||||
out uint newLeaderGuid))
|
||||
{
|
||||
_commands.Publish(new FellowshipAssignNewLeaderRuntimeCmd(newLeaderGuid));
|
||||
}
|
||||
_commands.Publish(new FellowshipQuitRuntimeCmd(disband));
|
||||
return EmitResult(
|
||||
RuntimeCommandDomain.Fellowship,
|
||||
operation: 3,
|
||||
RuntimeCommandStatus.Accepted);
|
||||
}
|
||||
|
||||
public RuntimeCommandResult AssignLeader(
|
||||
RuntimeGenerationToken expectedGeneration,
|
||||
uint newLeaderGuid)
|
||||
{
|
||||
RuntimeCommandStatus gate = Validate(expectedGeneration, requireWorld: true);
|
||||
if (gate != RuntimeCommandStatus.Accepted)
|
||||
return Result(gate);
|
||||
if (newLeaderGuid == 0u)
|
||||
{
|
||||
return EmitResult(
|
||||
RuntimeCommandDomain.Fellowship,
|
||||
operation: 4,
|
||||
RuntimeCommandStatus.Rejected,
|
||||
newLeaderGuid);
|
||||
}
|
||||
_commands.Publish(new FellowshipAssignNewLeaderRuntimeCmd(newLeaderGuid));
|
||||
return EmitResult(
|
||||
RuntimeCommandDomain.Fellowship,
|
||||
operation: 4,
|
||||
RuntimeCommandStatus.Accepted,
|
||||
newLeaderGuid);
|
||||
}
|
||||
|
||||
public RuntimeCommandResult SetOpen(
|
||||
RuntimeGenerationToken expectedGeneration,
|
||||
bool isOpen)
|
||||
{
|
||||
RuntimeCommandStatus gate = Validate(expectedGeneration, requireWorld: true);
|
||||
if (gate != RuntimeCommandStatus.Accepted)
|
||||
return Result(gate);
|
||||
_commands.Publish(new FellowshipChangeOpennessRuntimeCmd(isOpen));
|
||||
return EmitResult(
|
||||
RuntimeCommandDomain.Fellowship,
|
||||
operation: 5,
|
||||
RuntimeCommandStatus.Accepted);
|
||||
}
|
||||
|
||||
public RuntimeCommandResult SetPanelOpen(
|
||||
RuntimeGenerationToken expectedGeneration,
|
||||
bool panelOpen)
|
||||
{
|
||||
RuntimeCommandStatus gate = Validate(expectedGeneration, requireWorld: true);
|
||||
if (gate != RuntimeCommandStatus.Accepted)
|
||||
return Result(gate);
|
||||
_commands.Publish(new FellowshipUpdateRequestRuntimeCmd(panelOpen));
|
||||
return EmitResult(
|
||||
RuntimeCommandDomain.Fellowship,
|
||||
operation: 6,
|
||||
RuntimeCommandStatus.Accepted);
|
||||
}
|
||||
|
||||
public RuntimeCommandResult Swear(
|
||||
RuntimeGenerationToken expectedGeneration,
|
||||
uint patronGuid)
|
||||
{
|
||||
RuntimeCommandStatus gate = Validate(expectedGeneration, requireWorld: true);
|
||||
if (gate != RuntimeCommandStatus.Accepted)
|
||||
return Result(gate);
|
||||
if (patronGuid == 0u)
|
||||
{
|
||||
return EmitResult(
|
||||
RuntimeCommandDomain.Allegiance,
|
||||
operation: 0,
|
||||
RuntimeCommandStatus.Rejected,
|
||||
patronGuid);
|
||||
}
|
||||
_commands.Publish(new AllegianceSwearRuntimeCmd(patronGuid));
|
||||
return EmitResult(
|
||||
RuntimeCommandDomain.Allegiance,
|
||||
operation: 0,
|
||||
RuntimeCommandStatus.Accepted,
|
||||
patronGuid);
|
||||
}
|
||||
|
||||
public RuntimeCommandResult Break(
|
||||
RuntimeGenerationToken expectedGeneration,
|
||||
uint targetGuid)
|
||||
{
|
||||
RuntimeCommandStatus gate = Validate(expectedGeneration, requireWorld: true);
|
||||
if (gate != RuntimeCommandStatus.Accepted)
|
||||
return Result(gate);
|
||||
if (targetGuid == 0u)
|
||||
{
|
||||
return EmitResult(
|
||||
RuntimeCommandDomain.Allegiance,
|
||||
operation: 1,
|
||||
RuntimeCommandStatus.Rejected,
|
||||
targetGuid);
|
||||
}
|
||||
_commands.Publish(new AllegianceBreakRuntimeCmd(targetGuid));
|
||||
return EmitResult(
|
||||
RuntimeCommandDomain.Allegiance,
|
||||
operation: 1,
|
||||
RuntimeCommandStatus.Accepted,
|
||||
targetGuid);
|
||||
}
|
||||
|
||||
public RuntimeCommandResult Kick(
|
||||
RuntimeGenerationToken expectedGeneration,
|
||||
uint vassalGuid)
|
||||
{
|
||||
RuntimeCommandStatus gate = Validate(expectedGeneration, requireWorld: true);
|
||||
if (gate != RuntimeCommandStatus.Accepted)
|
||||
return Result(gate);
|
||||
if (vassalGuid == 0u)
|
||||
{
|
||||
return EmitResult(
|
||||
RuntimeCommandDomain.Allegiance,
|
||||
operation: 2,
|
||||
RuntimeCommandStatus.Rejected,
|
||||
vassalGuid);
|
||||
}
|
||||
_commands.Publish(new AllegianceKickRuntimeCmd(vassalGuid));
|
||||
return EmitResult(
|
||||
RuntimeCommandDomain.Allegiance,
|
||||
operation: 2,
|
||||
RuntimeCommandStatus.Accepted,
|
||||
vassalGuid);
|
||||
}
|
||||
|
||||
public RuntimeCommandResult RequestInfo(
|
||||
RuntimeGenerationToken expectedGeneration,
|
||||
string playerName)
|
||||
{
|
||||
RuntimeCommandStatus gate = Validate(expectedGeneration, requireWorld: true);
|
||||
if (gate != RuntimeCommandStatus.Accepted)
|
||||
return Result(gate);
|
||||
_commands.Publish(new AllegianceInfoRequestRuntimeCmd(playerName ?? string.Empty));
|
||||
return EmitResult(
|
||||
RuntimeCommandDomain.Allegiance,
|
||||
operation: 3,
|
||||
RuntimeCommandStatus.Accepted);
|
||||
}
|
||||
|
||||
public RuntimeCommandResult SetUpdateSubscription(
|
||||
RuntimeGenerationToken expectedGeneration,
|
||||
bool on)
|
||||
{
|
||||
RuntimeCommandStatus gate = Validate(expectedGeneration, requireWorld: true);
|
||||
if (gate != RuntimeCommandStatus.Accepted)
|
||||
return Result(gate);
|
||||
_commands.Publish(new AllegianceUpdateRequestRuntimeCmd(on));
|
||||
return EmitResult(
|
||||
RuntimeCommandDomain.Allegiance,
|
||||
operation: 4,
|
||||
RuntimeCommandStatus.Accepted);
|
||||
}
|
||||
|
||||
private RuntimeCommandStatus Validate(
|
||||
RuntimeGenerationToken expectedGeneration,
|
||||
bool requireWorld)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue